20240809-python实现TCP通信

marverdol / 2024-08-10 / 原文

python实现TCP通讯

1.0 版本(备份)
import socket
from logUtils import log
from api import Api


def handle_client(client_socket, addr):
    log.info(f"客户端的ip地址和端口号: {addr}")
    try:
        while True:
            # 接收客户端发送的数据, 这次接收数据的最大字节数是1024
            recv_data = client_socket.recv(1024)
            if not recv_data:
                # 如果没有接收到数据,说明客户端可能已经关闭了连接
                break
                # 对二进制数据进行解码
            recv_content = recv_data #.decode("utf-8")
            log.info(f'-----------------------------------------begin-------------------------------------')
            log.info(f"接收客户端的数据为: {recv_content}")
            api = Api()
            send_data = api.Message_parsing(recv_data)
            # 准备发送的数据
            if send_data:
                print(f'要发送的数据:{send_data}')
                # send_data = b'h\x19\x00hK\x01\x00T\x07\x00\x02`\x00\x01\x00cE\x86A:3\x12\nXQ\x18\x10q!\x06{\x16'
                # 发送数据给客户端
                client_socket.send(send_data)
                print('发送成功!')
            log.info(f'-----------------------------------------end-------------------------------------')
    except Exception as e:
        print(f"处理客户端时发生错误: {e}")
        log.info(f'-----------------------------------------error-------------------------------------')
    finally:
        # 关闭与客户端的套接字
        client_socket.close()
        print(f"与客户端 {addr} 的连接已关闭")


if __name__ == '__main__':
    # 创建tcp服务端套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置端口号复用,让程序退出端口号立即释放
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 给程序绑定端口号
    tcp_server_socket.bind(("0.0.0.0", 6999))
    # 设置监听
    tcp_server_socket.listen(128)
    log.info("服务器开始监听端口号: 6999")

    try:
        while True:
            # 等待客户端建立连接的请求
            service_client_socket, ip_port = tcp_server_socket.accept()
            log.info(f"客户端的IP地址和端口号: {ip_port}")

            # 在单独的线程或进程中处理客户端连接,这里简化为直接调用处理函数
            handle_client(service_client_socket, ip_port)


    except KeyboardInterrupt:
        print("服务器接收到停止信号,正在关闭...")
    finally:
        # 关闭服务端的套接字(通常在实际应用中,这一步会在所有客户端连接都关闭后执行,但在这里我们直接响应中断信号)
        tcp_server_socket.close()

我想把他修改成可以服务端主动发报文的,进行了这样的操作:

import socket
from logUtils import log
from api import Api


def handle_client(client_socket, addr):
    log.info(f"客户端的ip地址和端口号: {addr}")
    # status = False
    try:
        while True:
            api = Api()
            # if status:
            #     print(f'04:\n20:插座启停')
            #     number = input("请输入你要调用的接口编号:")
            #
            #     avtive_send_data , statu = api.get_active_send_data(str(number))
            #     status = statu
            #     if avtive_send_data:
            #         print(f'要发送的数据:{avtive_send_data}')
            #         # send_data = b'h\x19\x00hK\x01\x00T\x07\x00\x02`\x00\x01\x00cE\x86A:3\x12\nXQ\x18\x10q!\x06{\x16'
            #         # 发送数据给客户端
            #         client_socket.send(avtive_send_data)
            #         print('发送成功!')

            # 接收客户端发送的数据, 这次接收数据的最大字节数是1024
            recv_data = client_socket.recv(1024)
            if not recv_data:
                # 如果没有接收到数据,说明客户端可能已经关闭了连接
                break
                # 对二进制数据进行解码
            recv_content = recv_data #.decode("utf-8")
            log.info(f'-----------------------------------------begin-------------------------------------')
            log.info(f"接收客户端的数据为: {recv_content}")

            send_data = api.Message_parsing(recv_data)
            # 准备发送的数据
            if send_data:
                log.info(f'要发送的数据:{send_data}')
                print(f'要发送的数据:{send_data}')
                # send_data = b'h\x19\x00hK\x01\x00T\x07\x00\x02`\x00\x01\x00cE\x86A:3\x12\nXQ\x18\x10q!\x06{\x16'
                # 发送数据给客户端
                client_socket.send(send_data)
                log.info('发送成功!')
                print('发送成功!')
            log.info(f'-----------------------------------------end-------------------------------------')
    except Exception as e:
        log.info(f"处理客户端时发生错误: {e}")
        print(f"处理客户端时发生错误: {e}")
        log.info(f'-----------------------------------------error-------------------------------------')
    finally:
        # 关闭与客户端的套接字
        client_socket.close()
        print(f"与客户端 {addr} 的连接已关闭")


if __name__ == '__main__':
    # 创建tcp服务端套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置端口号复用,让程序退出端口号立即释放
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 给程序绑定端口号
    tcp_server_socket.bind(("0.0.0.0", 6999))
    # 设置监听
    tcp_server_socket.listen(128)
    log.info("服务器开始监听端口号: 6999")

    try:
        while True:
            # 等待客户端建立连接的请求
            service_client_socket, ip_port = tcp_server_socket.accept()
            log.info(f"客户端的IP地址和端口号: {ip_port}")

            # 在单独的线程或进程中处理客户端连接,这里简化为直接调用处理函数
            handle_client(service_client_socket, ip_port)


    except KeyboardInterrupt:
        print("服务器接收到停止信号,正在关闭...")
    finally:
        # 关闭服务端的套接字(通常在实际应用中,这一步会在所有客户端连接都关闭后执行,但在这里我们直接响应中断信号)
        tcp_server_socket.close()

具体就是增加了一个状态变量,如果心跳了,就把状态设置成true,状态为true时,每次发送、接收报文都先判断一下是否需要主动发送。目前原理上感觉是可行的,我也写了一个测试的服务端-客户端,测试结果:可行。

但是这样子,在api.py里增加返回状态时,出现了报错。

具体是SIM卡上报信息是报错了:

cannot unpack non-iterable NoneType object

这个报错是对一个NoneType进行了数据操作,我找了半天也没找到SIM卡接口里有什么数据操作。

最后恢复一下代码,又能运行了。我再一步一步看看,到底是改了哪里出现了错误。

排查错误:
import socket
from logUtils import log
from api import Api


def handle_client(client_socket, addr):
    log.info(f"客户端的ip地址和端口号: {addr}")
    status = False
    try:
        while True:
            api = Api()
            if status:
                print(f'04:\n20:插座启停')
                number = input("请输入你要调用的接口编号:")

                avtive_send_data , statu = api.get_active_send_data(str(number))
                status = statu
                if avtive_send_data:
                    print(f'要发送的数据:{avtive_send_data}')
                    # send_data = b'h\x19\x00hK\x01\x00T\x07\x00\x02`\x00\x01\x00cE\x86A:3\x12\nXQ\x18\x10q!\x06{\x16'
                    # 发送数据给客户端
                    client_socket.send(avtive_send_data)
                    print('发送成功!')

            # 接收客户端发送的数据, 这次接收数据的最大字节数是1024
            recv_data = client_socket.recv(1024)
            if not recv_data:
                # 如果没有接收到数据,说明客户端可能已经关闭了连接
                break
                # 对二进制数据进行解码
            recv_content = recv_data #.decode("utf-8")
            log.info(f'-----------------------------------------begin-------------------------------------')
            log.info(f"接收客户端的数据为: {recv_content}")

            send_data = api.Message_parsing(recv_data)
            # 准备发送的数据
            if send_data:
                log.info(f'要发送的数据:{send_data}')
                print(f'要发送的数据:{send_data}')
                # send_data = b'h\x19\x00hK\x01\x00T\x07\x00\x02`\x00\x01\x00cE\x86A:3\x12\nXQ\x18\x10q!\x06{\x16'
                # 发送数据给客户端
                client_socket.send(send_data)
                log.info('发送成功!')
                print('发送成功!')
            log.info(f'-----------------------------------------end-------------------------------------')
    except Exception as e:
        log.info(f"处理客户端时发生错误: {e}")
        print(f"处理客户端时发生错误: {e}")
        log.info(f'-----------------------------------------error-------------------------------------')
    finally:
        # 关闭与客户端的套接字
        client_socket.close()
        print(f"与客户端 {addr} 的连接已关闭")


if __name__ == '__main__':
    # 创建tcp服务端套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置端口号复用,让程序退出端口号立即释放
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 给程序绑定端口号
    tcp_server_socket.bind(("0.0.0.0", 6999))
    # 设置监听
    tcp_server_socket.listen(128)
    log.info("服务器开始监听端口号: 6999")

    try:
        while True:
            # 等待客户端建立连接的请求
            service_client_socket, ip_port = tcp_server_socket.accept()
            log.info(f"客户端的IP地址和端口号: {ip_port}")

            # 在单独的线程或进程中处理客户端连接,这里简化为直接调用处理函数
            handle_client(service_client_socket, ip_port)


    except KeyboardInterrupt:
        print("服务器接收到停止信号,正在关闭...")
    finally:
        # 关闭服务端的套接字(通常在实际应用中,这一步会在所有客户端连接都关闭后执行,但在这里我们直接响应中断信号)
        tcp_server_socket.close()

把注释加回来,可以心跳。

func = Function_mapping[AFN].get(Fn)
log.info(f"接口匹配成功!")
data , status = func(dict_data)
log.info(f'type:{type(data)}')
return data, status

在接收数据这里加上status后,连登录都报错了。有两种报错

  1. non-hexadecimal number found in fromhex() arg at position 63

  2. too many values to unpack (expected 2):

    这通常意味着你尝试将一个可迭代对象(如列表、元组等)解包到比该对象实际包含的元素数量更少的变量中。a, b = (1, 2, 3) # 这里会引发错误

意料之中,因为我还没修改函数返回值。

#先返回False,返回True后就会直接让输入接口编号了
return byte_resp_data, False

send_data , status = api.Message_parsing(recv_data)
# 准备发送的数据
if send_data:
	log.info(f'要发送的数据:{send_data}')
	print(f'要发送的数据:{send_data}')

	# 发送数据给客户端
	client_socket.send(send_data)
    log.info('发送成功!')
    print('发送成功!')

把登录和接收的数据修改后,可以成功登录了!

所以登录后的登录验证开始报错了,接下来把登录验证修改一下

return byte_resp_data, True

果然,修改后能成功进行登录验证了。

接下来试试能不能请求主动发送的接口。

试了一下,能发出去,但是收到的信息很。。。乱,SIM后面跟了两条心跳。