1. 程式人生 > >網絡編程 之粘包問題、使用socketserver實現並發

網絡編程 之粘包問題、使用socketserver實現並發

簡述 3.1 roc json 發送 pip 圖片 RoCE read

一、粘包問題

註意:粘包問題只有tcp協議並且udp協議永遠不會粘包

粘包問題的產生:

簡述:粘包問題的產生主要是由於tcp協議傳輸數據(其內置的nagle算法來進行的)會將數據較小的且發送時間較短的合並成一個包從發送端發送出去,接收端不知道該怎麽去想要的數據拿出來這樣造成了粘包問題,另一方面是由於時間太短接收端沒有及時拿幹凈

傳來的數據造成數據混亂(這是因為tcp協議又叫流氏協議指的是其就像水流一樣傳輸數據)才產生的粘包問題。

1、發送端由於時間太短造成多個包合在一起發送產生粘包問題的實例如下:

服務端:

技術分享圖片
from socket import *
ip_port=(127.0.0.1,8080)

tcp_socket_server
=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) conn,addr=tcp_socket_server.accept() data1=conn.recv(10) data2=conn.recv(10) print(----->,data1.decode(utf-8)) print(----->,data2.decode(utf-8)) conn.close()
View Code

客戶端:

技術分享圖片
import socket
BUFSIZE
=1024 ip_port=(127.0.0.1,8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(ip_port) s.send(hello.encode(utf-8)) s.send(feng.encode(utf-8))
View Code

2、由於接收端沒有接收幹凈發送端發來的數據造成的粘包問題的實例如下:

服務端:

技術分享圖片
from socket import *
ip_port=(127.0.0.1,8080)

tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(
5) conn,addr=tcp_socket_server.accept() data1=conn.recv(2) #一次沒有收完整 data2=conn.recv(10)#下次收的時候,會先取舊的數據,然後取新的 print(----->,data1.decode(utf-8)) print(----->,data2.decode(utf-8)) conn.close()
View Code

客戶端:

技術分享圖片
import socket
BUFSIZE=1024
ip_port=(127.0.0.1,8080)

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)


s.send(hello feng.encode(utf-8))
View Code

3、粘包現象出現的實例如下:
3.1基於subprocess模塊產生的粘包現象

服務端:

技術分享圖片
from socket import *
import subprocess

ip_port=(127.0.0.1,8080)
BUFSIZE=1024

tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)

while True:
    conn,addr=tcp_socket_server.accept()
    print(客戶端,addr)

    while True:
        cmd=conn.recv(BUFSIZE)
        if len(cmd) == 0:break

        res=subprocess.Popen(cmd.decode(utf-8),shell=True,
                         stdout=subprocess.PIPE,
                         stdin=subprocess.PIPE,
                         stderr=subprocess.PIPE)

        stderr=act_res.stderr.read()
        stdout=act_res.stdout.read()
        conn.send(stderr)
        conn.send(stdout)
View Code

客戶端:

技術分享圖片
import socket
BUFSIZE=1024
ip_port=(127.0.0.1,8080)

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)

while True:
    msg=input(>>: ).strip()
    if len(msg) == 0:continue
    if msg == quit:break

    s.send(msg.encode(utf-8))
    act_res=s.recv(BUFSIZE)

    print(act_res.decode(utf-8),end=‘‘)
View Code

3.2使用struct模塊的解決方案
服務端:

技術分享圖片
import socket,json,struct,subprocess
ip_port=(127.0.0.1,2206)
ip_base=1024
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(ip_port)
server.listen(5)
while True:
    conn,adder=server.accept()
    while True:
        try:
            data=conn.recv(ip_base)
            if len(data)==0:
                break
            res=subprocess.Popen(data.decode(utf-8),shell=True,stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
            stdout=res.stdout.read()
            stderr=res.stderr.read()
            head_dic={filname:dir,md5:fffff,head:len(stdout)+len(stderr)}
            head_json=json.dumps(head_dic)
            head_bytes=head_json.encode(utf-8)
            conn.send(struct.pack(i,len(head_bytes)))
            conn.send(head_bytes)
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break
    conn.close()
server.colse()
View Code

客戶端:

技術分享圖片
import socket,json,struct
ip_port=(127.0.0.1,2206)
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(ip_port)
while True:
    mag=input(>>>:).strip()
    if len(mag)==0:
        continue
    client.send(mag.encode(utf-8))
    #先拿到報頭的固定長度(4bytes)
    head_len=struct.unpack(i,client.recv(4))[0]
    #再拿到報頭
    head_bytes=client.recv(head_len)
    head_json=head_bytes.decode(utf-8)
    head_dic=json.loads(head_json)
    #拿到報頭的中數據的長度
    total=head_dic[head]
    rb=b‘‘
    stawith=0
    while stawith<total:
        data=client.recv(total)
        stawith+=len(data)
        rb+=data
    print(rb.decode(gbk))
client.close()
View Code

補充:為何udp協議不會發生粘包問題是由於udp協議不是通過連接進行數據傳輸的並且基於udp協議發送的數據都會自帶報頭,
所以接收端可以通過每條數據的報頭去取出數據並且udp協議傳輸數據是傳一個數據就立馬刪除掉這樣不會讓接收端由於接受不

急時而造成數據混亂。(udp協議也可以叫數據報協議)

二、使用socketserver模塊實現並發

1、基於tcp協議通信實現並發

服務端:

技術分享圖片
import socketserver
class Myudpheadler(socketserver.DatagramRequestHandler):
    def handle(self):
        while True:
            data,sock=self.request()
            sock.sendto(data,self.client_address)
if __name__ == __main__:
    sever=socketserver.ThreadingUDPServer((127.0.0.1,2221),Myudpheadler)
    sever.serve_forever()
View Code

客戶端:

技術分享圖片
from socket import *
ip_port=(127.0.0.1,2221)
client=socket(AF_INET,SOCK_DGRAM)
while True:
    mad=input(>>>>:).strip()
    if len(mad)==0:
        continue
    client.sendto(mad.encode(utf-8),ip_port)
    adder,sock=client.recvfrom(1024)
    print(adder)
    print(sock)
client.close()
View Code

2、基於udp協議通信實現並發
服務端:

技術分享圖片
import socketserver
class mudphead(socketserver.DatagramRequestHandler):
    def handle(self):
        while True:
            data,sock=self.request()
            sock.sendto(data,self.client_address)
if __name__ == __main__:
    server=socketserver.ThreadingUDPServer((127.0.0.1,2220),mudphead)
    server.serve_forever()
View Code

客戶端:

技術分享圖片
from socket import *
ip_port=(127.0.0.1,2206)
client=socket(AF_INET,SOCK_DGRAM)
while True:
    client.sendto(hello.encode(utf-8),ip_port)
    adder,scok=client.recvfrom(1024)
    print(adder)
client.close()
View Code

補充:基於udp協議其自身就可以實現通信實現並發。

網絡編程 之粘包問題、使用socketserver實現並發