[Python 网络编程] TCP Client (四)

时间:2023-03-09 09:07:17
[Python 网络编程] TCP Client (四)

TCP Client

  • 客户端编程步骤:
  • 创建socket对象
  • 连接到服务端的ip和port,connect()方法
  • 传输数据
    •   使用send、recv方法发送、接收数据
  • 关闭连接,释放资源

最简单的客户端:

#TCP Client客户端
import socket client = socket.socket()
client.connect(('127.0.0.1',9999)) client.send("Hi, I'm client1.".encode())
client.close() #运行

  

服务端状态:

[16:08:25]	 [showthreads,1796] [<_MainThread(MainThread, started 9816)>, <Thread(show_client, started daemon 9344)>, <Thread(accept, started daemon 5908)>, <Thread(showthreads, started 1796)>]
[16:08:26] [accept,5908] <socket.socket fd=424, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 5287)>-('127.0.0.1', 5287)
[16:08:26] [show_client,9344] {('127.0.0.1', 5287): <_io.TextIOWrapper mode='rw' encoding='utf8'>}
2017/12/24 16:08:26 127.0.0.1:5287
Hi, I'm client1. [16:08:26] [recv,980] 2017/12/24 16:08:26 127.0.0.1:5287
Hi, I'm client1. [16:08:26] [recv,980] ('127.0.0.1', 5287) quit
[16:08:28] [showthreads,1796] [<_MainThread(MainThread, started 9816)>, <Thread(show_client, started daemon 9344)>, <Thread(accept, started daemon 5908)>, <Thread(showthreads, started 1796)>]

  

将上面的TCP Client封装成类:

1)搭架子

#TCP Client客户端 封装成类
import socket class ChatClient:
def __init__(self):
pass def start(self):
pass def _recv(self):
pass def send(self):
pass def stop(self):
pass

  

2)基础功能

客户端:

#TCP Client客户端 封装成类
import socket,threading,logging,datetime
DATEFMT="%H:%M:%S"
FORMAT = "[%(asctime)s]\t [%(threadName)s,%(thread)d] %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT) class ChatClient:
def __init__(self,ip='127.0.0.1',port=9999):
self.sock = socket.socket()
self.addr = (ip,port) self.event = threading.Event()
self.start() def start(self):
self.sock.connect(self.addr) # 准备接收数据,recv是阻塞的,启动新的线程
threading.Thread(target=self._recv,name='recv').start() def _recv(self):
while not self.event.is_set():
try:
data = self.sock.recv(1024) #阻塞 except Exception as e:
logging.info(e) #有任何异常保证退出
break
msg = "{:%H:%M:%S} {}:{}\n{}\n".format(datetime.datetime.now(),*self.addr,data.decode().strip())
# print(type(msg),msg)
logging.info("{}".format(data.decode())) def send(self,msg:str):
data = "{}\n".format(msg.strip()).encode()
self.sock.send(data) def stop(self):
logging.info("{} broken".format(self.addr))
self.sock.close() self.event.wait(3)
self.event.set()
logging.info("byebye") def main():
e = threading.Event()
cc = ChatClient() while True:
msg = input(">>> ")
if msg.strip() == 'quit':
cc.stop()
break
cc.send(msg) if __name__ == '__main__':
main()

  

服务端:

#TCP Server 改装成makefile
import threading,logging,time,random,datetime,socket
DATEFMT="%H:%M:%S"
FORMAT = "[%(asctime)s]\t [%(threadName)s,%(thread)d] %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT) class ChatServer:
def __init__(self,ip='127.0.0.1',port=9999): #启动服务
self.addr = (ip,port)
self.sock = socket.socket()
self.event = threading.Event() self.clients = {} #客户端 def show_client(self):
while not self.event.is_set():
if len(self.clients) > 0:
logging.info(self.clients)
self.event.wait(3) def start(self):
self.sock.bind(self.addr)
self.sock.listen()
# accept会阻塞主线程,所以开一个新线程
threading.Thread(target=self._accept,name='accept',daemon=True).start()
threading.Thread(target=self.show_client,name='show_client',daemon=True).start() def stop(self):
for c in self.clients.values():
c.close()
self.sock.close()
self.event.wait(3)
self.event.set() def _accept(self):
while not self.event.is_set(): #多人连接
conn,client = self.sock.accept() #阻塞
f = conn.makefile(mode='rw')
self.clients[client] = f logging.info("{}-{}".format(conn,client))
# recv 默认阻塞,每一个连接单独起一个recv线程准备接收数据
threading.Thread(target=self._recv, args=(f, client), name='recv',daemon=True).start() def _recv(self, f, client): #接收客户端数据
while not self.event.is_set():
try:
data = f.readline()
except Exception:
data = 'quit'
finally:
msg = data.strip()
# Client通知退出机制
if msg == 'quit':
f.close()
self.clients.pop(client) logging.info('{} quit'.format(client))
break msg = "{:%Y/%m/%d %H:%M:%S} {}:{}\n{}\n".format(datetime.datetime.now(),*client,data)
# msg = data
print(msg)
logging.info(msg) for c in self.clients.values():
# print(type(msg))
c.writelines(msg)
c.flush() cs = ChatServer()
print('!!!!!!!!!!!')
cs.start()
print('~~~~~~~~~~~~~~~~~~~~')
e = threading.Event()
def showthreads(e:threading.Event):
while not e.wait(3):
logging.info(threading.enumerate()) threading.Thread(target=showthreads,name='showthreads',args=(e,)).start() while not e.wait(1): # Sever控制台退出方式
cmd = input('>>> ').strip()
if cmd == 'quit':
cs.stop()
e.wait(3)
break

  

运行结果:

#服务端
~~~~~~~~~~~~~~~~~~~~
>>> [17:26:14] [show_client,7824] {('127.0.0.1', 7517): <_io.TextIOWrapper mode='rw' encoding='cp936'>}
[17:26:14] [accept,3832] <socket.socket fd=400, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 7517)>-('127.0.0.1', 7517)
[17:26:15] [showthreads,5928] [<Thread(accept, started daemon 3832)>, <Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(showthreads, started 5928)>]
[17:26:17] [show_client,7824] {('127.0.0.1', 7517): <_io.TextIOWrapper mode='rw' encoding='cp936'>}
[17:26:18] [showthreads,5928] [<Thread(accept, started daemon 3832)>, <Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(showthreads, started 5928)>]
[17:26:19] [recv,2112] 2017/12/24 17:26:19 127.0.0.1:7517
hello1 2017/12/24 17:26:19 127.0.0.1:7517
hello1 [17:26:20] [show_client,7824] {('127.0.0.1', 7517): <_io.TextIOWrapper mode='rw' encoding='cp936'>}
[17:26:21] [showthreads,5928] [<Thread(accept, started daemon 3832)>, <Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(showthreads, started 5928)>]
[17:26:23] [show_client,7824] {('127.0.0.1', 7517): <_io.TextIOWrapper mode='rw' encoding='cp936'>}
[17:26:23] [accept,3832] <socket.socket fd=436, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 7539)>-('127.0.0.1', 7539)
[17:26:24] [showthreads,5928] [<Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(accept, started daemon 3832)>, <Thread(recv, started daemon 6748)>, <Thread(showthreads, started 5928)>]
2017/12/24 17:26:25 127.0.0.1:7539
[17:26:25] [recv,6748] 2017/12/24 17:26:25 127.0.0.1:7539
hello2
hello2 [17:26:26] [show_client,7824] {('127.0.0.1', 7517): <_io.TextIOWrapper mode='rw' encoding='cp936'>, ('127.0.0.1', 7539): <_io.TextIOWrapper mode='rw' encoding='cp936'>}
[17:26:27] [showthreads,5928] [<Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(accept, started daemon 3832)>, <Thread(recv, started daemon 6748)>, <Thread(showthreads, started 5928)>]
[17:26:29] [show_client,7824] {('127.0.0.1', 7517): <_io.TextIOWrapper mode='rw' encoding='cp936'>, ('127.0.0.1', 7539): <_io.TextIOWrapper mode='rw' encoding='cp936'>}
[17:26:30] [showthreads,5928] [<Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(accept, started daemon 3832)>, <Thread(recv, started daemon 6748)>, <Thread(showthreads, started 5928)>]
[17:26:32] [show_client,7824] {('127.0.0.1', 7517): <_io.TextIOWrapper mode='rw' encoding='cp936'>, ('127.0.0.1', 7539): <_io.TextIOWrapper mode='rw' encoding='cp936'>}
[17:26:33] [showthreads,5928] [<Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(accept, started daemon 3832)>, <Thread(recv, started daemon 6748)>, <Thread(showthreads, started 5928)>]
[17:26:35] [show_client,7824] {('127.0.0.1', 7517): <_io.TextIOWrapper mode='rw' encoding='cp936'>, ('127.0.0.1', 7539): <_io.TextIOWrapper mode='rw' encoding='cp936'>}
[17:26:36] [showthreads,5928] [<Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(accept, started daemon 3832)>, <Thread(recv, started daemon 6748)>, <Thread(showthreads, started 5928)>]

  

#客户端1
>>> hello1
[17:26:19] [recv,2604] 2017/12/24 17:26:19 127.0.0.1:7517
hello1
>>> [17:26:25] [recv,2604] 2017/12/24 17:26:25 127.0.0.1:7539
hello2 [17:26:37] [recv,2604] [WinError 10054] 远程主机强迫关闭了一个现有的连接。

  

#客户端2
>>> hello2
>>> [17:26:25] [recv,4044] 2017/12/24 17:26:25 127.0.0.1:7539
hello2 [17:26:37] [recv,4044] [WinError 10054] 远程主机强迫关闭了一个现有的连接。

  

以上例子在客户端,如果服务端主动断开,客户端需要异常处理。