1 .简述socket 通信原理


如上图,socket通信建立在应用层与TCP/IP协议组通信(运输层)的中间软件抽象层,它是一组接口,在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议组隐藏在Socket接口后面,对于用户来说,一组简单的接口就是全部,让socket去组织数据,以符合指定的协议。
所以,经常对用户来讲,socket就是ip+prot 即IP地址(识别互联网中主机的位置)+port是程序开启的端口号
socket通信如下:

客户端
# _*_ coding: utf-8 _*_
import socket
ip_port = ('127.0.0.1',9696)
link = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
link.connect(ip_port)
print("开始发送数据")
cmd = input("client请输入要发送的数据>>>>").strip()
link.send(cmd.encode('utf-8'))
recv_data = link.recv(1024)
print("这是受到的消息:",recv_data)
link.close()
服务端
# _*_ coding: utf-8 _*_
import socket
ip_port = ('127.0.0.1',9696)
link = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
link.bind(ip_port)
link.listen(5)
conn,addr = link.accept()
#这里,因为我们知道自己写的少,所以1024够用
recv_data = conn.recv(1024)
print("这是受到的消息:",recv_data)
cmd = input("server请输入要发送的数据>>>>").strip()
conn.send(cmd.encode('utf-8'))
conn.close()
link.close()
2,粘包的原因和解决方法?
2,粘包的原因和解决方法?
TCP是面向流的协议,发送文件内容是按照一段一段字节流发送的,在接收方看来不知道文件的字节流从和开始,从何结束。
UDP是面向消息的协议,每个UDP段都是一个消息,
直接原因:
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的根本原因: 发送方引起的粘包是由TCP协议本身造成的,TCP为了提高传送效率,发送方往往要收集到足够多的数据才发送一个TCP段,若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成到一个TCP段后一次发送过去,这样接收方就受到了粘包数据。如果需要一直收发消息,加一个while True即可。但是这里有个1024的问题,即粘包,
粘包的根源在于:接收端不知道发送端将要发的字节流的长度,所以解决粘包问题的方法就是围绕如何让发送端在发送数据前,把自己将要发送的字节流大小让接收段知道,然后接收端来一个死循环,接收完所有的数据即可。
粘包解决的具体做法:为字节流加上自定义固定长度报头,报头中包含字节流长度,然后依次send到对端,对端在接受时,先从缓存中取出定长的报头,然后再取真是数据。
客户端
# _*_ coding: utf-8 _*_
import socket
import struct
import json
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080)) #连接服务器
while True:
# 发收消息
cmd = input('请你输入命令>>:').strip()
if not cmd:continue
phone.send(cmd.encode('utf-8')) #发送
#先收报头的长度
header_len = struct.unpack('i',phone.recv(4))[0] #吧bytes类型的反解
#在收报头
header_bytes = phone.recv(header_len) #收过来的也是bytes类型
header_json = header_bytes.decode('utf-8') #拿到json格式的字典
header_dic = json.loads(header_json) #反序列化拿到字典了
total_size = header_dic['total_size'] #就拿到数据的总长度了
#最后收数据
recv_size = 0
total_data=b''
while recv_size<total_size: #循环的收
recv_data = phone.recv(1024) #1024只是一个最大的限制
recv_size+=len(recv_data) #有可能接收的不是1024个字节,或许比1024多呢,
# 那么接收的时候就接收不全,所以还要加上接收的那个长度
total_data+=recv_data #最终的结果
print('返回的消息:%s'%total_data.decode('gbk'))
phone.close()
服务端
# _*_ coding: utf-8 _*_
import socket
import subprocess
import struct
import json
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8080)) #绑定手机卡
phone.listen(5) #阻塞的最大数
print('start runing.....')
while True: #链接循环
coon,addr = phone.accept()# 等待接电话
print(coon,addr)
while True: #通信循环
# 收发消息
cmd = coon.recv(1024) #接收的最大数
print('接收的是:%s'%cmd.decode('utf-8'))
#处理过程
res = subprocess.Popen(cmd.decode('utf-8'),shell = True,
stdout=subprocess.PIPE, #标准输出
stderr=subprocess.PIPE #标准错误
)
stdout = res.stdout.read()
stderr = res.stderr.read()
# 制作报头
header_dic = {
'total_size': len(stdout)+len(stderr), # 总共的大小
'filename': None,
'md5': None
}
header_json = json.dumps(header_dic) #字符串类型
header_bytes = header_json.encode('utf-8') #转成bytes类型(但是长度是可变的)
#先发报头的长度
coon.send(struct.pack('i',len(header_bytes))) #发送固定长度的报头
#再发报头
coon.send(header_bytes)
#最后发命令的结果
coon.send(stdout)
coon.send(stderr)
coon.close()
phone.close()
来源:https://www.cnblogs.com/wanghui0412/p/12048125.html