Socket 套接字和解决粘包问题
---恢复内容开始---
Socket 套接字:
什么是socket:
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实
就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口
就是全部,让Socket去组织数据,以符合指定的协议。
socket在OSI模型中的位置
二为什么需要socket
那为什么需要socket呢?一个字 懒,程序员都是懒的!
我们发现还没有开始实现应用程序逻辑,就需要花大把时间来实现各种协议,太特么费事儿了,就有人专门把协议中一堆复杂的事情进行了封装,于是socket就诞生了!
有了socket以后,无需自己编写代码实现三次握手,四次挥手,ARP请求,打包数据等等,socket已经封装好了,只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
三socket的发展:
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
基于文件类型的套接字家族:
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族:
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于大部通讯都是网络通讯,所以大部分时候使用AF_INET)
python中的socket:
需明确:关于网络协议 和socket相关概念,对于所有编程语言都是一致的,区别仅仅是各编程语言的函数名称不同
TCP协议:
1.导入socket模块
import socket
2创建TCP套接字对象,函数定义如下
server = socket.socket(scoket.AF_INET,socket.SOCK_STREAM)
或者
server = socket.socket()里面不写就是默认TCP协议的
服务端的函数:
bind()绑定主机号加端口到套接字
127.0.0.1是本机回还地址,只能在本机适用。
listen()进入监听 可以可以放监听的客户端个数,也是半链接池,就是还没有完全通过三次握手的,填5就表示最大接收5个处于半链接状态,等链接断了就从这里拿一个出来,然后再进去,防止洪水攻击。
accept()被动接受客户端的链接,阻塞式等待链接的到来。
客户端的函数:
connect 主动链接服务端
connect_ex 提高健壮性,出错不会报异常,而是返回错误码。
公共:
recv()接收
send()发送
这2个要一一对应,客户端是发送服务端就要收,不然会阻塞。
粘包:
问题点:因为有2点一点就是tcp协议内部的优化机制,Nikon算法,会将数据量小河发送间隔短的数据打包一起发送给对方。二就是超出了接收方的最大接收字节,下次还会继续发,直到发完。
解决:
服务端:
1.先写一个发送给客户端的字典
2.制作字典的报头
3.发送字典的报头
4.发送字典的数据
5.发送真实数据
客户端:
1.先接受字典的报头长度
2.解包得到字典的长度
3.得到字典的数据
4.从字典中获取真实数据的长度
5.接收真实数据
服务端
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket import subprocess import json import struct server = socket.socket() server.bind(('127.0.0.1',9696)) server.listen(5) while True: conn,addr = server.accept() while True: try: data = conn.recv(1024).decode('utf-8') if len(data) == 0:break obj = subprocess.Popen(data,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) msg = obj.stdout.read() + obj.stderr.read() dic = {'name':"lw",'file_size':len(msg)} json_dic = json.dumps(dic) dic_header = struct.pack('i',len(json_dic)) # 先产生字典的报头长度 conn.send(dic_header) # 先发送报头长度 conn.send(json_dic.encode('utf-8')) # 发送字典数据 conn.send(msg) # 发送真实数据 except ConnectionResetError: break conn.close()
客户端
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket import struct import json client = socket.socket() client.connect_ex(('127.0.0.1',9696)) while True: cmd = input('请输入指令:').strip() if len(cmd) == 0:continue client.send(cmd.encode('utf-8')) data = client.recv(4) # 获得字典报头大小 dic_head = struct.unpack('i',data) [0] # 获得字典的长度 json_dic = client.recv(dic_head) #接收字典的数据 json_bytes = json.loads(json_dic.decode('utf-8')) # 反序列化处理 recv_size = 0 recv_data = b'' while recv_size < json_bytes.get('file_size'): data = client.recv(1024) recv_data+=data recv_size+=len(data) print(recv_data.decode('gbk'))
---恢复内容结束---