WIN32汇编语言教程:第16章 TCP/IP和网络通信 · 16.3 TCP协议编程(6)

                 mov    @stSin.sin_addr,INADDR_ANY

                invoke bind,hSocket,addr @stSin,sizeof @stSin

                  .if    eax == SOCKET_ERROR

                        invoke MessageBox,hWinMain,addr szErrBind,NULL,\

                                  MB_OK or MB_ICONWARNING

                       invoke SendMessage,hWinMain,WM_CLOSE,0,0

               .else

                          invoke listen,hSocket,5

               .endif

                  ret

 

_Init         endp

;####################################################################

; 主窗口程序

;####################################################################

_ProcDlgMain      proc      uses ebx edi esi hWnd,wMsg,wParam,lParam

 

             mov    eax,wMsg

              .if    eax == WM_SOCKET

                     mov    eax,lParam

                      .if    ax ==  FD_ACCEPT

                              invoke accept,wParam,0,0

                            invoke _AddClient,eax

                     .elseif ax ==  FD_READ

                              invoke _RecvData,wParam

                      .elseif ax ==  FD_CLOSE

                             invoke _RemoveClient,wParam

                      .endif

;********************************************************************

; 退出时关闭全部连接

;********************************************************************

              .elseif eax == WM_CLOSE

                      invoke closesocket,hSocket

                     xor    ebx,ebx

                     mov    esi,offset stTable

                     cld

                     .while ebx <  MAX_SOCKET

                              lodsd

                              .if    eax

                                     invoke closesocket,eax

                             .endif

                             inc    ebx

                      .endw

                     invoke WSACleanup

                        invoke EndDialog,hWinMain,NULL

;********************************************************************

                  .elseif eax == WM_INITDIALOG

                         push      hWnd

                          pop    hWinMain

                          call      _Init

;********************************************************************

                  .else

                         mov    eax,FALSE

                          ret

                  .endif

                  mov    eax,TRUE

                  ret

 

_ProcDlgMain      endp

;####################################################################

; 程序开始

;####################################################################

start:

                 invoke GetModuleHandle,NULL

                 invoke  DialogBoxParam,eax,DLG_MAIN,NULL,offset _ProcDlgMain,0

                 invoke ExitProcess,NULL

;####################################################################

                  end    start

服务器端程序的结构如图16.11所示,对于WinSock库的装入和清除,何时去读取套接字,以及发送数据时的流量控制等方面,服务器端程序和工作站端程序的用法是相同的,所以WSAStartup,WSACleanup,recv和send等函数的使用方法和客户端程序大同小异。服务器端程序的特殊之处就在于如何监听和接受连接。


图16.11 TCP服务器端程序的常见结构

当服务器端程序准备在某个端口提供服务时(如图16.11中的②),程序首先使用socket函数创建一个用于监听的流套接字并将它设置为非阻塞模式,例子中对应的代码是这样的:

   invoke socket,AF_INET,SOCK_STREAM,0

   mov    hSocket,eax

   invoke WSAAsyncSelect,hSocket,hWinMain,WM_SOCKET,FD_ACCEPT

   ... ...

   invoke bind,hSocket,addr @stSin,sizeof @stSin

由于用来监听的套接字以后并不会用来收发数据,也不会用它去主动连接到其他地方,所以不需要为它设置FD_CONNECT,FD_READ,FD_WRITE和FD_CLOSE等通知码,惟一需要设置的就是FD_ACCEPT通知码。然后,必须使用bind函数将套接字绑定到一个固定的端口上,例子程序中使用9 999号端口来当做服务端口。接下来,需要让套接字进入监听方式。

1. 监听进入的连接

使用listen可以让一个流套接字进入监听状态:

   invoke listen,s,backlog

参数s指定套接字句柄,backlog参数是监听队列中允许保持的尚未处理的最大连接数量。

注意不要将backlog参数理解为最多能和多少个客户端相连接,它的真正含义是:当套接字监听到有客户端连接进来后,还需要调用accept函数来接受这个连接后,连接才真正被建立。在调用accept函数之前,连接请求将暂时被保留在队列中,如果这时有另一个客户端也发起连接的话,这个连接也将被保留在队列中,backlog参数指的就是这个队列的长度。实际上,如果程序能够在足够短的时间内响应进入的连接,那么即使将backlog参数指定为1,仍然不会丢失任何连接请求。

如果listen函数执行失败将返回SOCKET_ERROR。执行成功函数返回0,这时套接字就处于等待连接进入的状态了(如图16.11中的③)。

2. 接受客户端的连接请求

当有客户端向监听的套接字发起连接后,必须对监听的套接字调用accept函数以后,连接才最后被确定:

   invoke accept,s,lpsockaddr,lpaddrlen

   .if    eax != INVALID_SOCKET

           mov    hNewSocket,eax

   .endif

参数s指定监听中的套接字句柄,lpsockaddr指向一个缓冲区,函数会在这里返回一个sockaddr_in结构,结构中存放有连接请求方的IP地址和端口,lpaddrlen则指向一个双字变量,函数在这里放入返回到缓冲区中的结构长度。如果不需要得到对方的地址信息,可以将lpsockaddr和lpaddrlen参数都设置为NULL。

如果执行成功,函数将新建一个套接字,这个套接字和客户端相连接,程序可以使用它来和客户端之间收发数据,而原来在监听状态的套接字仍然保持监听状态,以便接受下一个连接的进入。

上页:第16章 TCP/IP和网络通信 · 16.3 TCP协议编程(5) 下页:第16章 TCP/IP和网络通信 · 16.3 TCP协议编程(7)

第16章 TCP/IP和网络通信

版权所有 © 中山市飞娥软件工作室 证书:粤ICP备09170368号