socketserver.ThreadingTCPServer
ThreadingTCPServer, 可以支持多个client同时通信, 它为每个client创建一个线程,这样即使前一个 client 还没有通信完成,后一个 client 也可以和 server 建立连接进行通信。
1. server 代码
import socketserver
bufsize = 1024
bind_host = 'localhost'
bind_port = 40001
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
client_sock = self.request
while (True):
try:
print('server 开始接收中...')
message = client_sock.recv(bufsize)
print('client addr ===> {0}'.format(client_sock.getpeername()))
print('recv : {0} len : {1}'.format(message,len(message)))
client_sock.send('yes'.encode('utf-8'))
except (BrokenPipeError, ConnectionResetError): # 由于对方断开连接而产生的异常
print("Other end had a timeout, socket closed")
client_sock.close()
break
except:
print(
"Error handling request (closing socket, client will retry)"
)
client_sock.close()
break
print(client_sock, ' ===> stopped')
server = socketserver.ThreadingTCPServer((bind_host, bind_port), Myserver)
print("socket server start.....")
server.serve_forever()
简单来说, server 启动后, 每当有 client 尝试连接 server, 就会创建一个线程, 这个线程运行 handle() 的代码, 这里我们认为 client 每次发送的数据量都小于 bufsize=1024 个字节(bufsize大小自己定义,不一定是1024,甚至可以是 1,或者 2 等等,socket 发送的数据必须是字节对象),这样 server 就可以通过一次 recv() 函数接收了。
为了实现 client 一次建立连接,可以多次与 server 进行通信, 我们在 handle() 中使用 while(True) 的方式阻止这个线程结束, 也就是说连接建立后, server 和 client 的连接通信会一直保持 (只要 client 不主动断开)。而 server 会一直阻塞在 recv() 上, 每当 client 发送一次数据, server 就会通过 recv() 函数接收, 并作出相应(向 client 回复一个 ‘yes’,为什么要回复,在后面解释),之后又继续阻塞在 recv()。
在这种方式下,其实如果 client 发送的数据超过 bufsize,那么由于 while(True) 的存在, server 会反复运行 recv(),直到所有数据接收完,然后继续阻塞在 recv() 上面。
2. client 代码
import socket
server_host = 'localhost'
server_port = 40001
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((server_host, server_port))
print('my addr ===> {0}'.format(sock.getsockname()))
message1 = '你好,我是client'.encode('utf-8')
input('回车发送')
print('sending {0} '.format(message1))
sock.send(message1)
message2 = '很高兴认识你'.encode('utf-8')
input('回车再次发送')
print('sending {0} '.format(message2))
sock.send(message2)
input('结束通信')
sock.close()
client 的代码很简单,使用 connect() 函数连接到 server,然后发送了 2 次消息,最后结束通信。
3. 运行示例
在 2 个终端分别启动 server 和 client 的代码
server 启动
socket server start.....
server 开始接收中...
client 第 1 次发送
my addr ===> ('127.0.0.1', 44452)
回车发送
sending b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
回车再次发送
server 第 1 次接收
client addr ===> ('127.0.0.1', 44452)
recv : b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
server 开始接收中...
client 第 2 次发送
sending b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
结束通信
server 第 2 次接收
client addr ===> ('127.0.0.1', 44452)
recv : b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
server 开始接收中...
client 结束通信
结束通信
server 相应结束
Other end had a timeout, socket closed
<socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> ===> stopped
4. 修改 sever 的 bufsize 为 8
client 第 1 次发送数据
my addr ===> ('127.0.0.1', 44404)
回车发送
sending b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
回车再次发送
server 第 1 次接收情况
socket server start.....
server 开始接收中...
client addr ===> ('127.0.0.1', 44404)
recv : b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6' len : 8
server 开始接收中...
client addr ===> ('127.0.0.1', 44404)
recv : b'\x88\x91\xe6\x98\xafcli' len : 8
server 开始接收中...
client addr ===> ('127.0.0.1', 44404)
recv : b'ent' len : 3
server 开始接收中...
可以看到 server 每次接收 8 个字节的数据, 然后不停调用 while 循环直到数据接收完,最后一次是接收了 3 个字节的数据。
client 第 2 次发送数据
回车再次发送
sending b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
结束通信
server 第 2 次接收情况
server 开始接收中...
client addr ===> ('127.0.0.1', 44404)
recv : b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85' len : 8
server 开始接收中...
client addr ===> ('127.0.0.1', 44404)
recv : b'\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4' len : 8
server 开始接收中...
client addr ===> ('127.0.0.1', 44404)
recv : b'\xbd\xa0' len : 2
server 开始接收中...
client 结束通信
结束通信
server 也结束
Other end had a timeout, socket closed
<socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> ===> stopped
3 和 4 client 的端口不一样是因为每次重新运行 client 的代码,系统都会为 client 自动分配一个端口,不一定是同一个端口号。
5. 如何判断 client 是否在线
对于 server 来说,判断与 client 的连接是否正常十分重要,而对于 client 来说,结束通信是一件很随意的事情, 直接结束程序或者使用 close() 函数都可以结束通信,但是 server 必须时刻判断连接是否依然存在。
现在我们注释掉
# client_sock.send('yes'.encode('utf-8'))
handle() 的代码如下,为了便于调试,在 server 每次recv() 后使用 input(‘确认接收’) 来暂停一下程序
bufsize=1024
def handle(self):
client_sock = self.request
while (True):
try:
print('server 开始接收中...')
message = client_sock.recv(bufsize)
print('client addr ===> {0}'.format(client_sock.getpeername()))
print('recv : {0} len : {1}'.format(message,len(message)))
# client_sock.send('yes'.encode('utf-8'))
input('确认接收')
except (BrokenPipeError, ConnectionResetError): # 由于对方断开连接而产生的异常
print("Other end had a timeout, socket closed")
client_sock.close()
break
except:
print(
"Error handling request (closing socket, client will retry)"
)
client_sock.close()
break
print(client_sock, ' ===> stopped')
client 第 1 次发送数据
my addr ===> ('127.0.0.1', 44982)
回车发送
sending b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
回车再次发送
server 第 1 次接收数据,并确认
socket server start.....
server 开始接收中...
client addr ===> ('127.0.0.1', 44982)
recv : b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
确认接收
server 开始接收中..
client 第 2 次发送数据
sending b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
结束通信
server 第 2 次接收数据
client addr ===> ('127.0.0.1', 44982)
recv : b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
确认接收
server 开始接收中
client 结束通信
结束通信
server 没有正常结束,而是接收到了一个 b’',而此时 client 程序已经结束了
client addr ===> ('127.0.0.1', 44982)
recv : b'' len : 0
确认接收
server 确认后又会收到 b’’
server 开始接收中...
client addr ===> ('127.0.0.1', 44982)
recv : b'' len : 0
确认接收
server 再次确认再次收到 b’‘,不断确认不断收到 b’’
server 开始接收中...
client addr ===> ('127.0.0.1', 44982)
recv : b'' len : 0
确认接收
server 开始接收中...
client addr ===> ('127.0.0.1', 44982)
recv : b'' len : 0
确认接收
server 开始接收中...
client addr ===> ('127.0.0.1', 44982)
recv : b'' len : 0
确认接收
server 开始接收中...
client addr ===> ('127.0.0.1', 44982)
recv : b'' len : 0
确认接收
而实际上,client 早就结束了
但是这里并没有触发异常,应该是哪里没有处理好,现在不知道怎么解释这个问题
当然,这里可以通过判断接收到的数据是不是 b’’ 来判断 client 是不是已经结束通信了
此时 handle() 的代码修改如下
def handle(self):
client_sock = self.request
while (True):
try:
print('server 开始接收中...')
message = client_sock.recv(bufsize)
print('client addr ===> {0}'.format(client_sock.getpeername()))
print('recv : {0} len : {1}'.format(message,len(message)))
# client_sock.send('yes'.encode('utf-8'))
input('确认接收')
if message==b'':
print('client closed')
client_sock.close()
break
except (BrokenPipeError, ConnectionResetError): # 由于对方断开连接而产生的异常
print("Other end had a timeout, socket closed")
client_sock.close()
break
except:
print(
"Error handling request (closing socket, client will retry)"
)
client_sock.close()
break
print(client_sock, ' ===> stopped')
这样确实是可行的,不过 try-except 就没有作用了。
同样,这里给出运行过程
client 第 1 次发送数据
my addr ===> ('127.0.0.1', 45134)
回车发送
sending b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
回车再次发送
server 第 1 次接收数据并确认
socket server start.....
server 开始接收中...
client addr ===> ('127.0.0.1', 45134)
recv : b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
确认接收
server 开始接收中...
client 第 2 次发送数据
sending b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
结束通信
server 第 2 次接收数据并确认
client addr ===> ('127.0.0.1', 45134)
recv : b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
确认接收
server 开始接收中...
client 结束通信
结束通信
server 收到 b’’
client addr ===> ('127.0.0.1', 45134)
recv : b'' len : 0
确认接收
server 确认接收后, 关闭连接退出 while 循环
client closed
<socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> ===> stopped
在最先实验的案例里面,我们采用了下面的方式来判断 client 是否断开
为了判断 client 的是否已经结束,在 server 端每次 recv() 都会发送 send() 一个响应消息,如果 client 断开, 就会触发异常,然后退出 while(True) 循环。至于 send() 什么消息不重要, 不过发现如果仅仅调用 send(),发送的内容为空的话是没有用的,必须发送一些东西才行。
比如
client_sock.send(''.encode('utf-8'))
是不行的,至少发送一个字符才可以
client_sock.send('a'.encode('utf-8'))
6. 遗留的问题
-
在 server 没有向 client send() 消息的时候,为什么 client 结束后,server 会一直收到 b’’
-
按照最先的处理方式,server 在 recv() 后向 client 进行 send(),发现当 client 断开连接后,handle() 函数代码存在执行上问题,可以发现有些代码已经不执行了,这里和 3 中运行示例是一样的,重复一下
server 在 recv() 后向 client 进行 send(),即 handle() 代码如下
def handle(self):
client_sock = self.request
while (True):
try:
print('server 开始接收中...')
message = client_sock.recv(bufsize)
print('client addr ===> {0}'.format(client_sock.getpeername()))
print('recv : {0} len : {1}'.format(message,len(message)))
client_sock.send('yes'.encode('utf-8'))
except (BrokenPipeError, ConnectionResetError): # 由于对方断开连接而产生的异常
print("Other end had a timeout, socket closed")
client_sock.close()
break
except:
print(
"Error handling request (closing socket, client will retry)"
)
client_sock.close()
break
print(client_sock, ' ===> stopped')
client 第 1 次发送数据
my addr ===> ('127.0.0.1', 45528)
回车发送
sending b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
回车再次发送
server 第 1 次接收数据
socket server start.....
server 开始接收中...
client addr ===> ('127.0.0.1', 45528)
recv : b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
server 开始接收中...
client 第 2 次发送数据
sending b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
结束通信
server 第 2 次接收数据
client addr ===> ('127.0.0.1', 45528)
recv : b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
server 开始接收中...
client 结束通信
结束通信
server 响应结束
Other end had a timeout, socket closed
<socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> ===> stopped
这里的问题是,当 server 第 2 次接收数据后, server 与 client 的通信线程就阻塞在 recv()
print('server 开始接收中...')
message = client_sock.recv(bufsize)
那么当 client 结束通信后,server 直接越过了下面的代码
print('client addr ===> {0}'.format(client_sock.getpeername()))
print('recv : {0} len : {1}'.format(message,len(message)))
client_sock.send('yes'.encode('utf-8'))
马上进入 except 代码
except (BrokenPipeError, ConnectionResetError): # 由于对方断开连接而产生的异常
print("Other end had a timeout, socket closed")
client_sock.close()
break
之后结束通信
这个问题和问题 1 是对应的,问题 1 中 server 没有使用 send(),在 client 结束后, 就一直陷入
while (True):
try:
print('server 开始接收中...')
message = client_sock.recv(bufsize)
print('client addr ===> {0}'.format(client_sock.getpeername()))
print('recv : {0} len : {1}'.format(message,len(message)))
的无限循环中
而在问题 2 中,直接越过了
print('client addr ===> {0}'.format(client_sock.getpeername()))
print('recv : {0} len : {1}'.format(message,len(message)))
client_sock.send('yes'.encode('utf-8'))
那么
client_sock.send('yes'.encode('utf-8'))
到底有没有执行呢?
不理解…
7. 补充
关于 client_sock 的值,此处使用 1 中的代码
client_sock = self.request
当 client 向服务器使用 connect() 建立连接时
查看 client_sock
client_sock = self.request
print(client_sock)
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001), raddr=('127.0.0.1', 45786)>
其中 socket.socket fd=4,同时后面标识出 server(laddr) 和 client(raddr) 的地址
并且 server 发现 client 断开,在 except 中关闭 client_sock (client_sock.close())后,发现 client_sock 的值就变成
<socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
其中 socket.socket [closed] fd = -1,标识出 [close],且不再显示 server 和 client 的地址
此处测试的 handle() 代码如下
def handle(self):
client_sock = self.request
print(client_sock)
while (True):
try:
print('server 开始接收中...')
message = client_sock.recv(bufsize)
print('client addr ===> {0}'.format(client_sock.getpeername()))
print('recv : {0} len : {1}'.format(message,len(message)))
client_sock.send('yes'.encode('utf-8'))
print(client_sock)
except (BrokenPipeError, ConnectionResetError): # 由于对方断开连接而产生的异常
print("Other end had a timeout, socket closed")
client_sock.close()
break
except:
print(
"Error handling request (closing socket, client will retry)"
)
client_sock.close()
break
print(client_sock, ' ===> stopped')
然后如果把这里的 except 中的 client_sock.close()注释掉,即
except (BrokenPipeError, ConnectionResetError): # 由于对方断开连接而产生的异常
print("Other end had a timeout, socket closed")
# client_sock.close()
break
except:
print(
"Error handling request (closing socket, client will retry)"
)
# client_sock.close()
break
此时当 client 结束通信,server 仍然触发 except 的代码,跳出 while 循环,
由于 server 没有使用 client_sock.close() 关闭连接,我们再次查看 client_sock 的值
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001)>
而使用 client_sock.close() 正常关闭时
<socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
对比起来
发现在没有正常关闭 socket 连接的情况下,socket.socket fd 的值还是等于 4 ,且没有[closed] 标识,同时还保留了 server 的地址, 但是已经没有 client 的地址了。
所以最好还是手动 sock.close() 一下。
毕竟 python 官方文档是这么说的
垃圾回收时,套接字会自动关闭,但建议显式 close() 它们,或在它们周围使用 with 语句。
不过好像这里提示我们可以判断 client_sock 中有没有 client(i.e., raddr) 的 地址 来判断 server 与 client 的连接是否正常。
经过测试发现,在 client 断开的情况下,如果 server 不使用 client_sock.send() 去响应 client,client_sock 仍然还保留了 client (i.e., raddr) 的值
测试代码如下
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
client_sock = self.request
print(client_sock)
while (True):
try:
print('server 开始接收中...')
message = client_sock.recv(bufsize)
print('client addr ===> {0}'.format(client_sock.getpeername()))
print('recv : {0} len : {1}'.format(message,len(message)))
print(client_sock)
toSend=input('确认接收')
if toSend=='y':
client_sock.send('yes'.encode('utf-8'))
print(client_sock)
input('pause')
except (BrokenPipeError, ConnectionResetError): # 由于对方断开连接而产生的异常
print("Other end had a timeout, socket closed")
client_sock.close()
break
except:
print(
"Error handling request (closing socket, client will retry)"
)
client_sock.close()
break
print(client_sock, ' ===> stopped')
client 第 1 次发送数据
my addr ===> ('127.0.0.1', 55976)
回车发送
sending b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
回车再次发送
server 第 1 次接收数据,并确认
socket server start.....
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001), raddr=('127.0.0.1', 55976)>
server 开始接收中...
client addr ===> ('127.0.0.1', 55976)
recv : b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001), raddr=('127.0.0.1', 55976)>
确认接收
server 开始接收中...
client 第 2 次发送数据
sending b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
结束通信
server 第 2 次接收数据,并确认
client addr ===> ('127.0.0.1', 55976)
recv : b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001), raddr=('127.0.0.1', 55976)>
确认接收
server 开始接收中..
client 结束通信
结束通信
server 开始收到 b’’
client addr ===> ('127.0.0.1', 55976)
recv : b'' len : 0
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001), raddr=('127.0.0.1', 55976)>
确认接收
server 不断确认,不断收到 b’’
server 开始接收中...
client addr ===> ('127.0.0.1', 55976)
recv : b'' len : 0
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001), raddr=('127.0.0.1', 55976)>
确认接收
server 开始接收中...
client addr ===> ('127.0.0.1', 55976)
recv : b'' len : 0
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001), raddr=('127.0.0.1', 55976)>
确认接收
此时发现在 client 已经关闭的情况下,client_sock 的 raddr 的值仍然存在
此时我们在 server 的 确认接收 后输入 ‘y’,向已经结束的 client 发送send() 一个消息
server 端 输入 ‘y’
确认接收y
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001)> # send() 消除了 raddr 信息
pause
可以看到在 server send() 之后,raddr 的值已经不见了
所以 server 向一个已经结束的 client 发送一个消息,可以消除 client_sock 中 client (i.e., raddr) 的信息
可见仍然需要 server 向 client 发送一个消息,才能确认 client 是否已经结束(既然这样,都是要向 client 发送一个信息,不如直接使用 1 的方式判断 client 是否在线)
server 在 pause 后面输入 回车 继续执行,发现触发了另一个 except,也不理解…,然后 server 退出 while
server 开始接收中...
Error handling request (closing socket, client will retry)
<socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> ===> stopped # close() 标记 [closed], 置 fd=-1,消除地址信息(laddr)
当然这里 socket.socket [closed] fd=-1,后面的 laddr 没有了, 是因为使用了 close() 关闭了 client_sock,如果没有使用 close() 函数,则结果是这样的
server 开始接收中...
client addr ===> ('127.0.0.1', 56264)
recv : b'' len : 0
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001), raddr=('127.0.0.1', 55976)>
确认接收y
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001)> # send() 消除了 raddr 信息
pause
server 开始接收中...
Error handling request (closing socket, client will retry)
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001)> ===> stopped # 没有 close(),client_sock 的信息没有变化
在不使用 close() 的情况下,client_sock 的 socket.socket fd 不会主动显示结束[closed] (-1),server 地址也会保留
总结来说,最好还是让 server 向 client 发送 send() 一个响应,如果 client 已经结束,server 会将 client_sock 的 client (i.e., raddr) 去掉;一旦确认 client 已经结束,server 最好手动调用 client_sock.close(),使其 socket.socket [closed]=-1,且去掉 server 地址 (laddr)
但既然 server 要向 client 发送 send() 一个响应,不如就用 1 的代码(最初的代码)来处理 client 是否在线的问题
最后,通过以下代码测试 close() 到底做了什么
在 server 响应 client 的请求后,立即调用 client_sock.close(),关闭连接
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
client_sock = self.request
print(client_sock)
client_sock.close()
print(client_sock)
input('test')
while (True):
try:
print('server 开始接收中...')
message = client_sock.recv(bufsize)
print('client addr ===> {0}'.format(client_sock.getpeername()))
print('recv : {0} len : {1}'.format(message,len(message)))
# client_sock.send('yes'.encode('utf-8'))
print(client_sock)
toSend=input('确认接收')
if toSend=='y':
client_sock.send('yes'.encode('utf-8'))
print(client_sock)
input('pause')
except (BrokenPipeError, ConnectionResetError): # 由于对方断开连接而产生的异常
print("Other end had a timeout, socket closed")
client_sock.close()
break
except:
print(
"Error handling request (closing socket, client will retry)"
)
client_sock.close()
break
print(client_sock, ' ===> stopped')
启动 server
socket server start.....
client 建立与 server 的连接
my addr ===> ('127.0.0.1', 56662)
回车发送
查看 server 输出信息,发现连接建立后,client_sock 的值正常,之后马上调用 client_sock.close(),发现 socket.socket [closed]=-1,server (i.e., laddr) 和 client (i.e., raddr) 也没有了,说明 close() 函数就是修改 fd 的值,并标记为 [closed],以及将 通信双方的地址信息 消除。
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 40001), raddr=('127.0.0.1', 56662)>
<socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
test
在 server 端 回车后,继续执行代码,此时触发了 except
server 开始接收中...
Error handling request (closing socket, client will retry)
<socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> ===> stopped
server 直接结束了与 client 通信的线程
此时如果 client 发送第 1 条消息,server 无任何响应
sending b'\xe4\xbd\xa0\xe5\xa5\xbd,\xe6\x88\x91\xe6\x98\xafclient' len : 19
回车再次发送
client 尝试发送第 2 条消息,client 出现异常,server 仍无任何响应
sending b'\xe5\xbe\x88\xe9\xab\x98\xe5\x85\xb4\xe8\xae\xa4\xe8\xaf\x86\xe4\xbd\xa0' len : 18
Traceback (most recent call last):
File "client.py", line 20, in <module>
sock.send(message2)
BrokenPipeError: [Errno 32] Broken pipe