套接字介绍:
1.套接字:实现网络编程进行数据传输的一种技术手段
2.Python实现套接字编程: import socket
3.套接字分类
1. 流式套接字(SOCK_STREAM):提供面向连接的、可靠的数据传输服务,通常用于TCP协议。(面向连接--tcp协议--可靠的--流式套接字)
2. 数据报套接字(SOCK_DGRAM):提供无连接的、不可靠的数据传输服务,通常用于UDP协议。(无连接--udp协议--不可靠--流式套接字)
3. 原始套接字(SOCK_RAW):允许直接访问网络层数据包,通常用于实现自定义的网络协议或进行网络数据包的捕获和分析。
4. 顺序数据包套接字(SOCK_SEQPACKET):提供面向连接的、可靠的数据传输服务,但保证数据的顺序传递。
5. 数据报文套接字(SOCK_RDM):提供面向连接的、可靠的数据传输服务,但不保证数据的顺序传递。
TCP套接字编程
服务端流程
socket --> bind --> listen --> accpet -->send/recv --> close
代码实现
import socket # 1 创建套接字 socketd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 共能:创建套接字 # 参数: socket_family 网络地址类型 AF_INET表示ipv4 # socket_type 套接字类型 SOCK_STREAM(流式) SOCK_DGRAM(数据报) # proto 选择子协议 # 返回值 套接字对象 # 2 绑定地址 # 本地地址: ‘localhost’, '127.0.0.1' # 网络地址: ‘172.x......’ # 自动获取地址: ‘0.0.0.0 addr = ('0.0.0.0', 8000) socketd.bind(addr) # 功能:绑定本机网络地址 # 参数:二元元组(IP,port) # 3 设置监听 n = 3 socketd.listen(n) # 功能: 将套接字设置为监听套接字,确认监听队列大小,可以连接多个客户端,但是连接的过程是一个一个连接的 # 参数: 监听队列大小 # 4 等待处理客户端连接请求 connect, add = socketd.accept() # 阻塞函数 # 功能: 阻塞等待处理客户端请求 # 返回值: connect 客户端连接套接字 # add 连接的客户端地址 # 5 收发消息 data = connect.recv(1024) # 功能: 接收客户端的消息 # 参数: 每次接收消息的最大的大小 # 返回值: 接收的内容,该返回值的类型是字节串(bytes格式) msg = 'Hello World!' n = connect.send(msg.encode()) # 功能: 发送消息 # 参数: 发送的内容 (bytes格式) # 返回值: 发送的字节数 # 6 关闭套接字 socketd.close() # 功能: 关闭套接字
(绑定地址可以参考上图 )
客户端流程
socket --> bind(可选) --> connect --> send/recv --> close
代码实现
import socket # 1 创建套接字 socketd = socket.socket() # 2 连接 addr = ('127.0.0.1', 8000) socketd.connect(addr) # 收发消息 data = 'Hello! what is your name?' socketd.send(data.encode()) msg = socketd.recv(1024) # 关闭套接字 socketd.close()
简单的聊天实现
服务端
import socket socketd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) addr = ('0.0.0.0', 8000) socketd.bind(addr) n = 3 socketd.listen(n) print('waiting for connect...') while True: connect, add = socketd.accept() # 阻塞函数,等待客户端连接 print('connect from {}'.format(add)) while True: data = connect.recv(1024) if not data: # r如果data为空的话,说明客户端已经退出 break print(data.decode()) msg = 'my name is zjc' n = connect.send(msg.encode()) connect.close() # 关闭该客户端的连接 print('waiting new connect...') # 等待新的客户端连接 socketd.close()
客户端
import socket # 1 创建套接字 socketd = socket.socket() # 2 连接 addr = ('127.0.0.1', 8000) socketd.connect(addr) while True: # 收发消息 data = input('请输入...') if not data: # 如果用户未输入,则判断用户已经退出 break socketd.send(data.encode()) # 发送消息 msg = socketd.recv(1024) # 接收消息 print(msg.decode()) # 关闭套接字 socketd.close()
收发缓冲区
概念
TCP套接字有两种缓冲区:发送缓冲区和接收缓冲区。
发送缓冲区:数据从应用程序发送到套接字时,首先被放入发送缓冲区。如果发送缓冲区已满,应用程序可能会被阻塞,直到有空间可用。发送缓冲区的大小可以通过操作系统的参数进行调整。
接收缓冲区:当套接字接收到数据时,数据首先被放入接收缓冲区。如果接收缓冲区已满,新的数据可能会被丢弃。接收缓冲区的大小同样可以通过操作系统的参数进行调整。
功能
1.网络缓冲区有效的协调了消息的收发速度
2.send和recv实际上是向缓冲区发送接收消息,当缓冲区不为空时recv就不会阻塞。
TCP粘包
概念
TCP粘包是指发送方发送的若干包数据到接收方时,接收方可能会将多个数据包粘合在一起,导致接收方无法正确解析每个数据包的边界。这可能会导致接收方无法正确处理数据,从而产生错误。
原因
1. TCP是面向流的协议,发送的数据被看作是一个字节流,而不是一个个独立的数据包。因此,在接收端无法直接得知发送端发送的数据包的边界。
2. 网络传输时,数据包的大小可能会受到MTU(最大传输单元)的限制,导致大的数据包被分割成多个小的数据包,而接收端可能会一次性接收到多个小的数据包。
解决方法
1. 使用固定长度的消息。发送端在每个数据包的末尾添加固定长度的填充数据,接收端按照固定长度来切分数据包。
2. 使用特殊的分隔符。发送端在每个数据包的末尾添加特殊的分隔符,接收端根据分隔符来切分数据包。
3. 使用消息头来表示消息的长度。发送端在每个数据包的开头添加消息长度信息,接收端先读取消息长度,然后按照消息长度来接收完整的数据包。
接收发送文件练习
服务端
import socket sockedf = socket.socket() sockedf.bind(('0.0.0.0', 8000)) sockedf.listen(3) while True: # 等待客户端连接 print('Waiting for connect...') connect, add = sockedf.accept() f = open('gg.jpg', 'wb') while True: data = connect.recv(1024) # 接收消息 if not data: break f.write(data) connect.close() f.close() sockedf.close()
客户端
import socket sockedf = socket.socket() sockedf.connect(('127.0.0.1',8000)) f = open('ojbk.jpg', 'rb') while True: data = f.read(1024) if not data: break sockedf.send(data) f.close() sockedf.close()