Python-TCP服务端程序开发

Posted 礁之

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python-TCP服务端程序开发相关的知识,希望对你有一定的参考价值。

文章目录

一. TCP服务端程序开发

"""
主动套接字: 可以收发信息的套接字
被动套接字: 不能收发信息

TCP服务端的流程:
    1. 创建socket对象
    2. 绑定, 固定服务器的ip和端口
    3. 设置监听, 将主动套接字变成被动套接字, 可以接收新的客户端连接,
    4. 阻塞等待客户端的连接, 有客户端连接, 会返回一个新的socket, 用来和客户端通信, 原始socket会监听新的创建的socket
    5. 新的socket接收信息
    6. 新的socket发送信息
    7. 新的socket关闭, 表示不能通信的 监听的socket 关闭了, 不能接收新的客户端连接
    注意: 其中 5,6 步骤是可以重复执行的
"""
import socket


if __name__ == '__main__':
    # 1. 创建socket对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 绑定, 固定服务器的ip和端口    socket对象.bind((IP地址, 元组)) 注意是元组
    # server_socket.bind(("192.168.218.1", 8355))
    server_socket.bind(("", 8355))   # 收不到的话可能是端口被占用了

    # 3. 设置监听, 将主动套接字变成被动套接字, 可以接收新的客户端连接    socket对象.listen(同时连接服务器的最大数量)    连接成功之后就不会占用连接名额
    server_socket.listen(128)
    print("服务等待连接中......")

    # 4. 阻塞等待客户端的连接, 有客户端连接, 会返回一个新的socket, 用来和客户端通信, 原始socket会监听新的创建的socket
    # socket对象.accept()   返回一个元组, (新的socket, (客户端的ip, 端口))  注意是两层元组
    new_socket, ip_port = server_socket.accept()   # 拆分赋值变量
    print(f"客户端ip_port 已经连接了......")

    # 5. 新的socket接收信息, 使用新的socket进行接收
    buf = new_socket.recv(4096)
    print("接收到的信息为", buf.decode("gbk"))

    # 6. 新的socket发送信息
    send_data = "信息已经收到......".encode("gbk")
    new_socket.send(send_data)

    # 7. 新的socket关闭, 表示不能通信的 监听的socket 关闭了, 不能接收新的客户端连接, 创建的socket也关闭
    new_socket.close()
    server_socket.close()

二. 端口复用

"""
问题: 在进行连接后立即执行, 会报错显示端口已经被使用
正常情况下, 服务器的代码是永远不会关闭的, 就不会出现整个问题
但是特殊情况下, 启动服务器后, 运行一次, 马上关闭再次运行, 就会出现整个bug, 端口必须要等待 30s - 2min后才能使用

解决方案:
1. 换个端口(显然不太现实, 因为客户端也需要不停的换端口)
2. 代码解决, 代码实现,让程序关闭之后, 可以立即使用整个端口, 即 端口复用
"""
import socket


if __name__ == '__main__':
    # 1. 创建socket对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 设置端口复用
    # level 设置哪个级别的 socket,   socket.SOL_SOCKET 表示当前socket
    # optname 设置什么内容(权限)    socket.SO_REUSEADDR 端口复用
    # value 设置为什么值   True
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

    # 2. 绑定, 固定服务器的ip和端口    socket对象.bind((IP地址, 元组)) 注意是元组
    # server_socket.bind(("192.168.218.1", 8355))
    server_socket.bind(("", 8355))   # 收不到的话可能是端口被占用了

    # 3. 设置监听, 将主动套接字变成被动套接字, 可以接收新的客户端连接    socket对象.listen(同时连接服务器的最大数量)    连接成功之后就不会占用连接名额
    server_socket.listen(128)
    print("服务等待连接中......")

    # 4. 阻塞等待客户端的连接, 有客户端连接, 会返回一个新的socket, 用来和客户端通信, 原始socket会监听新的创建的socket
    # socket对象.accept()   返回一个元组, (新的socket, (客户端的ip, 端口))  注意是两层元组
    new_socket, ip_port = server_socket.accept()   # 拆分赋值变量
    print(f"客户端ip_port 已经连接了......")

    # 5. 新的socket接收信息, 使用新的socket进行接收
    buf = new_socket.recv(4096)
    print("接收到的信息为", buf.decode("gbk"))

    # 6. 新的socket发送信息
    send_data = "信息已经收到......".encode("gbk")
    new_socket.send(send_data)

    # 7. 新的socket关闭, 表示不能通信的 监听的socket 关闭了, 不能接收新的客户端连接, 创建的socket也关闭
    new_socket.close()
    server_socket.close()

三. 判断客户端程序是否断开

"""
问题: 在客户端没有发送消息的情况下直接断开, 后续的代码还会继续执行

解决方案:
判断客户端程序是否已经断开
"""
import socket


if __name__ == '__main__':
    # 1. 创建socket对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 设置端口复用
    # level 设置哪个级别的 socket,   socket.SOL_SOCKET 表示当前socket
    # optname 设置什么内容(权限)    socket.SO_REUSEADDR 端口复用
    # value 设置为什么值   True
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

    # 2. 绑定, 固定服务器的ip和端口    socket对象.bind((IP地址, 元组)) 注意是元组
    # server_socket.bind(("192.168.218.1", 8355))
    server_socket.bind(("", 8355))   # 收不到的话可能是端口被占用了

    # 3. 设置监听, 将主动套接字变成被动套接字, 可以接收新的客户端连接    socket对象.listen(同时连接服务器的最大数量)    连接成功之后就不会占用连接名额
    server_socket.listen(128)
    print("服务等待连接中......")

    # 4. 阻塞等待客户端的连接, 有客户端连接, 会返回一个新的socket, 用来和客户端通信, 原始socket会监听新的创建的socket
    # socket对象.accept()   返回一个元组, (新的socket, (客户端的ip, 端口))  注意是两层元组
    new_socket, ip_port = server_socket.accept()   # 拆分赋值变量
    print(f"客户端ip_port 已经连接了......")

    # 5. 新的socket接收信息, 使用新的socket进行接收
    # 当对方的 socket close断开 后,自己的 socket 不再进行阻塞, recv接收的内容是空的, 长度为0
    buf = new_socket.recv(4096)
    if buf:
        print("接收到的信息为", buf.decode("gbk"))

        # 6. 新的socket发送信息
        send_data = "信息已经收到......".encode("gbk")
        new_socket.send(send_data)
    else:
        print(f"客户端ip_port已经断开连接......")

    # 7. 新的socket关闭, 表示不能通信的 监听的socket 关闭了, 不能接收新的客户端连接, 创建的socket也关闭
    new_socket.close()
    server_socket.close()

四. 多任务版本

"""
在客户端连接后会创建新的socket
"""
import socket
import threading


def handle_client_request(n_socket, client_ip_port):
    # 5. 新的socket接收信息, 使用新的socket进行接收
    # 当对方的 socket close断开 后,自己的 socket 不再进行阻塞, recv接收的内容是空的, 长度为0
    while True:  # 这里也需要循环
        buf = n_socket.recv(4096)
        if buf:
            print(f"接收到client_ip_port的信息为", buf.decode("gbk"))

            # 6. 新的socket发送信息
            send_data = "信息已经收到......".encode("gbk")
            n_socket.send(send_data)
        else:
            print(f"客户端client_ip_port已经断开连接......")
            break  # 关闭后跳出循环, 关闭socket
    # 7. 新的socket关闭, 表示不能通信的 监听的socket 关闭了, 不能接收新的客户端连接, 创建的socket也关闭
    n_socket.close()


if __name__ == '__main__':
    # 1. 创建socket对象
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 设置端口复用
    # level 设置哪个级别的 socket,   socket.SOL_SOCKET 表示当前socket
    # optname 设置什么内容(权限)    socket.SO_REUSEADDR 端口复用
    # value 设置为什么值   True
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

    # 2. 绑定, 固定服务器的ip和端口    socket对象.bind((IP地址, 元组)) 注意是元组
    # server_socket.bind(("192.168.218.1", 8355))
    server_socket.bind(("", 8355))  # 收不到的话可能是端口被占用了

    # 3. 设置监听, 将主动套接字变成被动套接字, 可以接收新的客户端连接    socket对象.listen(同时连接服务器的最大数量)    连接成功之后就不会占用连接名额
    server_socket.listen(128)
    print("服务等待连接中......")

    while True:  # 循环等待客户端的消息
        # 4. 阻塞等待客户端的连接, 有客户端连接, 会返回一个新的socket, 用来和客户端通信, 原始socket会监听新的创建的socket
        # socket对象.accept()   返回一个元组, (新的socket, (客户端的ip, 端口))  注意是两层元组
        new_socket, ip_port = server_socket.accept()  # 拆分赋值变量
        print(f"客户端ip_port 已经连接了......")

        # 创建线程, 执行任务
        sub_thread = threading.Thread(target=handle_client_request, args=(new_socket, ip_port))

        # 启动线程
        sub_thread.start()

    server_socket.close()  # 关闭监听的 socket 注意不要写在循环里面

以上是关于Python-TCP服务端程序开发的主要内容,如果未能解决你的问题,请参考以下文章

python-TCP传输模型

Python-TCP网络编程基础以及客户端程序开发

TCP/IP中的传输层协议TCPUDP

LWIP网络开发 | TCP/IP协议簇二

TCP 进阶

Linux下的TCP/IP编程----进程及多进程服务端