zoukankan      html  css  js  c++  java
  • 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

     

     

     

     

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

  • 相关阅读:
    RSA算法
    随机数相关面试题
    黑冰
    Servlet
    中国文人的弱点
    黑客与画家
    J2SE、JS及JavaWeb的若干知识
    离婚前规则
    自己写的一个智能指针类
    用_makepath和_splitpath构造路径&分解路径
  • 原文地址:https://www.cnblogs.com/xinfan1/p/11318168.html
Copyright © 2011-2022 走看看