🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## **一 最简单的C/S实现** 基于前面的打电话伪代码,实现服务器与客户端的单次通信 **server端代码** ``` import socket #1、买手机 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #2、绑定手机卡 phone.bind(('127.0.0.1',8081)) #元组形式的主机和端口 #3、开机 phone.listen(5) #允许5个连接排队 #4、等电话链接 print('starting...') conn,client_addr=phone.accept() #接受的数据是一个元组 #5、收,发消息 data=conn.recv(1024) #1024代表最大接收1024个bytes print('客户端的数据',data.decode('utf-8')) #接受的是bytes,需要转码 conn.send(data.upper()) #6、挂电话 conn.close() #7、关机 phone.close() ``` **client端代码** ``` import socket #1、买手机 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #2、拨号 phone.connect(('127.0.0.1',8081)) #3、发,收消息 phone.send('hello'.encode('utf-8')) #只能发送bytes类型,所以要转码 data=phone.recv(1024) print(data.decode('utf-8')) #4、关闭 phone.close() ``` ## **二 循环发送消息的实现** 上面的代码客户端和服务器断只能通信一次,然后就会断开连接,如果想多次发送数据,就需要加入循环语句 **server端代码** ``` import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.bind(('127.0.0.1',8083)) phone.listen(5) print('starting...') conn,client_addr=phone.accept() while True: #通信循环 data=conn.recv(1024) print('客户端的数据',data) conn.send(data.upper()) conn.close() phone.close() ``` **client端代码** ``` import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect(('127.0.0.1',8083)) while True: #通信循环 msg=input('>>: ').strip() if not data:continue #避免发送空数据 phone.send(msg.encode('utf-8')) data=phone.recv(1024) print(data) phone.close() ``` ### **bug解释和修复** 上面的代码有2个bug,关于bug的说明和修改方式如下 1. client直接断开服务的话,server端会报错 修改server端的通信循环为如下样式,分别针对linux和windows服务器做不同的处理 ``` while True: #通信循环 try: data=conn.recv(1024) if not data:break #适用于linux操作系统 print('客户端的数据',data) conn.send(data.upper()) except ConnectionResetError: #适用于windows操作系统 break ``` 2. 重启server端时可能提示端口占用 问题是由于服务端仍然存在四次挥手的time_wait状态在占用地址 解决方法1:socket方法 ~~~ sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #写在bind前 sock_server.bind((HOST, PORT)) ~~~ 解决方法2:linux内核办法 `vi /etc/sysctl.conf`,在打开文件中加入以下内容: net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 30 然后执行 `/sbin/sysctl -p `让参数生效。 >net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭; net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭; net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。 net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间 ## **循环建立连接的实现** 上面的C/S结构代码,仅能支持client与server一对一的通信,如果client断开连接也不会有新的客户端被接入,因此需要server段再建立连接处再加一层循环实现循环建立连接 **server端代码** ``` import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8083)) #0-65535:0-1024给操作系统使用 phone.listen(5) print('starting...') while True: # 链接循环 conn,client_addr=phone.accept() print(client_addr) while True: #通信循环 try: data=conn.recv(1024) if not data:break #适用于linux操作系统 print('客户端的数据',data) conn.send(data.upper()) except ConnectionResetError: #适用于windows操作系统 break conn.close() phone.close() ``` client端代码不需要任何改变 ### **特别说明** 如上修改后,能实现多个客户端排队连接客户端进行通话的情况,但是实现不了多个客户端同时连接服务端通话的情况,需要用到后面的**并发编程**知识