网络模块socket
实现本地收发
-
简单的尴聊,实现服务器和客户端的文字传输
TCP协议情况
#server.py import socket #导入socket模块 from socket import SOL_SOCKET, SO_REUSEADDR #记住这个模块 sk = socket.socket() #实例化socket对象 sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) #这行代码是为了实现端口继续利用的 sk.bind(('127.0.0.1', 8080)) #为sk对象绑定一个ip和一个端口 sk.listen() #启动监听 conn, addr = sk.accept() #等待一个客户端的连接 while 1: ret = conn.recv(1024).decode('utf-8') #等待客户端发送消息 if ret == 'bye': break print(ret) info = input('反馈: ') conn.send(bytes(info, encoding='utf-8')) #向客户端发动消息 conn.close() sk.close() ___________________________________________________ ___________________________________________________ #client import socket sk = socket.socket() sk.connect(('127.0.0.1', 8080)) while 1: info = input('输入: ') sk.send(bytes(info, encoding='utf-8')) if info == 'bye': break ret = sk.recv(1024).decode('utf-8') print(ret) sk.close()
改进版,为聊天消息加入时间
#server import socket from socket import SOL_SOCKET, SO_REUSEADDR from time import ctime, time sk = socket.socket() sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sk.bind(('127.0.0.1', 8080)) sk.listen() conn, addr = sk.accept() while 1: t = ctime(float(conn.recv(1024).decode('utf-8'))) ret = conn.recv(1024).decode('utf-8') if ret == 'bye': break print('{}收到{}'.format(t, ret)) info = input('反馈: ') conn.send(bytes(info, encoding='utf-8')) conn.send(bytes(str(time()), encoding='utf-8')) conn.close() sk.close() ---------------------------------------------- ---------------------------------------------- #client import socket from time import time, ctime sk = socket.socket() sk.connect(('127.0.0.1', 8080)) while 1: info = input('输入: ') sk.send(bytes(str(time()), encoding='utf-8')) sk.send(bytes(info, encoding='utf-8')) if info == 'bye': break ret = sk.recv(1024).decode('utf-8') t = ctime(float(sk.recv(1024).decode('utf-8'))) print(t, ret) sk.close()
- TCP协议下只能允许一个客户端链接,多个客户端连接需要排队,等待第一个连接断开才能接入连接
__UDP协议__情况
#server #创建一个UDP协议的服务器 import socket sk = socket.socket(type=socket.SOCK_DGRAM) #实例化一个socket对象 sk.bind(('127.0.0.1', 8081)) #给服务器绑定一个ip和端口 conn, addr = sk.recvfrom(1024) #接受消息,接收到消息内容和对方机器地址 print(conn.decode('utf-8'), addr) sk.sendto(b'bye', addr) sk.close() -------------------------------- -------------------------------- #client #创建一个UDP客户端 import socket sk = socket.socket(type=socket.SOCK_DGRAM) #创建socket对象 ip_port = ('127.0.0.1', 8081) sk.sendto(b'hello', ip_port) conn, addr = sk.recvfrom(1024) print(conn.decode('utf-8'), addr) sk.close()
黏包现象
-
出现在TCP协议中,如果一个包在一次发送中没有完全接收将会在下一次接收时继续接收
-
可以理解成管道拥堵,再次接收会获取管道中的信息
-
在UDP协议中没有黏包现象,没有接收完毕的包将直接丢弃
-
黏包涉及到TCP协议自带的拆包机制,一种优化文件传输的机制
黏包出现的两种情况
- 数据包太长,服务器接收参数小于数据包的长度
- 两个数据包的发送时间太近,神奇的某算法将其整合成一个数据包发送
解决黏包的方案
-
每次发送信息之前,先向服务器反馈信息的长度,告知服务器即将接收数据的长度
#server import socket from socket import SOL_SOCKET, SO_REUSEADDR sk = socket.socket() sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sk.bind(('127.0.0.1', 8080)) sk.listen() conn, addr = sk.accept() #服务器发送命令 cmd = input('输入需要发送的命令: ') conn.send(bytes(cmd, encoding='utf-8')) print('命令已经发送!') #接收命令反馈信息的长度 num = int(conn.recv(10).decode('utf-8')) print('接收到长度,长度是{}'.format(num)) #反馈给客户端已经收到长度信息 conn.send(b'Roger that') #接收命令反馈信息 msg = conn.recv(num).decode('utf-8') print('接收到信息') #打印命令返回信息 print(msg) #反馈信息给客户端 conn.send(bytes('服务器说:接收完毕', encoding='utf-8')) #关闭服务 conn.close() sk.close() ----------------------------- ----------------------------- #client import socket import subprocess import time sk = socket.socket() sk.connect(('127.0.0.1', 8080)) #接收服务器命令 cmd = sk.recv(1024).decode('utf-8') print(cmd) #将命令传给控制台 ret = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) print('控制台已经接收到命令') #将控制台传回的信息长度反馈给服务器 std_out = ret.stdout.read() std_err = ret.stderr.read() num = len(std_out) + len(std_err) sk.send(bytes(str(num), encoding='utf-8')) print('控制台输出信息的长度已经反馈') #询问服务器收到长度了不 has_len = sk.recv(1024) print(has_len.decode('utf-8')) #将控制台传回的信息内容反馈给服务器 sk.send(std_out) sk.send(std_err) print('控制台输出的信息内容已经反馈') #接收服务器反馈的信息 info = sk.recv(1024).decode('utf-8') print(info) #关闭客户端 sk.close()