day7-通过socket实现处理多个连接
简述
现在我们是发送和接收一次,如果想要重复发送和接收多次,该如何做呢?
发送和接收中文
客户端代码
import socket #导入socket #客户端 #声明socket类型,同时生成socket连接对象(实例) client = socket.socket() #连接远程机器 client.connect(("localhost",6969)) #发送数据 client.send("我要下载a电影".encode("utf-8")) #接收服务器端数据 data = client.recv(1024) print("recv:",data.decode()) #关闭连接 client.close() #运行输出 recv: 我要下载A电影
服务端代码
import socket #服务器端 server = socket.socket() #绑定要监听的IP地址和端口 server.bind(("localhost",6969)) #监听 server.listen() print("我要接电话了") #conn就是客户端连过来而在服务器端为其生成的一个连接实例 conn,addr = server.accept() print(conn,addr) print("电话来了") #接收客户端发来的数据 data = conn.recv(1024) print("recv:",data) #发送数据到客户端 conn.send(data.upper()) #关闭服务器端 server.close() #运行输出 我要接电话了 <socket.socket fd=272, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 50662)> ('127.0.0.1', 50662) 电话来了 recv: b'\xe6\x88\x91\xe8\xa6\x81\xe4\xb8\x8b\xe8\xbd\xbda\xe7\x94\xb5\xe5\xbd\xb1'
解析:客户端发送中文前需要将字符串encode为字节bytes,否则将会报错:bytes can only contain ASCII literal characters,因为b"test string"可以直接产生bytes类型,但需要注意的是,这个语法只适用于英文字符,如果想把它应用到中文字符上,是会产生异常的。
以上是我们只发送和接收一次,如何重复发送和接收多次?
重复发送多次和接收
服务器端代码
import socket #服务器端 server = socket.socket() #绑定要监听的IP地址和端口 server.bind(("localhost",6969)) #监听 server.listen() print("我要接电话了") while True: #conn就是客户端连过来而在服务器端为其生成的一个连接实例 conn,addr = server.accept() print(conn,addr) print("电话来了") #接收客户端发来的数据 data = conn.recv(1024) print("recv:",data) #发送数据到客户端 conn.send(data.upper()) #关闭服务器端 server.close() #运行输出 我要接电话了 <socket.socket fd=272, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 51192)> ('127.0.0.1', 51192) 电话来了 recv: b'\xe4\xbd\xa0\xe5\xa5\xbd' <socket.socket fd=244, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 51195)> ('127.0.0.1', 51195) 电话来了 recv: b'abc'
客户端代码
import socket #导入socket #客户端 #声明socket类型,同时生成socket连接对象(实例) client = socket.socket() #连接远程机器 client.connect(("localhost",6969)) while True: msg = input(">>:") #发送数据 client.send(msg.encode("utf-8")) #发送中文需要encode为bytes #接收服务器端数据 data = client.recv(1024) print("recv:",data.decode()) #关闭连接 client.close() #运行输出(需要输入) session 1: >>:你好 recv: 你好 >>:你好a Traceback (most recent call last): File "C:/Users/huwei/PycharmProjects/python/module_3/socket_client.py", line 14, in <module> data = client.recv(1024) ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。 session 2: >>:abc recv: ABC >>:a
解析:在服务器端和客户端都加了while True循环以后,同样只能发送和接收一次,这是为什么?
解决:修改服务端代码,只将发送和接收放入while循环
import socket server = socket.socket() server.bind(("localhost",6969)) server.listen() print("我要接电话了") conn, addr = server.accept() print(conn, addr) print("电话来了") while True: data = conn.recv(1024) print("recv:",data) conn.send(data.upper()) server.close() #服务器端运行输出 我要接电话了 <socket.socket fd=272, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 51240)> ('127.0.0.1', 51240) 电话来了 recv: b'abc' recv: b'\xe4\xbd\xa0\xe5\xa5\xbd' ---------------------------------------------------------------- #客户端运行输出 >>:abc recv: ABC >>:你好 recv: 你好 >>:hi recv: HI >>: #等待输入
解析:服务器端这里不能将while循环放在conn,addr = server.accept()前面,因为服务器端每接收和发送一次到客户端的数据,在当前会话还没有断开的情况下,又重新进行下一个实例的连接等待,下一个客户端实例连入server循环一次数据,服务器端又重新进行下一个实例的连接等待,周而复始,所以当前服务器的实例和当前客户端会卡住。
处理多个连接
上面我们实现了客户端和服务器端的多次发送和接收数据,那么我们如何在当前客户端断开以后,另一个客户端进行对服务器端的连接,按照思路,我们将客户端的连接断开,让第二个用户进行连接。
改变服务器端配置:添加了循环超过10次自动跳出程序
import socket server = socket.socket() server.bind(("localhost",6969)) #绑定要监听的端口 server.listen() #监听 print("我要开始等电话了") conn,addr = server.accept() #等电话打进来 #conn就是客户端连过来而在服务器端为其生成的一个连接实例 print(conn,addr) count = 0 while True: print("电话来了") data = conn.recv(1024) print("recv:",data.decode()) conn.send(data.upper()) count+=1 if count > 10: break server.close()
实验结果:
解析:当客户端被人工断开以后,服务器端进入死循环接收客户端发来的空数据,直到满足if条件句break中断为止。针对此种情况,我们可以在服务器端增加条件,当服务器端接收为空时,断开连接,代表客户端已经断开。
针对上面出现的问题,我们原本是想让客户端断开,而服务器端不进入死循环,等待下一个客户端的重新连接,如何实现客户端断开以后,服务器端等待下一次的连接呢?
因为服务器端在accept()处等待连接实例,所以我们在accept()前面再加一个while循环
import socket server = socket.socket() server.bind(("localhost",6969)) #绑定要监听的端口 server.listen() #监听 while True: print("我要开始等电话了") conn,addr = server.accept() #等电话打进来 #conn就是客户端连过来而在服务器端为其生成的一个连接实例 print(conn,addr) while True: print("电话来了") data = conn.recv(1024) print("recv:",data.decode()) conn.send(data.upper()) if not data: print("client has been lost....") break server.close()
实验结果:
解析:当服务器端运行后,客户端1运行后可以和其交互,当客户端2也运行后,服务端不能接收数据,客户端2也无法收到服务器端返回的数据,但是客户端1可以正常交互
尝试一:将客户端1断开
解析:此时,当客户端1断开后,服务器端接收到了客户端2发送的数据,而客户端2此时也接收到了之前发送的数据,继续输入数据也可以返回。说明此前客户端2发送的连接服务器将其置于挂起状态。当客户端1断开后,客户端2可以正常连接。
模拟SSH访问
客户端
import socket #客户端 client = socket.socket() #声明socket类型,同时生成socket连接对象 client.connect(("127.0.0.1",6969)) while True: msg = input(">>:") if len(msg) == 0: continue client.send(msg.encode("utf-8")) data = client.recv(1024) #收1024字节 print(data.decode()) client.close()
服务端
import os import socket server = socket.socket() server.bind(("127.0.0.1",6969)) #绑定要监听的端口 server.listen(5) #监听 print("我要开始等电话了") conn,addr = server.accept() #等电话打进来 #conn就是客户端连过来而在服务器端为其生成的一个连接实例 print(conn,addr) while True: print("电话来了") data = conn.recv(1024) print("recv:",data.decode()) if not data: print("Client has been lost.....") break res = os.popen(data.decode()).read() conn.send(res.encode("utf-8")) server.close()
实验结果:
注意:当客户端接收数据有限制时,如果服务端发送的数据超过了客户端接收数据的最大值,客户端只能接收设定值大小,其余将存放在缓冲区,下一次服务端再发送数据的时候,是将缓冲区里面的数据发送完以后,再发送新的返回数据。