Socket编程
对于Python而言,进行Socket编程,发送网络消息,可以使用Python内置的Socket库;目前使用最多的是利用TCP协议来进行网络通行的
利用TCP进行通信的双方,称之为服务端(severt)与客户端(client),TCP协议双方通信之前需要建立连接,然后才能进行信息的传递(三次握手)
编码流程:
"""服务器端:
1.实例化创建Socket对象
2.绑定本地IP地址与端口号
3.监听
4.模拟三次握手建立连接过程,返回通信Socket对象
5.进行信息的接受与发送
6.关闭连接"""
"""客户端端:
1.实例化创建Socket对象
2.与服务端建立连接(需要预先知道服务器端的IP地址与端口号)
3.进行信息的发送与接受
4.关闭连接"""
传送消息的格式为字符串
服务器端代码:
#服务端
#导入socket库
from socket import *
#定义配置参数
#本地IP地址
IP="0.0.0.0"
#本地端口号
PORT=50000
#定义一次性从SOCKET缓冲区中读取最大字节数
BUFFERMAX=512
#创建Socket对象
"""参数说明:
参数AF_INET表明该对象网络层使用IP协议
参数SOCK_STREAM表明该对象传输层使用TCP协议"""
listenSocket=socket(AF_INET,SOCK_STREAM)
#给当前SOCKET绑定地址和端口号
listenSocket.bind((IP,PORT))
#使socket进程处于监听状态,等待客户端的连接请求
#参数表明最多可以接受等待连接的客户端数目,如4表明当前最多可以接受4个客户端的请求
listenSocket.listen(4)
print("服务端启动成功,在端口号{PORT}等待客户端的连接")
"""返回参数说明:
dataSocket:新的Socket对象,用于信息传递
addr:以元组的形式返回客户端的(IP地址,端口号)
"""
dataSocket,addr=listenSocket.accept() #建立连接,三次握手过程
print("接受一个客户端连接",addr)
while True:
#读取客户端发送的信息
#参数说明:BUFFERMAX 指定从接受缓冲区中读取的最大字节数
recved=dataSocket.recv(BUFFERMAX) #一直等待对方信息,当对方关闭连接时,返回的是一个空的字节
#返回数据recved为空Byte,表明对方关闭连接,退出当前循环
if not recved:
break
#读取的数据为Bytes类型,需要解码为字符串类型
info=recved.decode()
print(f"接收到的信息为:{info}")
#发送的数据必须要为Bytes类型,需要编码
dataSocket.send(f"服务端收到的信息为: {info}".encode())
#通信结束,关闭Socket
dataSocket.close()
listenSocket.close()
客户端代码:
#客户端代码
#导入Socket库
from socket import *
#定义配置参数
IP="127.0.0.1" #服务器IP地址
SEVERT_PORT=50000 #服务器端口号
BUFFERMAX=1024 #可接收最大缓冲区个数
#创建Socket对象
dataSocket=socket(AF_INET,SOCK_STREAM)
#与服务端建立连接
dataSocket.connect((IP,SEVERT_PORT)) #模拟三次握手过程
while True:
#用户通过中断输入发送信息
tosend=input(">> ") #INPUT输入的内容为字符串形式
#发送信息为空,退出循环
if tosend=="":
break
#发送消息为Bytes类型,所以需要编码
dataSocket.send(tosend.encode())
#等待接收服务端的消息
recved=dataSocket.recv(BUFFERMAX)
#如果返回为空Bytes,表明对方一=已关闭了连接,退出循环
if not recved:
break
#打印读取的信息
print(f"客户端收到的消息为:{recved.decode()}")
#关闭连接
dataSocket.close()
应用消息格式
TCP协议进行数据通信的时候,可能不会接受完整消息,可能发送的消息也不完整
格式定义的消息
消息头
消息头存放消息的格式数据,比如消息的长度,类型,状态等
消息体
存放具体的传送数据
使用TCP协议传输信息的程序来说,格式定义一定要明确规定消息的边界
TCP协议传输的是字节流,如果消息中没有指定边界或者长度,接收方就不知道一个完整的消息从字节流的哪里开始到哪里结束
指定消息的边界有两种方式:
- 用特殊字节作为消息的结尾符号
注意:特殊字节在消息内容中不可出现
- 在消息开头的某个位置,直接指定消息的长度
UDP协议通常不需要指定消息边界,因为UDP是数据包协议,应用程序从SOCKET接受到的必定是发送方发送的完整消息
传送文件
发送文件时,以二进制比特流发送,发送文件正文内容时,发送方应该告诉接收方文件的相关信息(文件名,文件大小),然后再向接受方发送文件的正文内容
客户端代码
#coding=utf-8
import socket
import tqdm #模拟进度条
import os
#传输数据分隔符
SEPERATOR="<SEPERATOR>"
#服务器属性
IP="127.0.0.1"
PORT=50000
#文件缓冲区的大小
BUFFER_SIZE=256
#传输文件名字
file_name="触控键盘.pdf"
#文件大小
file_size=os.path.getsize(file_name) #返回文件大小
#创建SOCKET连接
#默认UDP连接
s=socket.socket()
#连接服务器
print(f"服务器连接中{IP}:{PORT}")
s.connect((IP,PORT))
print(f"服务器连接成功")
#发送文件名字和文件大小,必须进行编码处理,因为传递为数据流
s.send(f"{file_name}{SEPERATOR}{file_size}".encode()) #文件名,分隔符,文件大小
#文件体(数据内容)传输
#添加进度条
progress=tqdm.tqdm(range(file_size),f"发送{file_name}",unit="B",unit_divisor=1024)
#打开文件,以二进制读取发送
with open(file_name,"rb") as f:
for _ in progress: #进度条
#读取文件
bytes_read=f.read(BUFFER_SIZE)
#如果读完,退出
if not bytes_read:
break
s.sendall(bytes_read) #sendall:确保即使网络忙碌,数据仍可传输
progress.update(len(bytes_read))
#关闭资源
s.close()
服务器端代码
#coding=utf-8
import socket
import os
#进度条
import tqdm
#设置服务器配置信息
IP="0.0.0.0"
PORT=50000
#设置缓冲区大小
BUFFER_SIZE=256
#传输数据分隔符
SEPERATOR="<SEPERATOR>"
#创建SOCKET
s=socket.socket()
#绑定端口号
s.bind((IP,PORT))
#设置连接监听最大数量
s.listen(5)
print(f"服务器监听{IP}:{PORT}")
#模拟连接过程
data_socket,addr=s.accept() #data_socket:连接成功,用于数据传输新的socket,addr:建立连接的客户端地址信息
#打印客户端信息
print(f"建立连接的客户端为:{addr}")
#接受信息
receved=data_socket.recv(BUFFER_SIZE).decode() #注意解码
#获取文件名和文件大小
file_name,file_size=receved.split(SEPERATOR) #用分隔符分隔开
#从完整路径中提取文件名
file_name="new"+os.path.basename(file_name)
#获取文件大小(接受的为字符串类型)
file_size=int(file_size)
#文件接受处理
progress=tqdm.tqdm(range(file_size),f"接受{file_name}",unit="B",
unit_divisor=1024,unit_scale=True)
#读取文件正文内容
with open(file_name,"wb") as f:
for _ in progress:
#从客户端读取数据
bytes_read=data_socket.recv(BUFFER_SIZE)
#如果没有数据,退出
if not bytes_read:
break
#写入文件
f.write(bytes_read)
#更新进度条
progress.update(len(bytes_read))
#关闭资源
data_socket.close()
s.close()