1、网络编程基础

1. socket之send和recv的原理剖析

  1. 当创建一个Tcp Socket对象时,会有一个发送缓冲区与一个接收缓冲区,这个发送缓冲区与接收缓冲区指的是内存中的一片空间。
  2. send原理:应用程序把发送的数据先写入到发送缓冲区,再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡。
  3. recv原理:应用程序调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区,应用程序再从接收缓冲区获取客户端发送的数据。

2. 端口和端口号

  1. 端口号有65536个。
  2. 应用程序通过ip地址找到设备,通过端口号找到对应的端口,然后通过端口把数据传输给应用程序。
  3. 端口号可以标识唯一的一个端口。
  4. 端口号的分类
    1. 知名端口号(众所周知的端口号,范围:0~1023)
    2. 动态端口号(开发应用程序使用的端口号,范围:1024~65535)

3. tcp

  1. tcp是一个稳定、可靠的传输协议,常用于对数据进行准确无误的传输。
  2. 特点:面向连接、可靠传输

4. socket

  1. 作用:负责进程之间的网络数据传输。

5. tcp网络应用程序的开发流程

  1. 客户端开发流程
    创建客户端套接字对象
    和服务端套接字建立连接
    发送数据
    接收数据
    关闭客户端套接字
  2. 服务端开发流程
    创建服务端套接字对象
    绑定端口号
    设置监听
    等待接收客户端的连接请求
    接收数据
    发送数据
    关闭套接字

6. tcp客户端程序示例

  1. 示例
    import socket
    
    if __name__ == '__main__':
        # 创建tcp客户端套接字
        # AF_INET:ipv4地址类型
        # SOCK_STREAM:tcp传输协议类型
        tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 和服务端套接字建立连接
        tcp_client_socket.connect("192.168.132.128", 8080)
        # 发送数据到服务器
        send_content = "hello"
        # 对字符串进行编码成为二进制数据
        send_data = send_content.encode("utf-8")
        tcp_client_socket.send(send_data)
        # 接收服务端的数据
        # 1024表示每次接收的最大字节数
        recv_data = tcp_client_socket.recv(1024)
        recv_content = recv_data.decode("utf-8")
        print("Recieved data: ", recv_content)
        # 关闭套接字
        tcp_client_socket.close()
    

7. tcp服务端程序示例

  1. 示例
    import socket
    
    if __name__ == '__main__':
        # 创建tcp服务端套接字
        # AF_INET:ipv4,AF_INET6:ipv6
        tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置端口号复用
        # SOL_SOCKET:表示当前套接字
        # SO_REUSEADDR:表示复用端口号的选项
        # True:确定复用
        tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 绑定端口号
        # 第一个参数表示ip地址,一般不用指定,表示本机的任何一个ip即可
        # 第二个参数表示端口号
        tcp_server_socket.bind(("", 9090))
        # 设置监听
        # 128:表示最大等待建立连接的个数
        tcp_server_socket.listen(128)
        # 等待接受客户端的连接请求
        # 每次当客户端和服务端建立连接成功都会返回一个新的套接字
        new_client, ip_port = tcp_server_socket.accept()
        print("Client's ip and port: ", ip_port)
        # 接收客户端的数据
        recv_data = new_client.recv(1024)
        recv_content = recv_data.decode("utf-8")
        print("Recieved Content: ", recv_content)
        # 发送数据到客户端
        send_content = "......"
        send_data = send_content.encode("utf-8")
        new_client.send(send_data)
        # 关闭套接字
        new_client.close()
        tcp_server_socket.close()
    

8. 端口号复用

  1. 当客户端和服务端建立连接后,服务端程序退出后端口后不会立即释放,需要等待大概1~2分钟。
  2. 解决方法:
    1. 更换服务端端口号
    2. 设置端口号复用(推荐),让服务端程序退出后,端口号立即释放。
  3. 示例
    参照【7. tcp服务端程序示例】

9. tcp网络应用程序的注意点

  1. 当客户端的套接字调用close后,服务器端的recv会解阻塞,返回的数据长度为0,服务端可以根据返回的数据长度来判断客户端是否已经下线,反之服务端关闭套接字,客户端的recv也会解阻塞,返回的数据长度也为0。

10. tcp服务端服务于多个客户端示例

  1. 示例
    import socket
    
    if __name__ == '__main__':
        # 创建tcp服务端套接字
        # AF_INET:ipv4,AF_INET6:ipv6
        tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置端口号复用
        # SOL_SOCKET:表示当前套接字
        # SO_REUSEADDR:表示复用端口号的选项
        # True:确定复用
        tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 绑定端口号
        # 第一个参数表示ip地址,一般不用指定,表示本机的任何一个ip即可
        # 第二个参数表示端口号
        tcp_server_socket.bind(("", 9090))
        # 设置监听
        # 128:表示最大等待建立连接的个数
        tcp_server_socket.listen(128)
        # 循环等待接受客户端的连接请求
        while:
            # 等待接受客户端的连接请求
            # 每次当客户端和服务端建立连接成功都会返回一个新的套接字
            new_client, ip_port = tcp_server_socket.accept()
            print("Client's ip and port: ", ip_port)
            # 接收客户端的数据
            recv_data = new_client.recv(1024)
            recv_content = recv_data.decode("utf-8")
            print("Recieved Content: ", recv_content)
            # 发送数据到客户端
            send_content = "......"
            send_data = send_content.encode("utf-8")
            new_client.send(send_data)
            # 关闭套接字
            new_client.close()
        tcp_server_socket.close()
    

11. 多人版tcp服务端示例

  1. 示例
    import socket
    import threading
    
    
    def handle_client_request(ip_port, new_client):
        print("Client's ip and port: ", ip_port)
    
        # 循环接收客户端信息
        while:
            # 接收客户端的数据
            recv_data = new_client.recv(1024)
            # 客户端断开连接判断
            if recv_data:
                recv_content = recv_data.decode("utf-8")
                print("Recieved Content: ", recv_content, ip_port)
                # 发送数据到客户端
                send_content = "......"
                send_data = send_content.encode("utf-8")
                new_client.send(send_data)
            else:
                break
    
        # 关闭套接字
        new_client.close()
    
    
    if __name__ == '__main__':
        # 创建tcp服务端套接字
        # AF_INET:ipv4,AF_INET6:ipv6
        tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置端口号复用
        # SOL_SOCKET:表示当前套接字
        # SO_REUSEADDR:表示复用端口号的选项
        # True:确定复用
        tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 绑定端口号
        # 第一个参数表示ip地址,一般不用指定,表示本机的任何一个ip即可
        # 第二个参数表示端口号
        tcp_server_socket.bind(("", 9090))
        # 设置监听
        # 128:表示最大等待建立连接的个数
        tcp_server_socket.listen(128)
        # 循环等待接受客户端的连接请求
        while:
            # 等待接受客户端的连接请求
            # 每次当客户端和服务端建立连接成功都会返回一个新的套接字
            new_client, ip_port = tcp_server_socket.accept()
            # 当客户端和服务端建立连接成功,创建子线程,让子线程专门负责接收客户端的消息
            sub_thread = threading.Thread(target=handle_client_request, args=(ip_port, new_client))
            # 设置守护主线程,主线程退出,子线程直接销毁
            sub_thread.setDaemon(True)
            # 启动子线程执行对应的任务
            sub_thread.start()
    
        tcp_server_socket.close()
    
发布了13 篇原创文章 · 获赞 2 · 访问量 810

猜你喜欢

转载自blog.csdn.net/ccblogs/article/details/104638245