main函数自定义,针对个人需求处理发送的消息和接收的数据,以下是Server,为了保证传输的数据完整性,增加简单的数据校验,在发送的数据前加标志位,如:#234,代表以#开始,#后一位数代表后面34的长度为2,34则代表发送的数据的长度。
import json
import socket
import threading
import time
from socket import AF_INET, SOCK_STREAM
from typing import Union
class SocketServer:
def __init__(self, ip: str, port: int):
# 一般大写字母用来表示配置信息
self.server = None
self.client_address = None
self.client_socket = None
self.SERVER_IP = ip
print(self.SERVER_IP)
self.SERVER_PORT = port
self.BUF_LEN = 1024 # 一次性接受的最大字节数
self.__start_server()
self.__keep_runing = True
threading.Thread(target=self.__ping_clients).start()
def __start_server(self):
self.server = socket.socket(AF_INET, SOCK_STREAM) # 监听端口,使用INET即为网络层使用IP协议,STREAM表示传输层使用TCP协议
self.server.bind((self.SERVER_IP, self.SERVER_PORT)) # 监听端口需要绑定ip地址和端口号,bind中只能是一个参数,因此使用元组传参
self.server.listen(5) # 最多接受五个等待连接的客户端
def __wait_client(self):
client_socket, client_address = self.server.accept() # 等待新客户端连接
print("连接已建立:", client_address)
self.client_socket = client_socket
self.client_address = client_address
def send(self, data):
"""
仅仅发送消息,无需等待返回值
:param data:
:return:
"""
msg_length = str(len(data))
command = f"#{len(msg_length)}{msg_length}{data}"
self.client_socket.sendall(command.encode("utf-8"))
def recvive(self):
# 接收客户端的消息
data = self.client_socket.recv(self.BUF_LEN)
if not data: # 连接已关闭
return None
else:
message = data.decode("utf-8")
print("收到客户端消息:", message)
return message
def __ping_clients(self):
"""
判断连接是否存活
"""
while self.__keep_runing:
if self.client_socket:
try:
self.send("ping")
except socket.error:
# Client is most likely disconnected
print("连接已关闭:", self.client_address)
self.client_socket = None
self.client_address = None
else:
...
time.sleep(5)
else:
print(f"线程停止!")
def send_msg(self, command: Union[dict, str]):
"""
发送消息并等待返回
:param command:
:return:
"""
if isinstance(command, dict):
command = json.dumps(command)
else:
...
while True:
if self.client_socket:
self.send(command)
# 接收客户端的消息
message = self.recvive()
if message == 'ok':
# 回复客户端
response = f"已收到消息:{message}"
self.send(response)
break
elif message == 'no':
print(f"Send again")
time.sleep(3)
continue
else:
break
else:
self.__wait_client()
continue
def close(self):
self.__keep_runing = False
self.client_socket.close()
self.server.close()
def main():
server = SocketServer('127.0.0.1', 8888)
cmd = {'start_test': ''}
server.send_msg(cmd)
time.sleep(10)
server.send_msg('close')
server.close()
if __name__ == '__main__':
main()
Client客户端,将接收到的字典保存到txt中
import threading
import time
from pathlib import Path
from socket import socket
TXT_PATH = Path.cwd() / 'data.txt'
class SocketClient:
def __init__(self, server_ip: str, server_port: int):
self.command = None
self.BUF_LEN = 1024
self.server_socket = socket()
server_address = (server_ip, server_port)
self.server_socket.connect(server_address)
self.keep_running = True
def start_receive(self):
threading.Thread(target=self.__receive_ping).start()
def send(self, msg):
self.server_socket.send(msg.encode())
@staticmethod
def __check_data(data: str):
"""
检查收到的信息是否完整
:param data:
:return:
"""
idx = data.find('#')
length_of_data_len = int(data[idx + 1])
data_length = int(data[idx + 2: idx + 2 + length_of_data_len])
msg = data[idx + 2 + length_of_data_len:]
if len(msg) == data_length:
return msg
else:
return False
def __receive_ping(self):
"""
接收心跳信息
"""
while self.keep_running:
recv_data = self.server_socket.recv(self.BUF_LEN).decode() # 发送完数据,等待接受不超过1024字节的回应数据
msg = self.__check_data(recv_data)
if msg is False:
self.send('no')
continue
elif msg == 'ping' or not recv_data:
...
elif msg == '已收到消息:ok':
pass
elif msg == 'close':
self.command = msg
self.close()
break
else:
self.command = msg
print(msg)
self.__save_task_to_txt(msg)
self.send('ok')
else:
print(f'线程停止!')
@staticmethod
def __save_task_to_txt(msg: str):
data = eval(msg)
if type(data) != dict:
return
with open(TXT_PATH, 'w') as f:
for func in data:
f.write('{'+f"'{func}': '{data[func]}'"+'}\n')
def close(self):
self.keep_running = False
self.server_socket.close()
def main():
ip = '127.0.0.1'
client = SocketClient(ip, 8888)
client.start_receive() # 开启线程接收消息