使用socket进行网络通信时踩过的坑
我们现在先简单实现跟一个人来回通话过程。
客户端:
通过循环来输入多次命令,若代码如下:
import socket
client = socket.socket() #声明socket类型,同时生成socket连接对象
client.connect(('localhost',6969))
while True:
msg = input(">>:").strip()
client.send(msg.encode("utf-8"))
data = client.recv(10240)
print("recv:",data.decode())
client.close()
client.recv(1024): 1024换算一下表示每次只接收1K的内容
服务器端:
使用多次接收,如果代码如下:
import socket
client = socket.socket() #声明socket类型,同时生成socket连接对象
server.bind(('localhost',6969)) #绑定要监听端口
server.listen(5) #监听
print("我要开始等电话了")
while True:
conn, addr = server.accept() # 等电话打进来
# conn就是客户端连过来而在服务器端为其生成的一个连接实例
print(conn, addr)
print("电话来了")
data = conn.recv(1024)
print("recv:",data)
conn.send(data.upper())
server.close()
如果是上述代码,那么造成的结果,就是嘻嘻嘻:
很多人连上来,但是每次只能跟服务端说一句话,然后第二句话卡住。
第二个客户端一样,每次只能跟服务端说一句话,然后第二句话卡住:
也说明服务端写成这种代码是不行的,会造成代码卡住。
我们现在先来实现跟一个人可以来来往往的说话:(暂时无法实现跟多个人)
import socket
client = socket.socket() #声明socket类型,同时生成socket连接对象
server.bind(('localhost',6969)) #绑定要监听端口
server.listen(5) #监听
print("我要开始等电话了")
conn, addr = server.accept() # 等电话打进来
# conn就是客户端连过来而在服务器端为其生成的一个连接实例
print(conn, addr)
print("电话来了")
while True:
data = conn.recv(1024)
print("recv:",data)
以上的服务端代码已经可以跟客户端实现完美的交换式通讯,但是你会发现产生一个新的问题:
当客户端断开的时候,服务端提示报错,然后也断开连接,结束代码了。
其实这个是win上的一个BUG,其中一个连接断开的时候,server也断开了,这个是不对的。
但是在linux上是可以正常执行的,客户端断开,服务端不会停止代码,也不会停止服务。
运行结果图如下:
下面我们放入Linux下运行:
但是现在运行在linux依旧还是有BUG,我们发现当客户端断开的时候,服务端会进入一个死循环。
表示服务端不停的接收到了空字符的,造成了死循环。所以我们要修改服务端的代码,当接收到空字符串的时候,不要进入死循环,而重新接收状态:
修正后的服务端代码如下:
print("我要开始等电话了")
while True:
conn, addr = server.accept() # 等电话打进来
# conn就是客户端连过来而在服务器端为其生成的一个连接实例
print(conn, addr)
print("电话来了")
while True:
data = conn.recv(1024)
print("recv:",data)
if not data:
print("client has lost...")
break
conn.send(data.upper())
server.close()
这样实现了,第一个人可以跟服务端不停的通讯,这个时候如果第二个人进来想通话,会卡住;直到第一个人跟服务端断开以后,第二个人可以立即接入进行跟服务端进行通讯~!
这边有个要注意的地方server.listen(5),这里的5表示最多允许有5个client挂起,等待中,排队中。。。不过这个必须在异步的情况下,才可以测试出来。
然后,我们又发现了一个bug,如果客户端client发送一个空,会造成程序卡死。
client.send不能发送空!不能发送空!所以我们客户端的代码要改进下:
import socket
client = socket.socket() #声明socket类型,同时生成socket连接对象
client.connect(('localhost',6969))
while True:
msg = input(">>:").strip()
if len(msg) == 0:continue
client.send(msg.encode("utf-8"))
data = client.recv(10240)
print("recv:",data.decode())
client.close()
我们现在来模拟客户端是ssh:
先来看看服务端的代码的修改:
客户端执行代码,可以正常返回。
但是现在有几个新问题:
不能执行top命令,因为top命令会一直执行刷新,不停变化:
但是我们可以执行top -bn 1,让top执行一次。但是由于top -bn 1返回的结果超过了1024字节,你后面再输入命令df依旧返回的是top -bn 1的命令结果,继续执行新命令还是返回top -bn 1的返回值:如下图
这个时候我们需要修改客户端接收信息的大小,我们原来是1024字节,修改为102K,如下图:
这个时候,我们再执行top -bn 1,就 可以返回正常的所有值了。
如果我们这个时候发送一个超大文件,比如60M的文件:
我们来看看服务端的代码:(注意这里是py2的代码,到py3,必须先转换为二进制)
来看看客户端的代码修改:
执行上诉代码以后,我们发现服务端的conn.send也是有大小限制的,我们发现每次最多值发送32K大小的文件:
所以我们要修改服务端的代码,进行循环send,发送文件直到把文件发送出去。 我们需要用到conn.sendall方法:
虽然我们使用了sendall的方法,但是我们要注意服务端第一次只发送32K的文件(第2,第3次依次增加,但是最大不超过客户端的recv(xxxx),xxxx设置的数值,上诉例子设置的是10M),所以你客户端需要多次send命令,让服务端不停的往客户端发送文件。
我们用f.flush的方法,来强制客户端写入文件,让我们可以看到文件的增加的大小。
下面我们来看看文件的传输过程:
第一次:
第二次:
第三次:
文件的传输最大不超过10M/次,客户端的recv的参数设置