低级别网络接口-socket的应用和粘包现象

套接字的类型

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

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

 

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

 

TCP   socket 通信

服务端配置

import socket
server = socket.socket() #创建一个服务器对象
ip_port = ('192.168.15.79',8080)#创建一个元组
server.bind(ip_port) #将服务器和ip地址进行绑定
server.listen(3)  #设置服务器可以同时监听多少个客户端
con,addr =server.accept()#接收客户端发送过来的东西,如果协商成功,就建立相应的隧道
# print(con)#两者协商通信的协议,使用的ip地址和端口号
# #<socket.socket fd=96, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.15.79', 8080), raddr=('192.168.15.79', 57945)>
print(addr)#('192.168.15.79', 57945) 连接的客户端的地址和端口号
Client = con.recv(1024)#设置接收的最大的文件的大小,并将接收到的信息进行赋值

print('客户端消息:',Client)
con.send(b'you are over')#向客户端发送相关的信息,但只能发送呢bytes类型
con.close()
server.close()

客户端配置

import socket
client = socket.socket()#创建客户端
Server_ip = ('192.168.15.79',8080) #设置连接服务器的ip地址和端口
client.connect(Server_ip) #连接服务器
client.send(b'cisoc')#连接完成后向服务端发送信息
cc = client.recv(1024) #设置接收文件的大小
print(cc)
client.close()

使用TCP连接的时候,如果一个TCP服务端与多个客户端连接,但是服务端只能与第一客户端连接,

tcp属于长连接,长连接就是一直占用着这个链接,这个连接的端口被占用了,第二个客户端过来连接的时候,他是可以连接的,但是处于一个占线的状态,就只能等着去跟服务端建立连接,当服务端关闭与第一个客户端的连接之后,重新等待连接才能与第二个客户端进行连接

一对多通信

import  socket
Ser_scoket = socket.socket()
Ser_scoket_ip  = ('192.168.11.70',8080)
Ser_scoket.bind(Ser_scoket_ip)
Ser_scoket.listen()
while 1:
    conn,addr = Ser_scoket.accept()
    while 1:
        Msg_to_Cli = input('服务端>>>').encode('utf-8')
        conn.send(Msg_to_Cli)
        if Msg_to_Cli == 'byebye'.encode('utf-8'):
            break

        Msg_from_Cli = conn.recv(1024).decode('utf-8')
        print('来自%s的消息%s' % (addr, Msg_from_Cli))
        if Msg_from_Cli == 'byebye':
            break

    conn.close()
一对多服务端
import socket
client_scoket = socket.socket()
Server_ip = ('192.168.11.70',8080)
client_scoket.connect(Server_ip)

while 1:
    Msg_from_Ser = client_scoket.recv(1024).decode('utf-8')
    print('来自[【%s】的消息:%s' % (Server_ip, Msg_from_Ser))
    if Msg_from_Ser == 'byebye':
        break

    Msg_to_Ser = input('客户端>>>').encode('utf-8')
    client_scoket.send(Msg_to_Ser)
    if Msg_to_Ser == 'byebye'.encode('utf-8'):
        break

client_scoket.close()
客户端

UDP socket通信

在UDP的配置中主要是两条命令

sendto  >>>>  两个参数是1.要发送的信息  目标地址

recvfrom >>> 将接收到的两个东西进行解包,第一个是接收到的信息,发送这条信息的IP地址

type = sock.SOCK_DGRAM  指定为UDP的类型

 

 粘包现象

每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。

 

每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。socket对象write()/send() 函数处理完数据后,都会数据往输出缓冲区中扔,写入到缓冲区后,socket对象就认为自己处理完数据,函数可以正常返回,再由TCP协议(传输层协议)将数据从输入缓冲区发送到目标机器。

由于TCP协议与 write()/send()是相关的两个个体,TCP协议按照自己的优化机制,对数据进行打包传输,这这就导致了多次写入的数据却被一次性发送,例如第一次发送的是12,第二次发送的是13, TCP在缓冲区读取的时候按照包的大小直接读取,并发送,在对端收到的就是1213连起来的数字
 

这些I/O缓冲区特性可整理如下:

 

1.I/O缓冲区在每个TCP套接字中单独存在;

2.I/O缓冲区在创建套接字时自动生成;

3.即使关闭套接字也会继续传送输出缓冲区中遗留的数据;

4.关闭套接字将丢失输入缓冲区中的数据

5.默认的大小是一般是8K

 

发生粘包的两种情况

     1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔短,数据小,被当做一个包发出去)

  2. 接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

 

粘包现象第一种情况:

服务端配置

import socket
server = socket.socket()
server_ip = ('192.168.11.70',8080)
server.bind(server_ip)
server.listen(3)
conn,addr = server.accept()
while 1:
    conn.send('12'.encode('utf-8'))
    conn.send('99'.encode('utf-8'))
View Code

客户端配置

import socket
client = socket.socket()
server_ip = ('192.168.11.70',8080)
client.connect(server_ip)
while 1:

    gg = client.recv(1024).decode('utf-8')
    ff = client.recv(1024).decode('utf-8')
    print(gg,ff)
View Code

粘包现象第二种情况:

服务端配置

import socket
import subprocess

server = socket.socket()
ip_port = ('192.168.11.70',8080)
server.bind(ip_port)
server.listen(2)
con,addr = server.accept()
# print(addr)

while 1 :

    client_cmd = con.recv(1024).decode('utf-8')
    # print('*****')
    sub = subprocess.Popen(
        client_cmd,
        shell=True,
        stderr=subprocess.PIPE,
        stdout=subprocess.PIPE,)
    # print('********')
    cmd_res = sub.stdout.read()
    print(type(cmd_res))
    con.send(cmd_res)
    print('结果长度>>>', len(cmd_res))
View Code

 客户端配置  >>>输入输出比较大的系统命令的时候就会出现粘包的现象 eg:ipconfig /all

mport socket
client = socket.socket()
ip_port = ('192.168.11.70',8080)
client.connect(ip_port)
while 1:
    client_cmd = client.send(input('请输入系统指令>>>').encode('utf-8'))
    cmd_result = client.recv(1024).decode('gbk')
    print(cmd_result)
View Code

解决粘包现象:

  1. 获取文件的传输大小,两边统一接收和传输的数据流大小

1.1利用struct模块,发送报头的方式来区分文件的传输 

 通过使用 struct.pack('i',len(文件长度))  返回值是后面的值的bytes类型,恒为4字节

低端在处理的时候先接受4字节,然后通过struct.upack(‘i’,字节) 获得文件的长度

但是一般这样子处理的扩展性比较差,一般是通过在发送端定义一个字典,对所有的要发送的文件进行概括,之后使用json 对字典进行序列化并进行编码,之后再使用struct 进行处理,让对端能够正确接收到字典,接收到字典后,双方在按照商定的大小进行接收和发送  eg:下面的例子

1.2   在发送端直接使用len 计算长度后,直接将数字发送到对端,对端按照发送过来的数字直接设置,进行接收(存在问题是如果文件过大也会导致,缓冲区爆满而出错)

1.3   不管文件大还是小,直接对字节码进行按照商定好的大小进行切片发送()

对端则按照商定好的大小进行接收(扩展差点,)

最优解决方案

服务端配置:

import socket
import os
import json
import struct

server = socket.socket()
server_ip  = ('192.168.11.70',8080)
server.bind(server_ip)
server.listen(3)
conn,addr = server.accept()
# print(os.path.getsize('D:python全栈第二阶段网络编程day28粘包服务端.py'))
dic  = {'filename':'粘包服务端','size':os.path.getsize('D:python全栈第二阶段网络编程day28粘包服务端.py')}
dic_JSON = json.dumps(dic).encode('utf-8')
Head_Byte = struct.pack('i',len(dic_JSON))
conn.send(Head_Byte+dic_JSON)
with open(r'D:python全栈第二阶段网络编程day28粘包服务端.py','rb') as f :
    while 1:
        file_Read = f.read(8192)
        if file_Read:
            conn.send(file_Read)
        else:break
View Code

客户端配置

import socket
import struct
import json

client = socket.socket()
server_ip = ('192.168.11.70',8080)
client.connect(server_ip)

dic_size = struct.unpack('i',client.recv(4))[0]
dic_json = client.recv(dic_size).decode('utf-8')
dic = json.loads(dic_json)
with open('py.py','wb') as f:
    if dic['size']:
        size_rev = client.recv(8192)
        f.write(size_rev)
        dic['size'] -=len(size_rev)
View Code

 

原文地址:https://www.cnblogs.com/vivi0403/p/10014841.html