Python 提供了两个级别访问的网络服务。:
- 低级别的网络服务支持基本的 socket,,可以访问底层操作系统Socket接口的方法。
- 高级别的网络服务模块 socketserver, 可以简化网络服务器的开发。
socket
查看socket类的帮助如下
重点关注初始化函数:
- family:网络协议簇,默认值为AF_INET
- type:套接字的类型,根据是面向连接的还是非连接分为
SOCK_STREAM
或SOCK_DGRAM
- proto:套接字协议,一般默认为0,表示
- fileno:套接字的int型的文件描述符
下面实现一个TCP聊天室和一个UDP聊天室
TCP聊天室
概要设计
获取多个连接的处理
开启accept线程,执行accept操作开始阻塞,有客户端连接时,再开启一个线程recv进行数据接收的处理。然后accept线程继续阻塞,等待后续客户端的连接。
阻塞的处理
服务端处理客户端的连接时,有两处存在阻塞,分别是:
- 获取连接时,socket.accept()会阻塞
- 每一个建立成功的连接在获取数据时,socket.recv(1024)
因此这两处都需要开启线程单独处理,否则会阻塞主线程。
客户端主动断开的处理
客户端主动断开时,如果不通知服务端,那么服务端上保存的客户端连接不会被清理,这是不合理的。因此客户端主动断开时,我们在应用层约定,客户端推出前需要发送/quit
指令到服务端上,然后有服务端关闭socket。
TCP聊天室-server
聊天室的server端主要是监听端口,处理来自client端的连接,并且分发数据到所有的client端
代码
TCP聊天室-client
聊天室的client端主要是发起连接,连接到server端,并且要接受来自服务端广播分发的消息。
代码
UDP聊天室
概要设计
阻塞的处理
在UDP服务端接收客户端的消息时,采用socket.recvfrom(1024)
这个方法以便保存客户端的地址信息,这个方法会阻塞当前线程,因此需要开启线程单独处理。
客户端主动断开的处理
UDP客户端主动关闭之后,服务端是无法检测到客户端已经关闭的。我们可以采用以下两种方法:
- 如果类似于TCP采用约定退出指令的方法,那么客户端发送退出指令后就调用close方法,然后服务端根据得到的指令剔除客户端字典中对应的客户端。
- 还可以通过客户端定时发送心跳给服务端,服务端通过心跳来判断客户端进程是否存活。
UDP聊天室-server
UDP服务端程序开启线程等待接收客户端的数据,然后广播给其他的客户端,并且检查所有连接的心跳是否超时。
代码
UDP聊天室-client
UDP的客户端的主线程一直在等待用户输入数据然后将数据发送到服务端,同时开启了一个心跳进程和一个接受服务端广播数据的线程。
代码
SocketServer
TODO(Flowsnow):改写聊天室程序的TcpChatServer和UdpChatServer
附一:TCP和UDP的本质区别
附二:参考资料
- socketserver — A framework for network servers