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()
实验结果:


注意:当客户端接收数据有限制时,如果服务端发送的数据超过了客户端接收数据的最大值,客户端只能接收设定值大小,其余将存放在缓冲区,下一次服务端再发送数据的时候,是将缓冲区里面的数据发送完以后,再发送新的返回数据。

浙公网安备 33010602011771号