网络编程笔记(2)——socket长连接、黏包问题、subprocess模块
内容目录:
- tcp协议的socket长连接
- 基于udp协议的socket连接
- 黏包问题
- subprocess模块
内容详细
1.tcp协议的socket长连接
- 长连接只能保持和一个client端连接,第一个断开连接后才能和后面的一个客户端连接
#Server端
import socket
sk = socket.socket() #拿到socket的句柄
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind(('127.0.0.1',8080))
sk.listen() #监听是否有通信
while True:
conn,addr = sk.accept() #接收到client:连接:connection,地址:address
print(addr)
while True:
ret = conn.recv(1024).decode('utf-8') #接收到1024个字节,如果更改,必须为1024的倍数
print(ret)
if ret == 'bye':
break
info = input(">>>:")
if info == 'bye':
conn.send(b'bye')
break
conn.send(bytes(info,encoding='utf-8')) #和client传信息,必须传一个byte类型
conn.close() #连接断开
sk.close()
#Client端--第一个客户端
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8080))
while True:
info = input('client2>>>:')
if info == 'bye':
sk.send(b'bye')
break
sk.send(bytes(info.encode('utf-8')))
ret = sk.recv(1024).decode('utf-8')
print(ret)
if ret == 'bye':
sk.send(b'bye')
break
sk.close()
#多个client端--代码如第一个客户端
2.基于udp协议的socket连接
-
UDP的server 不需要进行监听也不需要建立连接
-
在启动服务之后只能被动的等待客户端发送信息过来
-
客户端发送消息的同时还会自带地址信息
-
消息回复的时候,不仅需要发送消息,还需要把目标的地址带上
#Srever端 import socket sk = socket.socket(type=socket.SOCK_DGRAM) #DGRAM datagram基于UDP协议 sk.bind(('127.0.0.1',8080)) msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) sk.sendto(b'bye',addr) sk.close() #Client端 import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8080) sk.sendto(b'hello',ip_port) ret,addr = sk.recvfrom(1024) print(ret.decode('utf-8')) sk.close()
-
同时多个客户端聊天
#Server: import socket sk = socket.socket(type=socket.SOCK_DGRAM) #DGRAM datagram基于UDP协议 sk.bind(('127.0.0.1',8080)) while True: msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) if msg == 'bye': break info = input('Server:') info = ('\033[31mServer:%s\033[0m'%info).encode('utf-8') sk.sendto(info,addr) sk.close() #Client1: import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8080) while True: info = input('Client1:') info = ('\033[32m来自Client1的消息:%s\033[0m'%info).encode('utf-8') sk.sendto(info,ip_port) ret,addr = sk.recvfrom(1024) print(ret.decode('utf-8')) if ret == 'bye': break sk.close() #Client: import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',8080) while True: info = input('Client2:') info = ('\033[34m来自Client2的消息:%s\033[0m'%info).encode('utf-8') sk.sendto(info,ip_port) ret,addr = sk.recvfrom(1024) print(ret.decode('utf-8')) if ret == 'bye': break sk.close()
3.黏包问题
-
使用TCP协议连接时:
- 会出现黏包现象:信息过长,接收的不完整,如果没接完上一条信息则执行下一命令时会继续打印上一条信息
- 不会丢包,信息可靠
-
使用UDP协议连接时:
- 信息接收不可靠,如果信息过长,则执行完命令后,多余信息直接抛弃
- 会丢包
-
所有的客户端执行server端下发的指令
-
将所有的执行结果反馈回来,我来接收
#例子:设置时间服务器 # server端: # 需求 # 写一个时间同步的服务器 # 服务端接收请求 # 按照client端发送的时间格式,将服务器时间转换成对应格式 # 发送给客户端 import time import socket sk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(('127.0.0.1',9000)) while True: msg,addr = sk.recvfrom(1024) # msg 客户端发送给server端的时间格式 "%Y-%m-%d %H:%M-%S" time_format = msg.decode('utf-8') time_str = time.strftime(time_format) sk.sendto(time_str.encode('utf-8'),addr) sk.close() #Client端: # client端每隔一段时间发送请求到服务端 # 发送时间的格式 import time import socket sk = socket.socket(type = socket.SOCK_DGRAM) sk.sendto('%Y-%m-%d %H:%M:%S'.encode('utf-8'),('127.0.0.1',9000)) msg,addr = sk.recvfrom(1024) print(msg.decode('utf-8')) sk.close() # 方式一 # 操作系统的定时任务 + python代码的形式 # 方式二 # while True + time.sleep的形式
4.subprocess模块
-
读取系统命令的模块
#Client端: import socket import subprocess sk = socket.socket() sk.connect(('127.0.0.1',8080)) cmd = sk.recv(1024).decode('gbk') #因为Windows传输都为GBK编码的 res = subprocess.Popen(cmd,shell=True, #PIPE为管道/队列,只能取一次,取完就结束了 stdout=subprocess.PIPE, #stdout为命令的输出 stderr=subprocess.PIPE) #stderr为命令错误的提示信息 std_out = res.stdout.read() #从队列中取出命令 std_err = res.stderr.read() #从队列中取出错误命令信息 sk.send(std_out) sk.send(std_err) sk.close()