当前位置: 首页 > news >正文

网络编程底层通信(socket)

文章目录

  • 一、socket函数介绍
  • 二、TCP/IP服务端/客户端
  • 三、UDP/IP服务端/客户端
  • 四、多线程服务器(threading)
  • 五、网络编程常见问题(地址复用、粘包、数据长度)

网络编程

指通过计算机网络实现程序间通信的技术。Python提供了丰富的库支持各种网络协议和编程模式

套接字

是网络通信的基本操作单元,是应用层与TCP/IP协议族通信的中间软件抽象层。它提供了一组接口,允许不同主机或同一主机的不同进程之间进行通信。分为面向连接套接字(TCP)和无连接套接字(UDP)

面向连接套接字(传输控制协议TCP)

在进行通信之前,先建立一个连接,该连接的通信是序列化的、可靠的、不重复的数据交付,意味着每条信息可以拆分成多个片段,并且每一条消息片段都能确保能够到达目的地,然后按顺序组合起来(SOCK_STREAM 流套接字之一)

无连接套接字(用户数据报协议UDP)

在通讯之前无需建立连接,数据传输无法保证它的顺序性、可靠性、重复性,信息是以整体发送的,而并非分成多个片段(SOCK_DGRAM)

一、socket函数介绍

socket(套接字)

是网络通信的端点,是应用层与传输层之间的接口。它允许不同主机或同一主机的不同进程之间进行通信

socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, fileno=None)

family/type:参数见下表
proto(通常省略):协议号,通常为0,表示使用默认协议 socket.IPPROTO_TCP:6 socket.IPPROTO_UDP:17 socket.IPPROTO_ICMP :1
fileno:文件描述符,可选参数,如果指定,将从指定的文件描述符创建一个套接字对象

通常使用如下简洁方法创建

TCP/IP套接字:socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
UDP/IP套接字:socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)

family参数描述常见用途
socket.AF_INETIPv4网络协议 (默认值)大多数互联网应用
socket.AF_INET6IPv6网络协议新一代互联网应用
socket.AF_UNIXUnix域套接字(本地通信)同一台主机上的进程间通信
type参数协议特点适用场景
socket.SOCK_STREAM (流式)TCP可靠、面向连接、按序到达Web、文件传输、数据库连接
socket.SOCK_DGRAM (数据报)UDP不可靠、无连接、可能丢失或乱序视频流、DNS查询、在线游戏
socket.SOCK_RAW (原始)ICMP等底层协议直接访问底层协议如IP、ICMP网络探测、协议开发

socket.socket()会返回一个套接字对象,该套接字对象常用方法如下

函数描述
服务器方法
bind()将地址(主机名、端口号)绑定到套接字上
listen()设置并启动TCP监听器
accept()被动接收TCP客户端连接,也一直等待直到连接到达(阻塞)
客户端方法
connect()主动发起TCP服务器连接
connect_ex()connect()的扩散版本,此时会以错误码的形式放回问题,二不是抛出异常
通用方法
recv()接收TCP信息
recv_into()接收TCP信息到指定缓冲区
send()发送TCP信息
sendall()完整的发生TCP信息
recvfrom()接收UDP信息
recvfrom_into()接收UDP信息到指定缓冲区
sendto()发送UDP信息
getpeername()连接到TCP套接字的远程地址
getsockname()当前套接字地址
getsockopt()返回给定套接字选项的值
setsockopt()设置给定套接字的值
shutdown()关闭连接
close()关闭套接字
detach()在未关闭文件描述符的情况下关闭套接字,返回文件描述符
ioctl()控制套接字的模式(仅支持Windows)
面向阻塞的方法
setblocking()设置套接字的阻塞或非阻塞模式
settimeout()设置阻塞套接字操作的超时时间
gettimeout()获取阻塞套接字操作的超时时间
面向文件的方法
fileno()套接字的文件描述符
makefile()创建与套接字关联的文件对象
数据属性
family套接字家族
type套接字类型
proto套接字协议

二、TCP/IP服务端/客户端

TCP (传输控制协议) 是一种面向连接的、可靠的、基于字节流的传输层通信协议。具有特点如下:

面向连接:通信前需要建立连接
可靠传输:保证数据顺序和完整性
全双工通信:双方可以同时发送和接收数据
流量控制:防止发送方过快导致接收方来不及处理

服务端

import socketdef tcp_server():# 1. 创建TCP socket(服务器套接字)server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 2. 设置地址重用(可选,即关闭后可重新启动)server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 3. 绑定地址和端口server_socket.bind(('0.0.0.0', 8888))# 4. 开始监听,设置最大等待连接数(注:是等待连接数,并非最大连接数)server_socket.listen(5)print("TCP服务器启动,等待连接...")try:while True:# 5. 接受客户端连接client_socket, addr = server_socket.accept()print(f"客户端 {addr} 已连接")try:while True:# 6. 接收数据(1024表示接收数据的缓冲区大小)data = client_socket.recv(1024)if not data:  # 客户端关闭连接breakprint(f"收到消息: {data.decode('utf-8')}")# 7. 发送响应response = "服务端已收到消息"client_socket.send(response.encode('utf-8'))except ConnectionResetError:print("客户端异常断开")finally:# 8. 关闭客户端连接client_socket.close()print(f"客户端 {addr} 已断开")except KeyboardInterrupt:print("服务器正在关闭...")finally:# 9. 关闭服务器socketserver_socket.close()if __name__ == '__main__':tcp_server()

客户端

import socketdef tcp_client():# 1. 创建TCP socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)try:# 2. 连接服务器client_socket.connect(('127.0.0.1', 8888))print("已连接到服务器")while True:# 3. 获取用户输入message = input("请输入消息(输入quit退出): ")if message.lower() == 'quit':break# 4. 发送数据client_socket.send(message.encode('utf-8'))# 5. 接收响应response = client_socket.recv(1024)print(f"服务器响应: {response.decode('utf-8')}")except ConnectionRefusedError:print("无法连接到服务器")except Exception as e:print(f"发生错误: {e}")finally:# 6. 关闭连接client_socket.close()print("连接已关闭")if __name__ == '__main__':tcp_client()

对于需要长时间保持连接的网络,并需要持续首发信息的情况,上方代码结构是完全不够的,上方只是一个简单的举例

三、UDP/IP服务端/客户端

UDP/IP 是互联网协议套件中的核心组合之一,由 UDP(用户数据报协议) 和 IP(网际协议) 共同构成。它是一种无连接的、轻量级的传输层协议,适用于对实时性要求高但允许少量数据丢失的场景

无连接:通信前无需建立连接,直接发送数据。
不可靠:不保证数据包的顺序、完整性或可达性(无重传机制)。
高效:头部开销小(仅8字节),传输延迟低。
支持广播/多播:可同时向多个目标发送数据

服务端

import socketdef udp_server():# 1. 创建UDP socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 2. 设置地址重用(可选,即关闭后可重新启动)server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 3. 绑定地址和端口server_socket.bind(('127.0.0.1', 8888))print("UDP服务器启动,等待消息...")try:while True:# 4. 接收数据和客户端地址data, addr = server_socket.recvfrom(1024)print(f"收到来自 {addr} 的消息: {data.decode('utf-8')}")# 5. 发送响应response = "UDP消息已收到"server_socket.sendto(response.encode('utf-8'), addr)except KeyboardInterrupt:print("服务器正在关闭...")finally:# 6. 关闭socketserver_socket.close()if __name__ == '__main__':udp_server()

客户端

import socketdef udp_client():# 1. 创建UDP socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)server_address = ('127.0.0.1', 8888)try:while True:# 2. 获取用户输入message = input("请输入消息(输入quit退出): ")if message.lower() == 'quit':break# 3. 发送数据client_socket.sendto(message.encode('utf-8'), server_address)# 4. 接收响应response, _ = client_socket.recvfrom(1024)print(f"服务器响应: {response.decode('utf-8')}")except Exception as e:print(f"发生错误: {e}")finally:# 5. 关闭socketclient_socket.close()print("客户端已关闭")if __name__ == '__main__':udp_client()

四、多线程服务器(threading)

多线程服务器是一种常见的并发服务器模型,它通过 Socket 实现网络通信,并利用 Threading(线程) 处理多个客户端请求,提高服务器的并发能力。

多线程服务器通常由以下部分组成:

主线程(Main Thread):负责监听客户端连接(accept())。
工作线程(Worker Thread):每个客户端连接由一个独立线程处理(recv(), send())。

工作流程如下

  1. 服务器启动,绑定IP和端口(bind()),并监听(listen())。
  2. 客户端发起连接(connect()),服务器接受连接(accept())。
  3. 为每个客户端创建一个新线程,处理该客户端的请求。
  4. 线程完成任务后关闭连接(close()),线程终止。

多线程服务端

import socket
import threadingdef handle_client(client_socket, addr):try:while True:data = client_socket.recv(1024)if not data:breakprint(f"来自 {addr} 的消息: {data.decode('utf-8')}")client_socket.send("已收到".encode('utf-8'i))except Exception as e:print(f"处理 {addr} 时出错: {e}")finally:client_socket.close()print(f"{addr} 已断开")def multi_thread_server():server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server.bind(('127.0.0.1', 8888))server.listen(5)print("多线程服务器启动...")try:while True:client, addr = server.accept()print(f"新连接: {addr}")thread = threading.Thread(target=handle_client, args=(client, addr))thread.start()finally:server.close()if __name__ == '__main__':multi_thread_server()

同理客户端复杂的时候,如果需要持续收发信息,客户端可能也需要创建一个线程持续接收服务端的数据

五、网络编程常见问题(地址复用、粘包、数据长度)

地址占用

启动服务器后,接着关闭在次重启时,可能会出现地址占用问题,即服务端无法启动,需要换个地址才能使用,创建套接字时使用setsockopt方法设置地址复用即可解决

server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

数据粘包问题(TCP粘包)

TCP是流式协议,数据无明确边界,多次发送可能被合并接收。可以采取如下方式解决
固定长度:每次发送固定长度的信息(效率低)
分隔符:用特殊字符分割信息(如\n
消息头+长度:发送信息前先传长度

# 客户端(在末尾添加\n分隔符)
message = "hello\n"
client_socket.send(message.encode('utf-8'))# 服务端(收到消息后按\n分隔后一项一项分别处理)
rece_message = data.decode('utf-8')
for message in rece_message.split("\n"):print(message)

数据长度问题

在发送消息过长或多次接收的信息合并到一起,导致信息长度超过接收缓冲区的长度时,可能会导致信息无法正常处理,这时需要对信息接收模块进行处理

# 服务端接收信息处理(客户端同理)
data = client_socket.recv(1024)
whilelen(data) % 1024 != 0:data += client_socket.recv(1024)
http://www.lryc.cn/news/581443.html

相关文章:

  • 人工智能安全基础复习用:隐私保护
  • 力扣网编程45题:跳跃游戏II之正向查找方法(中等)
  • 群晖(Synology)存储ext4视频文件删除的恢复方法
  • 基于Pandas和FineBI的昆明职位数据分析与可视化实现(五) - 基于随机森林算法预测职位分类
  • MySQL主从复制与读写分离概述
  • 【AI大模型】Spring AI 基于mysql实现对话持久存储详解
  • Neo4j 综合练习作业
  • 7,TCP服务器
  • 卫星通信终端天线的5种对星模式之一:信标跟踪
  • mysql的JDBC和连接池
  • 如何正确规范的开发术语自己的TYPECHO插件
  • 【CSS样式】有趣的滑块开关
  • Gin Web 服务集成 Consul:从服务注册到服务发现实践指南(下)
  • 【influxdb3】如何使用 SQL 对时间序列数据进行聚合查询
  • CppCon 2018 学习:Woes of Scope Guards and Unique_Resource
  • Redis存储Cookie实现爬虫保持登录 requests | selenium
  • RK3588 源码编译 opencv
  • Java 大视界 -- Java 大数据在智能教育在线课程学习效果影响因素分析与优化设计(334)
  • Web后端开发-SpringBootWeb入门、Http协议、Tomcat
  • Spring Boot + 本地部署大模型实现:优化与性能提升!
  • Docker相关内容
  • 闲庭信步使用图像验证平台加速FPGA的开发:开篇语——跨越软件和硬件开发的鸿沟
  • string类(详解)
  • Linux关机指令详解:shutdown命令的使用指南
  • SpringAI与智能体入门
  • 成为git砖家(12): 看懂git合并分支时冲突提示符
  • Linux操作系统之文件(四):文件系统(上)
  • PADS交互式布局
  • PageRank:互联网的马尔可夫链平衡态
  • 线程锁和线程同步