Python网络编程(二)编写TCP协议程序

简介: Python网络编程(二)编写TCP协议程序



TCP客户端程序开发

1. 开发 TCP 客户端程序开发步骤回顾

  1. 创建客户端套接字对象
  2. 和服务端套接字建立连接
  3. 发送数据
  4. 接收数据
  5. 关闭客户端套接字

2. socket 类的介绍

导入 socket 模块 import socket

创建客户端 socket 对象 socket.socket(AddressFamily, Type)

参数说明:

  • AddressFamily 表示IP地址类型, 分为TPv4和IPv6
  • Type 表示传输协议类型

方法说明:

  • connect((host, port)) 表示和服务端套接字建立连接, host是服务器ip地址,port是应用程序的端口号
  • send(data) 表示发送数据,data是二进制数据
  • recv(buffersize) 表示接收数据, buffersize是每次接收数据的长度

3. TCP 客户端程序开发示例代码

import socket
if __name__ == '__main__':
    # 创建tcp客户端套接字
    # 1. AF_INET:表示ipv4
    # 2. SOCK_STREAM: tcp传输协议
    tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 和服务端应用程序建立连接
    tcp_client_socket.connect(("192.168.131.62", 8080))
    # 代码执行到此,说明连接建立成功
    # 准备发送的数据
    send_data = "你好服务端,我是客户端小黑!".encode("gbk")
    # 发送数据
    tcp_client_socket.send(send_data)
    # 接收数据, 这次接收的数据最大字节数是1024
    recv_data = tcp_client_socket.recv(1024)
    # 返回的直接是服务端程序发送的二进制数据
    print(recv_data)
    # 对数据进行解码
    recv_content = recv_data.decode("gbk")
    print("接收服务端的数据为:", recv_content)
    # 关闭套接字
    tcp_client_socket.close()

执行结果:

b'hello'
接收服务端的数据为: hello

说明

  1. str.encode(编码格式) 表示把字符串编码成为二进制
  2. data.decode(编码格式) 表示把二进制解码成为字符串

网络调试助手充当服务端程序:

TCP服务端程序开发

1. 开发 TCP 服务端程序开发步骤回顾

  1. 创建服务端端套接字对象
  2. 绑定端口号
  3. 设置监听
  4. 等待接受客户端的连接请求
  5. 接收数据
  6. 发送数据
  7. 关闭套接字

2. socket 类的介绍

导入 socket 模块

import socket

创建服务端 socket 对象

socket.socket(AddressFamily, Type)

参数说明:

  • AddressFamily 表示IP地址类型, 分为TPv4和IPv6
  • Type 表示传输协议类型

方法说明:

  • bind((host, port)) 表示绑定端口号, host 是 ip 地址,port 是端口号,ip 地址一般不指定,表示本机的任何一个ip地址都可以。
  • listen (backlog) 表示设置监听,backlog参数表示最大等待建立连接的个数。
  • accept() 表示等待接受客户端的连接请求
  • send(data) 表示发送数据,data 是二进制数据
  • recv(buffersize) 表示接收数据, buffersize 是每次接收数据的长度

3. TCP 服务端程序开发示例代码

import socket
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(("", 8989))
    # 设置监听
    # 128:最大等待建立连接的个数, 提示: 目前是单任务的服务端,同一时刻只能服务与一个客户端,后续使用多任务能够让服务端同时服务与多个客户端,
    # 不需要让客户端进行等待建立连接
    # listen后的这个套接字只负责接收客户端连接请求,不能收发消息,收发消息使用返回的这个新套接字来完成
    tcp_server_socket.listen(128)
    # 等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞,代码才能继续往下执行
    # 1. 专门和客户端通信的套接字: service_client_socket
    # 2. 客户端的ip地址和端口号: ip_port
    service_client_socket, ip_port = tcp_server_socket.accept()
    # 代码执行到此说明连接建立成功
    print("客户端的ip地址和端口号:", ip_port)
    # 接收客户端发送的数据, 这次接收数据的最大字节数是1024
    recv_data = service_client_socket.recv(1024)
    # 获取数据的长度
    recv_data_length = len(recv_data)
    print("接收数据的长度为:", recv_data_length)
    # 对二进制数据进行解码
    recv_content = recv_data.decode("gbk")
    print("接收客户端的数据为:", recv_content)
    # 准备发送的数据
    send_data = "ok, 问题正在处理中...".encode("gbk")
    # 发送数据给客户端
    service_client_socket.send(send_data)
    # 关闭服务与客户端的套接字, 终止和客户端通信的服务
    service_client_socket.close()
    # 关闭服务端的套接字, 终止和客户端提供建立连接请求的服务
    tcp_server_socket.close()

执行结果:

客户端的ip地址和端口号: ('172.16.47.209', 52472)
接收数据的长度为: 5
接收客户端的数据为: hello

说明:

当客户端和服务端建立连接后,服务端程序退出后端口号不会立即释放,需要等待大概1-2分钟。

解决办法有两种:

  1. 更换服务端端口号
  2. 设置端口号复用(推荐大家使用),也就是说让服务端程序退出后端口号立即释放。

设置端口号复用的代码如下:

# 参数1: 表示当前套接字
# 参数2: 设置端口号复用选项
# 参数3: 设置端口号复用选项对应的值
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

网络调试助手充当客户端程序:

TCP网络应用程序的注意点

  1. 当 TCP 客户端程序想要和 TCP 服务端程序进行通信的时候必须要先建立连接
  2. TCP 客户端程序一般不需要绑定端口号,因为客户端是主动发起建立连接的。
  3. TCP 服务端程序必须绑定端口号,否则客户端找不到这个 TCP 服务端程序。
  4. listen 后的套接字是被动套接字,只负责接收新的客户端的连接请求,不能收发消息。
  5. 当 TCP 客户端程序和 TCP 服务端程序连接成功后, TCP 服务器端程序会产生一个新的套接字,收发客户端消息使用该套接字。
  6. 关闭 accept 返回的套接字意味着和这个客户端已经通信完毕
  7. 关闭 listen 后的套接字意味着服务端的套接字关闭了,会导致新的客户端不能连接服务端,但是之前已经接成功的客户端还能正常通信。
  8. 当客户端的套接字调用 close 后,服务器端的 recv 会解阻塞,返回的数据长度为0,服务端可以通过返回数据的长度来判断客户端是否已经下线,反之服务端关闭套接字,客户端的 recv 也会解阻塞,返回的数据长度也为0

案例:多任务版TCP服务端程序开发

1. 需求

目前我们开发的TCP服务端程序只能服务于一个客户端,如何开发一个多任务版的TCP服务端程序能够服务于多个客户端呢?

完成多任务,可以使用线程,比进程更加节省内存资源。

2. 具体实现步骤

  1. 编写一个TCP服务端程序,循环等待接受客户端的连接请求
  2. 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞
  3. 把创建的子线程设置成为守护主线程,防止主线程无法退出。

3. 多任务版TCP服务端程序的示例代码:

import socket
import threading
# 处理客户端的请求操作
def handle_client_request(service_client_socket, ip_port):
    # 循环接收客户端发送的数据
    while True:
        # 接收客户端发送的数据
        recv_data = service_client_socket.recv(1024)
        # 容器类型判断是否有数据可以直接使用if语句进行判断,如果容器类型里面有数据表示条件成立,否则条件失败
        # 容器类型: 列表、字典、元组、字符串、set、range、二进制数据
        if recv_data:
            print(recv_data.decode("gbk"), ip_port)
            # 回复
            service_client_socket.send("ok,问题正在处理中...".encode("gbk"))
        else:
            print("客户端下线了:", ip_port)
            break
    # 终止和客户端进行通信
    service_client_socket.close()
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(("", 9090))
    # 设置监听, listen后的套接字是被动套接字,只负责接收客户端的连接请求
    tcp_server_socket.listen(128)
    # 循环等待接收客户端的连接请求
    while True:
        # 等待接收客户端的连接请求
        service_client_socket, ip_port = tcp_server_socket.accept()
        print("客户端连接成功:", ip_port)
        # 当客户端和服务端建立连接成功以后,需要创建一个子线程,不同子线程负责接收不同客户端的消息
        sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port))
        # 设置守护主线程
        sub_thread.setDaemon(True)
        # 启动子线程
        sub_thread.start()
    # tcp服务端套接字可以不需要关闭,因为服务端程序需要一直运行
    # tcp_server_socket.close()

执行结果:

客户端连接成功: ('172.16.47.209', 51528)
客户端连接成功: ('172.16.47.209', 51714)
hello1 ('172.16.47.209', 51528)
hello2 ('172.16.47.209', 51714)

socket的send和recv原理剖析

1. 认识TCP socket的发送和接收缓冲区

当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区这个发送和接收缓冲区指的就是内存中的一片空间。

2. send原理剖析

send是不是直接把数据发给服务端?

不是,要想发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的,它需要调用操作系统接口,也就是说,应用程序把发送的数据先写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡

3. recv原理剖析

recv是不是直接从客户端接收数据?

不是,应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区(内存中的一片空间),应用程序再从接收缓存区获取客户端发送的数据

4. send和recv原理剖析图

说明:

  • 发送数据是发送到发送缓冲区
  • 接收数据是从接收缓冲区 获取
目录
相关文章
|
3月前
|
机器学习/深度学习 算法 安全
【PSO-LSTM】基于PSO优化LSTM网络的电力负荷预测(Python代码实现)
【PSO-LSTM】基于PSO优化LSTM网络的电力负荷预测(Python代码实现)
195 0
|
3月前
|
人工智能 Linux 开发工具
Python从零到一:手把手带你写出第一个实用程序
Python语法简洁易懂,适合编程新手入门。它广泛应用于人工智能、自动化办公、Web开发等领域。学习Python可快速搭建项目,拥有丰富库支持和强大社区资源。通过本教程,你将掌握基础语法、环境搭建、程序逻辑控制及实战项目开发,开启编程之旅。
348 0
|
26天前
|
机器学习/深度学习 大数据 关系型数据库
基于python大数据的青少年网络使用情况分析及预测系统
本研究基于Python大数据技术,构建青少年网络行为分析系统,旨在破解现有防沉迷模式下用户画像模糊、预警滞后等难题。通过整合多平台亿级数据,运用机器学习实现精准行为预测与实时干预,推动数字治理向“数据驱动”转型,为家庭、学校及政府提供科学决策支持,助力青少年健康上网。
|
1月前
|
监控 负载均衡 安全
WebSocket网络编程深度实践:从协议原理到生产级应用
蒋星熠Jaxonic,技术宇宙中的星际旅人,以代码为舟、算法为帆,探索实时通信的无限可能。本文深入解析WebSocket协议原理、工程实践与架构设计,涵盖握手机制、心跳保活、集群部署、安全防护等核心内容,结合代码示例与架构图,助你构建稳定高效的实时应用,在二进制星河中谱写极客诗篇。
WebSocket网络编程深度实践:从协议原理到生产级应用
|
2月前
|
设计模式 决策智能 Python
Python条件控制:让程序学会"思考"的魔法
本文深入浅出地讲解Python条件控制,从基础if语句到多分支、嵌套结构,再到简洁的三元表达式与Python 3.10新增的match-case模式匹配,结合电商折扣、会员等级、ATM系统等实战案例,全面掌握程序“智能决策”的核心逻辑。
340 0
|
2月前
|
机器学习/深度学习 算法 PyTorch
【Pytorch框架搭建神经网络】基于DQN算法、优先级采样的DQN算法、DQN + 人工势场的避障控制研究(Python代码实现)
【Pytorch框架搭建神经网络】基于DQN算法、优先级采样的DQN算法、DQN + 人工势场的避障控制研究(Python代码实现)
|
2月前
|
机器学习/深度学习 算法 PyTorch
【DQN实现避障控制】使用Pytorch框架搭建神经网络,基于DQN算法、优先级采样的DQN算法、DQN + 人工势场实现避障控制研究(Matlab、Python实现)
【DQN实现避障控制】使用Pytorch框架搭建神经网络,基于DQN算法、优先级采样的DQN算法、DQN + 人工势场实现避障控制研究(Matlab、Python实现)
131 0
|
3月前
|
机器学习/深度学习 算法 调度
基于遗传算法GA算法优化BP神经网络(Python代码实现)
基于遗传算法GA算法优化BP神经网络(Python代码实现)
250 0
|
3月前
|
机器学习/深度学习 数据采集 TensorFlow
基于CNN-GRU-Attention混合神经网络的负荷预测方法(Python代码实现)
基于CNN-GRU-Attention混合神经网络的负荷预测方法(Python代码实现)
132 0

推荐镜像

更多