最近看了其他的一些python实现的socket通信的相关样例或demo代码,一直想找一个较为不错的样例作为编写socket通信的一个样板,固定下编写风格,但是苦于找到的相关样例都是非常简易的一个demo,甚至针对于超过buf_size需要多次recv数据然后进行数据拼接都没有做。这样就导致demo在演示传输较大数据或者是传输文件二进制流,或是其他编码流的方面产生无法解析或无法解码的情况出现。或是在聊天场景下发送超过buf_size的信息会出现收取断层的情况。基于以上问题,设计了这么一个实现思路。
实现思路:
将传输的数据进行base64编码后再添加自定义结尾符号,判断超过buf_size的数据是否收取完成。
完整思路过程

针对实际开发环境,在此代码的基础上可以再进行设计通信数据加密,来保证通信过程中数据的安全。

采用RSA+AES混合加密手段来保证通信过程中的数据安全措施,请看另一篇文章。
使用RSA+AES混合加密手段加密Socket通信过程中的数据

服务端代码:Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from socketserver import BaseRequestHandler, ThreadingTCPServer
import base64

BUF_SIZE = 1024 # 通常设置buf大小为1024


class Handler(BaseRequestHandler):
@classmethod
def en_data(cls, data):
"""
构造bytes数据
:param data: str
:return: base64-bytes
"""
return base64.b64encode(data.encode())+b"#"

@classmethod
def de_data(cls, data):
"""
解析base64-bytes数据
:param data: base64-bytes
:return: str
"""
return base64.b64decode(data).decode()

def handle(self):
while True:
data = b''
while True:
try:
recv_data = self.request.recv(BUF_SIZE)
if len(recv_data) > 0:
if recv_data[-1:] == b'#':
data += recv_data[:-1]
break
else:
data += recv_data
else:
break
except Exception as e:
print("Socket receiving data error! | "+str(e))
break

if len(data) != 0:
recv = self.de_data(data)
print('收到数据:', recv)
self.request.sendall(self.en_data("服务端已收到数据: "+str(recv)))
else:
print("Client Close!")
break


def main(host, port):
ADDR = (host, port)
server = ThreadingTCPServer(ADDR, Handler) # 参数为监听地址和已建立连接的处理类
print('Server Start!')
server.serve_forever() # 监听,建立好TCP连接后,为该连接创建新的socket和线程,并由处理类中的handle方法处理


if __name__ == '__main__':
main("127.0.0.1", 8089)

客户端代码:Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from socket import *
import base64


class SocketClient(object):
"""
客户端
"""
def __init__(self, host, port):
"""
建立连接对象
:param host: 主机地址
:param port: 主机通信端口
"""
self.HOST = host
self.PORT = port

self._BUFSIZ = 1024
self._ADDR = (self.HOST, self.PORT)
self._tcpCliSock = socket(AF_INET, SOCK_STREAM)
self._tcpCliSock.connect(self._ADDR)

def sent_data(self, data=None):
"""
发送数据,短连接
:param data: 要发送的数据 [str]
:return: 收到的服务端发来的数据或状态 [str]
"""
if data:
self._tcpCliSock.sendall(self._en_data(data))
recv = self._recv_data()
self.close()
return recv
else:
print("The sent data is empty or not sent!")

def sent_data_pending_input(self):
"""
发送数据,长连接
"""
lastdata = ''
while lastdata != "quit":
lastdata = input("请输入要发送的数据:")
if len(lastdata) > 0:
self._tcpCliSock.sendall(self._en_data(lastdata))
recv = self._recv_data()
print(recv)
else:
print("The sent data is empty or not sent!")
print("Client端已退出!")

def close(self):
"""
关闭连接对象
"""
self._tcpCliSock.close()

def _recv_data(self):
"""
接收服务端数据
:return: 接收服务端数据-ERROR=None
"""
data = b''
while True:
try:
self._tcpCliSock.settimeout(10) # 设置超时时间为10s超过10s判定为服务端没有返回状态,未收到数据
recv_data = self._tcpCliSock.recv(self._BUFSIZ)
if len(recv_data) > 0:
if recv_data[-1:] == b'#':
data += recv_data[:-1]
break
else:
data += recv_data
else:
break
except Exception as e:
print("Socket receiving data error! | "+str(e))
return None # 出现异常返回None

if len(data) != 0:
return self._de_data(data)
else:
return "" # 非异常无数据,返回空字符串

@classmethod
def _en_data(cls, data):
"""
构造bytes数据
:param data: str
:return: base64-bytes
"""
return base64.b64encode(data.encode())+b"#"

@classmethod
def _de_data(cls, data):
"""
解析base64-bytes数据
:param data: base64-bytesS
:return: str
"""
return base64.b64decode(data).decode()


if __name__ == '__main__':
cli_obj = SocketClient('127.0.0.1', 8089)
# print(cli_obj.sent_data("helloworld")) # 发送一次连接断开socket,短链接测试请用这个
cli_obj.sent_data_pending_input() # 持续发送,长连接测试请用这个