Socket 套接字和解决粘包问题

---恢复内容开始---

Socket 套接字:

什么是socket:

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实

就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口

就是全部,让Socket去组织数据,以符合指定的协议。

socket在OSI模型中的位置

二为什么需要socket

在标准的OIS模型中并没有规定说必须有socket层,也就是说不使用socket也能完成通讯,是的,的确如此!

那为什么需要socket呢?一个字 懒,程序员都是懒的!

我们发现还没有开始实现应用程序逻辑,就需要花大把时间来实现各种协议,太特么费事儿了,就有人专门把协议中一堆复杂的事情进行了封装,于是socket就诞生了!

有了socket以后,无需自己编写代码实现三次握手,四次挥手,ARP请求,打包数据等等,socket已经封装好了,只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

三socket的发展:

套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。

基于文件类型的套接字家族:

套接字家族的名字:AF_UNIX

unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

基于网络类型的套接字家族:

套接字家族的名字:AF_INET

(还有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.接收真实数据

 服务端

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()
View Code

客户端

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'))
View Code

 

 

 

 

 

 

 

 

 

 

---恢复内容结束---

posted @ 2019-08-07 22:04  帅气逼人23  阅读(374)  评论(0编辑  收藏  举报