Python提供两种级别的网络服务访问。:底层网络服务支持基本套接字,可以访问底层操作系统套接字接口的方法。高级网络服务模块socketserver可以简化网络服务器的开发。Socket查看socket类的帮助如下importsocket#importsocketmodule>>>help(socket.socket)重点看初始化函数:__init__(self,family=,type=,proto=0,fileno=None)family:网络协议簇,默认值为AF_INETtype:socket类型,根据是面向连接还是非连接分为SOCK_STREAM或SOCK_DGRAMproto:socketprotocol,一般默认为0,表示fileno:socket的int类型文件描述符,下面实现一个TCP聊天室和一个UDP聊天室TCP聊天室概述设计获取多连接处理开启accept线程并执行accept操作开始阻塞,当有client连接时,开启一个线程recv处理数据接收。然后accept线程继续阻塞,等待后续的客户端连接。阻塞处理当服务器处理客户端的连接时,有两种阻塞,即:当获得一个连接时,socket.accept()会阻塞每一个成功的连接。获取数据时,socket.recv(1024)两处都需要开线程单独处理,否则会阻塞主线程。客户端主动断开的处理当客户端主动断开时,如果没有通知服务器,服务器上保存的客户端连接不会被清理,这是不合理的。因此,当客户端主动断开连接时,我们在应用层约定客户端在启动前需要向服务端发送/quit命令,然后服务端关闭socket。TCPchatroom-server聊天室服务器端主要是一个监听端口,处理来自客户端的连接,分发数据给所有客户端代码importsocketimportthreadingclassTcpChatServer:def__init__(self,ip='192.168.110.13',port=9001):self.ip=ipself.port=portself.clients={}self.sock=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)self.event=threading.Event()defrecv(self,so,ip,port):whilenotself.event.is_set():data=so.recv(1024).decode()#将接收到的byte数据字节转为utf-8格式的字符串ifdata.strip()=='/quit':#客户端主动断开时的处理so.close()self.clients.pop((ip,port))returnforsinself.clients.values():#广播发送s.send('{}:{}\n{}'.format(ip,port,data).encode())defaccept(self):whilenotself.event.is_set():so,(ip,port)=self.sock.accept()self.clients[(ip,port)]=so#因为so.recv会阻塞,所以单独开一个线程process数据的接收部分这样accept可以继续接受其他客户端的连接threading.Thread(target=self.recv,args=(so,ip,port),name='client-{}:{}'.format(ip,port)).start()defstart(self):self.sock.bind((self.ip,self.port))self.sock.listen()t=threading.Thread(target=self.accept,daemon=True)#为了不阻塞主线程,开一个单独的线程来处理accept(accept会阻塞线程)stop(self):forsinself.clients.values():s.close()self.sock.close()self.event.set()#停止所有循环if__name__=='__main__':tcp_chat_server=TcpChatServer()tcp_chat_server。start()TCPchatroom-client聊天室的client端主要是发起连接,连接到server端,接受server端广播分发的消息。代码importsocketimportthreadingclassTcpChatClient:def__init__(self):self.sock=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)self.event=threading.Event()defrecv(self):#client它需要一直接收服务器广播分发的消息而不是self.event.is_set():data=self.sock.recv(1024).decode()data=data.strip()print(data)defsend(self):#Inputmessageissentwhilenotself.event.is_set():data=input()self.sock.send(data.encode())ifdata.strip()=='/quit':#send/quitWhenself.stop()defstart(self,ip,port):self.sock.connect((ip,port))s=threading.Thread(target=self.send,daemon=False)r=穿线。线程(target=self.recv,daemon=False)s.start()r.start()defstop(self):self.sock.close()self.event.set()if__name__=='__main__':tcp_chat_client=TcpChatClient()tcp_chat_cclient.start('192.168.110.13',9001)UDP聊天室概要设计阻塞处理当UDP服务器收到来自客户端的消息时,使用socket.recvfrom(1024)方法保存客户端的地址信息。该方法会阻塞当前线程,所以需要开启线程单独处理客户端主动断开。UDP客户端主动关闭后,服务端无法检测到客户端已经关闭。我们可以使用下面两种方法:如果类似于TCP采用约定的退出命令的方法,那么客户端发送退出命令后会调用close方法,然后服务器从客户端字典中删除对应的客户端根据得到的指令。也可以通过客户端定时向服务端发送心跳,服务端通过心跳判断客户端进程是否存活。UDP聊天室-服务器UDP服务端程序开一个线程等待接收客户端的数据,然后广播给其他客户端,检查所有连接的心跳是否超时。代码importsocketimportdatetimeimportthreadingclassUdpChatServer:def__init__(self,ip='192.168.110.13',port=9001):self.addr=(ip,port)self.sock=socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM)self.clients={}self.event=threading.Event()defrecv(self):而不是self.event.is_set():data,addr=self.sock.recvfrom(1024)data=data.decode().strip()now=datetime.datetime.now()ifdata=='#ping#':#判断是否收到心跳self.clients[addr]=now#保存客户端地址iftheheartbeat收到,并更新时间戳continuedisconnected=set()#判断所有无效链接,一次没有收到数据。foraddr,timestampinself.clients.items():if(now-timestamp).total_seconds()>10:#无效条件:如果两次(即10s)没有收到心跳,则判断客户端断开.add(addr)else:self.sock.sendto('{}:{}\n{}'.format(addr[0],addr[1],data).encode(),addr)foraddrindisconnected:self.clients.pop(addr)defstart(self):self.sock.bind(self.addr)#绑定端口后打开线程一直在从客户端接收数据t=threading.Thread(target=self.recv(),daemon=True)try:t.start()t.join()exceptKeyboardInterrupt:self.stop()defstop(self):self.event.set()self.sock.close()if__name__=='__main__':udp_chat_server=UdpChatServer()udp_chat_server.start()UDP聊天室-客户端UDP客户端的主线程一直在等待用户输入数据,然后将数据发送到服务器,同时启动一个心跳进程和一个接受服务器广播数据的线程代码importsocketimportthreadingimporttimeclassUdpChatClient:def__init__(self,ip,port):self.addr=(ip,port)self.sock=socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM)self.event=threading.Event()defheartbeat(self):#心跳线程函数:每5s发送一次心跳而不是self.event.wait(5):self.sock.sendto(b'#ping#',self.addr)defrecv(self):#等待接收udp服务器广播的数据,而不是self.event.is_set():data=self.sock.recv(1024)print(data.decode())defstart(self):穿线。Thread(target=self.heartbeat,name='heartbeat',daemon=True).start()threading.Thread(target=self.recv,name='recv',daemon=True).start()打印('请Speakafter5s')time.sleep(5)#因为server在保存secondaryclient之前必须收到心跳,所以需要等待5sprint('Pleasestartspeaking')whilenotself.event.is_set():data=input('')data=data.strip()如果数据=='/quit':self.event.set()self.sock.close()返回self.sock.sendto(data.encode(),self.addr)if__name__=='__main__':udp_chat_client=UdpChatClient('192.168.110.13',9001)udp_chat_client.start()SocketServerTODO(Flowsnow):重写聊天室程序的TcpChatServer和UdpChatServer附1:TCP和UDP的本质区别udp:客户端发送的所有数据报都在队列上累积,然后服务器一个通过一个tcp的处理:每个client和server都有一个连接通道,只处理对应client的数据流。对计算机各个方向的视频课程和电子书,从入门、进阶、实用进行了认真梳理,并按照目录进行合理分类。你总能找到你需要的学习资料。你在等什么?立即关注并下载!!!念念不忘,必有回响,朋友们,点个赞吧,非常感谢职场亮哥,YY资深软件工程师,四年工作经验,不服的斜杠程序员成为领导者。听我说,我进步很大。如果有幸帮到你,请给我一个【点赞】,给我一个关注,如能评论鼓励,将不胜感激。职场凉阁文章列表:更多文章我的所有文章和回答均与版权保护平台合作,版权归职场凉阁所有。未经授权转载必究!