socket-cyg 0.1.6__tar.gz → 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: socket-cyg
3
- Version: 0.1.6
3
+ Version: 1.0.0
4
4
  Summary: CYG socket 封装
5
5
  Author: LiuWei
6
6
  Author-email: 183074632@qq.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "socket-cyg"
3
- version = "0.1.6"
3
+ version = "1.0.0"
4
4
  description = "CYG socket 封装"
5
5
  authors = ["LiuWei <183074632@qq.com>"]
6
6
  readme = "README.md"
@@ -0,0 +1,18 @@
1
+ # pylint: skip-file
2
+ import time
3
+
4
+ from socket_cyg.socket_client import SocketClient
5
+
6
+
7
+ if __name__ == "__main__":
8
+ def my_callback(data: bytes):
9
+ """示例回调函数。"""
10
+ print(f"回调收到数据: {data.decode('UTF-8')}")
11
+
12
+ client = SocketClient("127.0.0.1", 9001, my_callback)
13
+ if client.connect():
14
+ try:
15
+ client.send_data(b"ffffff")
16
+ time.sleep(500000)
17
+ finally:
18
+ client.disconnect()
@@ -0,0 +1,126 @@
1
+ # pylint: skip-file
2
+
3
+ import socket
4
+ import threading
5
+ import logging
6
+ from typing import Callable, Optional
7
+
8
+
9
+ class SocketClient:
10
+ """用于持续与服务器通信的 TCP Socket 客户端."""
11
+
12
+ def __init__(self, host: str, port: int,
13
+ receive_callback: Optional[Callable[[bytes], None]] = None,
14
+ buffer_size: int = 1024):
15
+ """初始化 Socket 客户端.
16
+
17
+ Args:
18
+ host: 要连接的服务器主机名或 IP 地址.
19
+ port: 服务器端口号.
20
+ receive_callback: 处理接收数据的回调函数, 回调函数应接受 bytes 类型作为唯一参数.
21
+ buffer_size: 接收缓冲区大小(字节),默认为 1024.
22
+ """
23
+ self.host = host
24
+ self.port = port
25
+ self.buffer_size = buffer_size
26
+ self.receive_callback = receive_callback
27
+ self.socket = None
28
+ self.is_connected = False
29
+ self.receive_thread = None
30
+ self.logger = logging.getLogger(self.__class__.__name__)
31
+
32
+ def connect(self) -> bool:
33
+ """建立与服务器的连接, 连接成功后会自动启动后台线程持续接收数据.
34
+
35
+ Returns:
36
+ bool: 连接成功返回 True,否则返回 False.
37
+ """
38
+ try:
39
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
40
+ self.socket.connect((self.host, self.port))
41
+ self.is_connected = True
42
+ self.logger.info("已连接到服务器 %s: %s", self.host, self.port)
43
+
44
+ self.receive_thread = threading.Thread(
45
+ target=self._receive_data,
46
+ daemon=True
47
+ )
48
+ self.receive_thread.start()
49
+ return True
50
+ except Exception as e:
51
+ self.logger.warning("连接失败, %s", str(e))
52
+ self.is_connected = False
53
+ return False
54
+
55
+ def disconnect(self):
56
+ """断开与服务器的连接并释放资源."""
57
+ if self.is_connected:
58
+ self.is_connected = False
59
+ try:
60
+ if self.socket:
61
+ self.socket.close()
62
+ if self.receive_thread and self.receive_thread.is_alive():
63
+ self.receive_thread.join(timeout=1)
64
+ except Exception as e:
65
+ self.logger.warning("断开连接时出错: %s", str(e))
66
+ finally:
67
+ self.logger.info("已断开与服务器的连接")
68
+
69
+ def send_data(self, data: bytes) -> bool:
70
+ """向服务器发送数据.
71
+
72
+ Args:
73
+ data: 要发送的字节数据.
74
+
75
+ Returns:
76
+ 发送成功返回 True,失败返回 False.
77
+
78
+ Raises:
79
+ 无显式抛出异常,但内部错误会打印到控制台.
80
+ """
81
+ if not self.is_connected:
82
+ self.logger.warning("未连接到服务器")
83
+ return False
84
+
85
+ try:
86
+ self.socket.sendall(data)
87
+ return True
88
+ except Exception as e:
89
+ self.logger.warning("发送数据出错: %s", str(e))
90
+ self.disconnect()
91
+ return False
92
+
93
+ def _receive_data(self):
94
+ """持续接收数据的内部方法."""
95
+ while self.is_connected:
96
+ try:
97
+ data = self.socket.recv(self.buffer_size)
98
+ if not data: # 服务器关闭连接
99
+ self.logger.info("服务器关闭了连接")
100
+ self.disconnect()
101
+ break
102
+
103
+ if self.receive_callback:
104
+ self.logger.info("收到数据: %s", data)
105
+ self.logger.info("触发回调函数")
106
+ self.receive_callback(data)
107
+ else:
108
+ self.logger.info("收到数据: %s", data)
109
+ except ConnectionResetError:
110
+ self.logger.warning("连接被服务器重置")
111
+ self.disconnect()
112
+ break
113
+ except Exception as e:
114
+ if self.is_connected:
115
+ self.logger.warning("接收数据出错: %s", str(e))
116
+ self.disconnect()
117
+ break
118
+
119
+ def __enter__(self):
120
+ """实现上下文管理协议,进入时自动连接."""
121
+ self.connect()
122
+ return self
123
+
124
+ def __exit__(self, exc_type, exc_val, exc_tb):
125
+ """实现上下文管理协议,退出时自动断开连接."""
126
+ self.disconnect()
@@ -1,3 +1,4 @@
1
+ # pylint: skip-file
1
2
  """异步socket."""
2
3
  import asyncio
3
4
  import datetime
@@ -7,10 +8,8 @@ import socket
7
8
  import sys
8
9
  from asyncio import AbstractEventLoop
9
10
  from logging.handlers import TimedRotatingFileHandler
10
- from typing import Union
11
11
 
12
12
 
13
- # pylint: disable=R0801, disable=R0902
14
13
  class CygSocketServerAsyncio:
15
14
  """异步socket class."""
16
15
  LOG_FORMAT = "%(asctime)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s"
@@ -19,15 +18,12 @@ class CygSocketServerAsyncio:
19
18
  tasks = {}
20
19
  loop: AbstractEventLoop = None
21
20
 
22
- def __init__(self, address="127.0.0.1", port=8000, key_value_split=b"-_-", end_identifier=b"@_@"):
21
+ def __init__(self, address="127.0.0.1", port=8000):
23
22
  self._address = address
24
23
  self._port = port
25
- self._key_value_split = key_value_split
26
- self._end_identifier = end_identifier
27
24
  self._logger = logging.getLogger(f"{self.__module__}.{self.__class__.__name__}")
28
25
  self._file_handler = None
29
26
  self.set_log()
30
- self.queue = asyncio.Queue()
31
27
 
32
28
  def set_log(self):
33
29
  """设置日志."""
@@ -60,68 +56,54 @@ class CygSocketServerAsyncio:
60
56
  """日志实例."""
61
57
  return self._logger
62
58
 
63
- async def consumer(self, operation_func: callable):
64
- """获取队列里客户端发来的数据进行处理.
59
+ def operations_return_data(self, data: bytes):
60
+ """操作返回数据."""
61
+ data = data.decode("UTF-8")
62
+ self._logger.warning("*** 回显 *** -> 没有重写 operations_return_data 函数, 默认是回显.")
63
+ return data
65
64
 
66
- Args:
67
- operation_func: 处理函数.
68
- """
69
- while True:
70
- while self.queue.qsize() != 0:
71
- data = await self.queue.get()
72
- operation_func(data)
73
- self.queue.task_done()
74
-
75
- async def socket_send(self, client_connection, data: Union[bytes, str]):
65
+ async def socket_send(self, client_connection, data: bytes):
76
66
  """发送数据给客户端."""
77
- if isinstance(data, str):
78
- data = data.encode("UTF-8")
79
67
  if client_connection:
80
68
  client_ip = client_connection.getpeername()
81
69
  await self.loop.sock_sendall(client_connection, data)
82
- self._logger.info("*** 发送 *** --> %s 发送成功, %s", client_ip, data)
70
+ self._logger.info("***发送*** --> %s 发送成功, %s", client_ip, data)
83
71
  else:
84
- self._logger.info("*** 发送 *** --> 发送失败, %s, 未连接", data)
72
+ self._logger.info("***发送*** --> 发送失败, %s, 未连接", data)
85
73
 
86
74
  async def receive_send(self, client_connection: socket.socket):
87
75
  """接收发送数据."""
88
76
  client_ip = client_connection.getpeername()[0] # 获取连接客户端的ip
89
- self._logger.info("%s 处理 %s 客户端的任务开始 %s", "-" * 30, client_ip, "-" * 30)
90
- buffer_byte = b""
91
77
  try:
92
- while data_byte := await self.loop.sock_recv(client_connection, 1024 * 1024):
93
- buffer_byte += data_byte
94
- if self._end_identifier in buffer_byte:
95
- await self.socket_send(client_connection, b"^_^")
96
- one_message_byte, remaining = buffer_byte.split(self._end_identifier, 1)
97
- buffer_byte = remaining
98
- keys, datas = one_message_byte.split(self._key_value_split, 1)
99
- await self.queue.put({keys: datas})
100
- self._logger.info("*** 将一条完整消息放入队列 ***")
101
- self._logger.info("keys: %s", keys)
102
- self._logger.info("datas: %s", datas[:129:])
78
+ while data := await self.loop.sock_recv(client_connection, 1024 * 1024):
79
+ self._logger.info("%s", '-' * 60)
80
+ self._logger.info("***Socket接收*** --> %s, 数据: %s", client_ip, data.decode('UTF-8'))
81
+ send_data = self.operations_return_data(data) # 这个方法实现具体业务, 需要重写, 不重写回显
82
+ send_data_byte = send_data.encode("UTF-8") + b"\r\n"
83
+ await self.loop.sock_sendall(client_connection, send_data_byte)
84
+ self._logger.info("***Socket回复*** --> %s, 数据: %s", client_ip, send_data)
85
+ self._logger.info("%s", '-' * 60)
103
86
  except Exception as e: # pylint: disable=W0718
104
- self._logger.warning("*** 通讯出现异常 *** --> 异常信息是: %s", e)
87
+ self._logger.warning("***通讯出现异常*** --> 异常信息是: %s", e)
105
88
  finally:
106
89
  self.clients.pop(client_ip)
107
90
  self.tasks.get(client_ip).cancel()
108
- self._logger.warning("*** 下位机断开 *** --> %s, 断开了", client_ip)
109
- self._logger.info("%s 处理 %s 客户端的任务结束 %s", "-" * 30, client_ip, "-" * 30)
91
+ self._logger.warning("***下位机断开*** --> %s, 断开了", client_ip)
110
92
  client_connection.close()
111
93
 
112
94
  async def listen_for_connection(self, socket_server: socket):
113
95
  """异步监听连接."""
114
- self._logger.info("*** 服务端已启动 *** --> %s 等待客户端连接", socket_server.getsockname())
96
+ self._logger.info("***服务端已启动*** --> %s 等待客户端连接", socket_server.getsockname())
115
97
 
116
98
  while True:
117
99
  self.loop = asyncio.get_running_loop()
118
100
  client_connection, address = await self.loop.sock_accept(socket_server)
119
- self._logger.warning("*** 下位机连接 *** --> %s, 连接了", address)
120
101
  client_connection.setblocking(False)
121
- await self.socket_send(client_connection, "^_^")
122
102
  self.clients.update({address[0]: client_connection})
123
- self.tasks.update({address[0]: self.loop.create_task(self.receive_send(client_connection))})
124
- self._logger.warning("*** 创建了处理 %s 客户端的任务 ***", address)
103
+ self.tasks.update({
104
+ address[0]: self.loop.create_task(self.receive_send(client_connection))
105
+ })
106
+ self._logger.warning("***下位机连接*** --> %s, 连接了", address)
125
107
 
126
108
  async def run_socket_server(self):
127
109
  """运行socket服务, 并监听客户端连接."""
@@ -1,43 +0,0 @@
1
- """client_example."""
2
- import os
3
-
4
- from socket_cyg.socket_client import SocketClient
5
-
6
-
7
- FILE_PATH = r"D:\python_workspace\equipment_cyg.rar"
8
-
9
-
10
- if __name__ == '__main__':
11
-
12
- client1 = SocketClient()
13
- client1.client_open()
14
- client1.run_receive_thread()
15
- with open(FILE_PATH, mode="rb+") as f:
16
- data = f.read()
17
- dir_path = f"{os.getcwd()}/a"
18
- client1.client_send(dir_path.encode("UTF-8") + b"-_-" + data + b"@__@")
19
-
20
- client2 = SocketClient()
21
- client2.client_open()
22
- client2.run_receive_thread()
23
- with open(FILE_PATH, mode="rb+") as f:
24
- data = f.read()
25
- dir_path = f"{os.getcwd()}/b"
26
- client2.client_send(dir_path.encode("UTF-8") + b"-_-" + data + b"@__@")
27
-
28
- client3 = SocketClient()
29
- client3.client_open()
30
- client3.run_receive_thread()
31
- with open(FILE_PATH, mode="rb+") as f:
32
- data = f.read()
33
- dir_path = f"{os.getcwd()}/c"
34
- client3.client_send(dir_path.encode("UTF-8") + b"-_-" + data + b"@__@")
35
-
36
- client4 = SocketClient()
37
- client4.client_open()
38
- client4.run_receive_thread()
39
- with open(FILE_PATH, mode="rb+") as f:
40
- data = f.read()
41
- dir_path = f"{os.getcwd()}/d"
42
- client4.client_send(dir_path.encode("UTF-8") + b"-_-" + data + b"@__@")
43
-
@@ -1,31 +0,0 @@
1
- """server_example."""
2
- import asyncio
3
- import os.path
4
- import threading
5
-
6
- from socket_cyg.socket_server_asyncio import CygSocketServerAsyncio
7
-
8
-
9
- def save(data):
10
- """保存文件的函数."""
11
- dir_path, datas = list(data.keys())[0], list(data.values())[0]
12
- dir_path = dir_path.decode("UTF-8")
13
- if not os.path.exists(dir_path):
14
- os.makedirs(dir_path)
15
- with open(f"{dir_path}/a.rar", "ab+") as f:
16
- f.write(datas)
17
-
18
-
19
- if __name__ == '__main__':
20
- server = CygSocketServerAsyncio(end_identifier=b"@__@")
21
-
22
- def run_server():
23
- """启动server."""
24
- asyncio.run(server.run_socket_server())
25
-
26
- def run_consumer():
27
- """启动消费队列."""
28
- asyncio.run(server.consumer(save))
29
-
30
- threading.Thread(target=run_server).start()
31
- threading.Thread(target=run_consumer).start()
@@ -1,125 +0,0 @@
1
- """socket 客户端."""
2
- import datetime
3
- import os
4
- import socket
5
- import sys
6
- import threading
7
- import logging
8
- from logging.handlers import TimedRotatingFileHandler
9
- from typing import Union
10
-
11
-
12
- class SocketClient:
13
- """socket 客户端class."""
14
- LOG_FORMAT = "%(asctime)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s"
15
-
16
-
17
- def __init__(self, host="127.0.0.1", port=8000):
18
- self._logger = logging.getLogger(f"{self.__module__}.{self.__class__.__name__}")
19
- self._file_handler = None
20
- self.set_log()
21
-
22
- self._host = host
23
- self._port = port
24
- self._client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
25
-
26
- def set_log(self):
27
- """设置日志."""
28
- self.file_handler.setFormatter(logging.Formatter(self.LOG_FORMAT))
29
- self.file_handler.setLevel(logging.INFO)
30
- self.logger.addHandler(self.file_handler)
31
- if sys.version_info.minor == 11:
32
- logging.basicConfig(level=logging.INFO, encoding="UTF-8", format=self.LOG_FORMAT)
33
- else:
34
- console_handler = logging.StreamHandler()
35
- console_handler.setFormatter(logging.Formatter(self.LOG_FORMAT))
36
- console_handler.setLevel(logging.INFO)
37
- self.logger.addHandler(console_handler)
38
- self.logger.setLevel(logging.INFO)
39
-
40
- @property
41
- def file_handler(self):
42
- """保存日志的日志处理器."""
43
- if self._file_handler is None:
44
- log_dir = f"{os.getcwd()}/log"
45
- os.makedirs(log_dir, exist_ok=True)
46
- file_name = f"{log_dir}/{datetime.datetime.now().strftime('%Y-%m-%d')}_{os.path.basename(os.getcwd())}.log"
47
- self._file_handler = TimedRotatingFileHandler(
48
- file_name, when="D", interval=1, backupCount=10, encoding="UTF-8"
49
- )
50
- return self._file_handler
51
-
52
- @property
53
- def logger(self):
54
- """日志器."""
55
- return self._logger
56
-
57
- @property
58
- def host(self):
59
- """服务端ip."""
60
- return self._host
61
-
62
- @host.setter
63
- def host(self, host):
64
- """设置连接的服务端ip."""
65
- self._host = host
66
-
67
- @property
68
- def port(self):
69
- """服务端端口号."""
70
- return self._port
71
-
72
- @port.setter
73
- def port(self, port):
74
- """设置要连接的服务端端口号."""
75
- self._port = port
76
-
77
- @property
78
- def client(self):
79
- """客户端socket实例."""
80
- return self._client
81
-
82
- @client.setter
83
- def client(self, client: socket):
84
- """设置客户端socket实例."""
85
- self._client = client
86
-
87
- def client_open(self):
88
- """连接服务端."""
89
- self.client.connect((self.host, self.port))
90
- self._logger.info("*** 和服务端连接成功 ***")
91
-
92
- def client_close(self):
93
- """关闭客户端连接."""
94
- self.client.close()
95
- self._logger.warning("*** 客户端关闭连接 ***")
96
-
97
- def client_send(self, message: Union[str, bytes]):
98
- """客户端发送数据."""
99
- if isinstance(message, str):
100
- message = message.encode("UTF-8")
101
- self.client.sendall(message)
102
- self._logger.info("*** 客户端发送数据 *** -> data: %s", message[:129:])
103
-
104
- def client_receive(self):
105
- """客户端接收数据."""
106
- try:
107
- while True:
108
- data = self.client.recv(1024)
109
- if not data:
110
- break
111
- str_data = data.decode("utf-8")
112
- self._logger.info("*** 客户端接收到服务端数据 *** -> data: %s", str_data)
113
- self.operations()
114
- except Exception as e: # pylint: disable=W0718
115
- self._logger.warning("*** 出现异常 *** -> 异常信息: %s", str(e))
116
- finally:
117
- self.client.close()
118
-
119
- def operations(self):
120
- """根据服务端发过来的数据进行的操作"""
121
-
122
- def run_receive_thread(self):
123
- """启动客户端线程, 实时监听服务端发来的数据."""
124
- thread = threading.Thread(target=self.client_receive, daemon=False)
125
- thread.start()
File without changes