WIN32汇编语言教程:第16章 TCP/IP和网络通信 · 16.3 TCP协议编程(3)
local @stWsa:WSADATA
mov eax,wMsg
.if eax == WM_SOCKET
;********************************************************************
; 处理 Socket 消息
;********************************************************************
mov eax,lParam
.if ax == FD_READ
invoke _RecvData,wParam
.elseif ax == FD_WRITE
invoke EnableWindow,hWinOK,TRUE
.elseif ax == FD_CONNECT
shr eax,16
.if ax == NULL
invoke SetWindowText,hWinConnect,\
addr szDisConnect
invoke EnableWindow,hWinConnect,TRUE
invoke EnableWindow,hWinServer,FALSE
invoke EnableWindow,hWinText,TRUE
invoke EnableWindow,hWinOK,TRUE
invoke SetFocus,hWinText
.else
invoke _DisConnect
invoke MessageBox,hWinMain,\
offset szErrConnect,NULL,\
MB_OK or MB_ICONWARNING
.endif
.elseif ax == FD_CLOSE
call _DisConnect
.endif
;********************************************************************
.elseif eax == WM_COMMAND
mov eax,wParam
.if ax == IDOK
invoke _SendData
.elseif ax == IDC_CONNECT
.if hSocket
invoke _DisConnect
.else
call _Connect
.endif
.endif
;********************************************************************
.elseif eax == WM_CLOSE
invoke _DisConnect
invoke WSACleanup
invoke EndDialog,hWinMain,NULL
;********************************************************************
.elseif eax == WM_INITDIALOG
push hWnd
pop hWinMain
invoke GetDlgItem,hWnd,IDC_SERVER
mov hWinServer,eax
invoke GetDlgItem,hWnd,IDOK
mov hWinOK,eax
invoke GetDlgItem,hWnd,IDC_TEXT
mov hWinText,eax
invoke GetDlgItem,hWnd,IDC_CONNECT
mov hWinConnect,eax
invoke GetDlgItem,hWnd,IDC_INFO
mov hWinInfo,eax
invoke SetWindowText,hWinServer,addr szIP
invoke WSAStartup,101h,addr @stWsa
;********************************************************************
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
_ProcDlgMain endp
;####################################################################
; 程序开始
;####################################################################
start:
invoke GetModuleHandle,NULL
invoke DialogBoxParam,eax,DLG_MAIN,NULL,offset_ProcDlg Main,0
invoke ExitProcess,NULL
;####################################################################
end start
在图16.8的模型中,已经了解了客户端程序大致的工作方式,但是到具体实现的时候,各种语句应该如何和Windows的消息驱动体系配合呢,上述源代码的结构看上去似乎比较松散,但是从图16.10中就可以看出它的工作流程。
图16.10 TCP客户端程序的常见结构
请读者结合图16.10中的数字序号来分析源代码,初始化WinSock库的WSAStartup函数被安排在窗口的初始化消息中(图中的①),当程序需要退出时,程序调用WSACleanup函数卸载WinSock库(图中的⑧)。
当用户要求连接时(比如例子中用户输入了IP地址并按下了“连接”按钮,见图16.10中的②),程序使用socket函数创建套接字,套接字的类型必须是流套接字,因为只有它是使用TCP协议的:
invoke socket,AF_INET,SOCK_STREAM,0
mov hSocket,eax
invoke WSAAsyncSelect,hSocket,hWinMain,\
WM_SOCKET,FD_CONNECT or FD_READ or FD_CLOSE or FD_WRITE
套接字创建以后,应该使用WSAAsyncSelect函数将通知消息以自定义的ID(例子中使用WM_SOCKET消息,它被预先定义为WM_USER+100)绑定到窗口过程中,这样就可以在适当的时候收到系统的通知消息,当收到消息的时候,lParam的低16位包括了通知码,高16位为出错信息,wParam参数指定了发生消息的套接字句柄。对于用于客户端的流套接字来说,在使用WSAAsyncSelect函数时必须包含FD_CONNECT,FD_READ,FD_WRITE和FD_CLOSE通知码。
完成这些事情后,接下来就可以使用connect函数去连接服务器了。
1. 连接到服务器
connect函数将一个流套接字连接到指定IP地址的指定端口上,它仅适用于流套接字,因为数据报套接字不是面向连接的。connect函数的用法是:
invoke connect,s,lpsockaddr,len
参数s指定用于连接的套接字句柄,lpsockaddr参数指向一个sockaddr_in结构,用来指定要连接到的服务器的IP地址和端口,这个结构在前面介绍的bind函数中已经使用过,len参数则指定sockaddr_in结构的长度。
当连接成功的时候,函数返回0,否则返回值是SOCKET_ERROR,但是当套接字工作在非阻塞模式时,函数返回的时候连接还没来得及完成,返回值肯定会是SOCKET_ERROR,但这并不意味着以后连接就不会成功,如果出错代码显示为WSAEWOULDBLOCK值,表示出错是因为操作尚未完成引起的,这时应该等待,否则才是真正的出错。
上页:第16章 TCP/IP和网络通信 · 16.3 TCP协议编程(2) 下页:第16章 TCP/IP和网络通信 · 16.3 TCP协议编程(4)