WIN32汇编语言教程:第16章 TCP/IP和网络通信 · 16.3 TCP协议编程(7)
在这里读者一定要分清监听套接字和新套接字之间的区别。假如用于监听的套接字是#1,那么前面的bind,listen和accept等函数都是对#1操作的,当accept函数返回套接字#2后,#2才是和客户端相连的,所以为了和客户端进行通信而使用的send和recv等函数都是针对#2的(如图16.11中的A,B和C)。如果连接被客户端断开或者服务器主动断开连接,那么需要对#2调用closesocket(如图16.11中的⑥)。只有服务器端程序想不再继续监听的时候,才需要对#1调用closesocket函数(如图16.11中的⑤)。
那么什么时候去调用accept函数呢,其实当连接进入的时候,系统会发送FD_ACCEPT通知码,只要在收到FD_ACCEPT通知码后再去调用就是了。
使用accept函数返回的新套接字通信时,发送和接收数据的方法与在客户端中使用的方法是一样的,为了使用这些套接字,在使用它们之前同样需要使用WSAAsyncSelect函数来设置通知消息,由于这些套接字已经处于连接状态,并且仅用于收发数据,所以设置的时候仅包含FD_READ,FD_WRITE和FD_CLOSE就可以,FD_CONNECT等通知码就没有必要设置了。
3. 维护连接列表
服务器端程序和客户端程序的最大不同是它必须维护一个连接列表。由于监听套接字每次接受一个连接后都会产生一个新的套接字,这些套接字在连接关闭之前必须被保存在一个列表中,而且列表中的数据必须动态维护,因为随时可能有新连接进入,也随时可能有连接被关闭,所以每当连接关闭时,程序必须在列表中找出并清除对应的项目,而新连接进入时,程序需要在列表中找出一个空位置并填写新的项目。
在例子程序中,服务器程序预定义了一个stTable变量,其中包括100个双字,这样就可以存放100个套接字句柄(这也决定了聊天室的容量是100个客户端)。在每次调用accept函数以后,程序马上使用_AddClient子程序将新的套接字句柄添加到列表中,在这个子程序中,程序从头开始扫描列表,如果找出一个位置的数据为0的话,则将句柄存放在这个地方,然后程序将在线客户端计数值dwCount加1并显示在窗口中。如果全部列表都不为0的话将直接关闭连接(客满了),这样从客户端看来就是连接被马上断开了。
当某个客户端断开的时候,程序收到FD_CLOSE通知,在这里除了关闭断开的套接字句柄以外,还需要从列表中将它找出并清除,这样列表中的无效项目才会空出来供新的连接使用,这个功能在_RemoveClient子程序中实现。
当某个客户端发送聊天语句的时候,服务器端程序收到FD_READ通知,在这里调用_RecvData子程序,子程序中首先使用recv函数读取发过来的聊天语句,然后将它显示在窗口中。接下来程序在句柄列表中找出当前在线的每个套接字句柄并使用send函数将聊天语句转发给它们,这样每个在线客户端的窗口中就会显示出这句聊天语句。
虽然这个TCP聊天室还有很多可以改进的地方(如加上登录验证密码,有连接进入时显示欢迎语句并向其他在线客户端发送“XXX进入了聊天室”,加上仅在某两个客户端之间收发数据的“私人谈话”功能等),但是它已经清楚地表现出了基于TCP协议的客户机/服务器模型的结构和实现方法,读者可以在这基础上很方便地实现其他功能。
上页:第16章 TCP/IP和网络通信 · 16.3 TCP协议编程(6) 下页:第16章 TCP/IP和网络通信 · 16.4 UDP协议编程(1)