python應用之socket編程


tcp/udp下的socket的基本使用

基於tcpsocket

Tcp是基於鏈接的,必須先啟動服務端,然后啟動客戶端進行鏈接

服務端:

 

ss = socket() #創建服務器套接字
ss.bind()      #把地址綁定到套接字
ss.listen()      #監聽鏈接
inf_loop:      #服務器無限循環
    cs = ss.accept() #接受客戶端鏈接
    comm_loop:         #通訊循環
        cs.recv()/cs.send() #對話(接收與發送)
    cs.close()    #關閉客戶端套接字
ss.close()        #關閉服務器套接字(可選)

 

客戶端:

cs = socket()    # 創建客戶套接字
cs.connect()    # 嘗試連接服務器
comm_loop:        # 通訊循環
     cs.send()/cs.recv()    # 對話(發送/接收)
cs.close()            # 關閉客戶套接字

簡單的實現:

這里是單個的 一次通信

 

 

mport socket           #   AF_INET 基於網絡通信, SOCK_STREAM(基於流的,tcp)

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #   買手機
phone.bind(('127.0.0.1', 8000)) #   綁定手機卡
phone.listen(5) #   開機(監聽)  #   5表示 最多有5個可以發送連接, 放在鏈接池中
print('--->')
conn, addr = phone.accept()  #   (等電話,等待連接,會阻塞)(觸發的是3次握手)

#  收發消息(在tcp協議中觸發的是數據傳輸)
# 注意收發信息不是用手機, 而是conn這條連接
mesg = conn.recv(1024)  #   收消息
print('客戶端發來的消息是:',mesg)
conn.send(mesg.upper())   # 發消息

#   斷開鏈接  (在tcp協議中觸發的是4次揮手)
conn.close()

#   關機
phone.close()
服務端
import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 買手機
phone.connect(('127.0.0.1', 8000))  # 撥通電話 (觸發的是三次握手)

#   收發數據
# phone.send('hello'.encode('utf-8'))   # socket不支持字符串發送,僅支持字節編碼發送所以要用
phone.send(bytes('hello', encoding='utf-8'))  # 這兩個發送一樣,只要轉換成二進制就行
data = phone.recv(1024)
print('收到服務端發來的消息', data)
客戶端

這個是簡單的交互模式

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 18-5-14 下午5:56
# @Author  : LK
# @File    : fu_ceshi.py
# @Software: PyCharm
from socket import *
buffersize = 1024
#
tcp_server = socket(AF_INET, SOCK_STREAM)  #   流式套接字
tcp_server.bind(('127.0.0.1', 8000))
tcp_server.listen(5)

#   為了接收多個連接
while True:
    #   等待連接
    conn, add = tcp_server.accept()  #  等待連接 ,會阻塞
    print('雙向連接是:',conn)
    print('客戶端地址是',add)

    #   收發信息
    while True:
        # try:
        #   這個異常實在win中,但是在linux下有時候是死循環, 因為客戶端突然斷掉,conn就沒了,mesg一直是空
            mesg = conn.recv(buffersize)
            if not mesg:
                print('客戶端你斷開連接了')
                break
            print('服務端接收的信息:',mesg.decode('utf-8'))
            conn.send(mesg.upper())
            print('服務端以發送回去')
        # except Exception:
        #     break
    conn.close()
#   關閉連接

tcp_server.close()
服務端
from socket import *
buffersize=1024
tcp_client = socket(AF_INET, SOCK_STREAM)
#   主動連接
tcp_client.connect(('127.0.0.1', 8000))

#   收發信息
while True:
    send_mesg = input('請輸入要個服務端發送的信息,break停止').strip('')
    #   如果發送的是空格,重新發送
    if not send_mesg: continue
    if send_mesg == 'break':
        print('客戶端停止發送信息了')
        break
    else:
        tcp_client.send(send_mesg.encode('utf-8'))
        print('客戶端已發送消息')

    mesg = tcp_client.recv(buffersize)
    print('客戶端接受的信息:',mesg.decode('utf-8'))

#   關閉連接
tcp_client.close()
客戶端

 

基於udpsocket

udp是無鏈接的,先啟動哪一端都不會報錯

 

服務端:
ss = socket()   #創建一個服務器的套接字
ss.bind()       #綁定服務器套接字
inf_loop:       #服務器無限循環
     cs = ss.recvfrom()/ss.sendto() # 對話(接收與發送)
ss.close()                         # 關閉服務器套接字

客戶端
cs = socket()   # 創建客戶套接字
comm_loop:      # 通訊循環
    cs.sendto()/cs.recvfrom()   # 對話(發送/接收)
cs.close()                      # 關閉客戶套接字

 

udp基本實現:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 18-5-15 下午1:38
# @Author  : LK
# @File    : udp_服務端.py
# @Software: PyCharm

from socket import *
ip_port = (('127.0.0.1', 8080))
buffsize = 1024
udp_server = socket(AF_INET, SOCK_DGRAM) #  數據報套接字
udp_server.bind(ip_port)
while True:
    data, addr = udp_server.recvfrom(buffsize)
    print('客戶端發來的消息',data.decode('utf-8'))
    udp_server.sendto(data.upper(), addr)   #   注意這里是addr
udp_server.close()
udp服務端
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 18-5-15 下午1:38
# @Author  : LK
# @File    : udp_客戶端.py
# @Software: PyCharm
from socket import  *
ip_port = (('127.0.0.1', 8080))
buffsize = 1024
udp_client = socket(AF_INET, SOCK_DGRAM) #  數據包套接字

while True:
    try:
        mesg = input('>>>').strip()
        udp_client.sendto(mesg.encode('utf-8'), ip_port)
        #   recvfrom 接受的是一個元祖, 里面是信息和對方地址
        data, addr = udp_client.recvfrom(buffsize)
        print('服務端發來的是', data.decode('utf-8'))
        # print(data)
    except KeyboardInterrupt as e:
        print(e)
        print('程序異常中斷')
        break





#   recv 在自己這端的緩沖區為空時,會阻塞(返回的是信息)
#   recvfrom 不會阻塞 (返回是一個元祖,分別是接受的信息和地址)

#   tcp沒有實現並發,而udp默認實現了,因為udp不用建立連接,直接網端口發送,而tcp需要建立連接
udp客戶端

udp的客戶端可以開多個,

 

 常用函數

服務端套接字函數
s.bind() 綁定(主機,端口號)到套接字
s.listen() 開始TCP監聽
s.accept() 被動接受TCP客戶的連接,(阻塞式)等待連接的到來

客戶端套接字函數
s.connect() 主動初始化TCP服務器連接
s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常

公共用途的套接字函數
s.recv() 接收TCP數據
s.send() 發送TCP數據(send在待發送數據量大於己端緩存區剩余空間時,數據丟失,不會發完)
s.sendall() 發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩余空間時,數據不丟失,循環調用send直到發完)
s.recvfrom() 接收UDP數據
s.sendto() 發送UDP數據
s.getpeername() 連接到當前套接字的遠端的地址
s.getsockname() 當前套接字的地址
s.getsockopt() 返回指定套接字的參數
s.setsockopt() 設置指定套接字的參數
s.close() 關閉套接字

面向鎖的套接字方法
s.setblocking() 設置套接字的阻塞與非阻塞模式
s.settimeout() 設置阻塞套接字操作的超時時間
s.gettimeout() 得到阻塞套接字操作的超時時間

面向文件的套接字的函數
s.fileno() 套接字的文件描述符
s.makefile() 創建一個與該套接字相關的文件

tcp粘包問題

 

首先要知道socket的收發消息的原理

 

 

send 是發送給用 對方后,存在對方的內核態中,

recv 收消息是從自己的緩存中收

只有tcp有粘包現象,udp沒有,但是udp會發生丟包問題, 就是發的數據太多,到那時udp的緩沖區,只能接收固定大小,剩下的就丟了, 所以udp沒有tcp可靠

流程就是, 服務端發送數據給自己的緩沖區,然后通過網絡發送給客戶端的緩沖區, 客戶端從自己的緩沖區進行取數據,recv函數 一次可能取不完,就可能會出現粘包問題 如果recv取的少的話,或者發送的過多,就可能會出現粘包問題 Recv 是從自己的 內核態中去數據, 如果數據過多的話就會出現粘包問題 解決粘包問題思路: 可以先獲取消息的大小, 然后死循環一直發,知道發送到指定大小在停止, 收包也是如此

所謂粘包問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的。

粘包的表現

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 18-5-16 上午8:42
# @Author  : LK
# @File    : tcp_粘包問題_服務端.py
# @Software: PyCharm

#   粘包問題發生的原因:
# 第一種情況:tcp在發送的時候,基於流的方式發送數據,發送端要等緩沖區滿后才發送出去,造成粘包
# (發送數據的時間很短,數據很小,就會和在一起發送)

#   第二種情況,發送端發送的數據很大,但是接收端的緩沖區已經放不下,直接從緩沖區里面拿
# 多次都從緩沖區里面取數據(就是發送的大,但是取的少)

#   解決方法:一般是先發送一個數據大小,然后回復一下,在用循環發送數據,高級辦法(struct),跟高級(偏函數)

from socket import  *
tcp_socket = socket(AF_INET, SOCK_STREAM)
ip_port = (('127.0.0.1', 8080))
tcp_socket.bind(ip_port)
tcp_socket.listen(5)
conn, addr = tcp_socket.accept()
print('連接的信息',conn)
print('詳細信息',addr)
#   收發數據
while True:
    # '''第二種粘包現象,就是發的多,每次收的少'''
    # mesg = conn.recv(2)
    # print('第一次接受的',mesg.decode('utf-8'))
    # mesg = conn.recv(1)
    # print('第二次接受的',mesg.decode('utf-8'))
    # mesg = conn.recv(1)
    # print('第三次接受的',mesg.decode('utf-8'))

    '''第一種粘包現象, 發的少收得多'''
    mesg = conn.recv(1024)
    print('第一次接受的',mesg.decode('utf-8'))
    mesg = conn.recv(1)
    print('第二次接受的',mesg.decode('utf-8'))
    mesg = conn.recv(1)
    print('第三次接受的',mesg.decode('utf-8'))

coon.close()
tcp_socket.close()
粘包的表現_服務端
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 18-5-16 上午8:41
# @Author  : LK
# @File    : tcp_粘包問題_客戶端.py
# @Software: PyCharm

from socket import *
tcp_client = socket(AF_INET, SOCK_STREAM)
ip_port = (('127.0.0.1',8080))
tcp_client.connect(ip_port)

#   收發消息
while  True:
    '''第一中粘包現象,發的少,收的多'''
    tcp_client.send('第一次發送的'.encode('utf-8'))
    tcp_client.send('第二次發送的'.encode('utf-8'))
    tcp_client.send('第三次發送的'.encode('utf-8'))
    tcp_client.recv(4)


    # '''第二種粘包現象,發的多收的少'''
    # tcp_client.send(b'hhhh')
    # tcp_client.send(b'ff')
    # tcp_client.recv(4)
粘包的表現_客戶端

圖解模式:

粘包的本質:接收端不知道,從緩存中取多少

第一種粘包現象:

接收端

 

發送端

運行結果

 

第二種粘包現象

接收端

發送端

結果

 

 tcp模擬終端,shell執行結果來顯示 粘包問題並解決

如果是linux下 ,不用修改, 輸入ls 顯示當前目錄下的文件,然后輸入ifconfig 查看ip等信息,返回的信息比較多,一次不能提取完,再輸入其他命令就會產生錯誤

windows 中 如果產生編碼錯誤就把utf-8 改成gbk  

命令用win下的 如 dir  然后 ipconfig 再輸入 其他的

#   存在粘包問題

import subprocess
from socket import *
from tcp_模擬終端處理數據 import dealwith

ip_port = (('127.0.0.1', 9000))
buffsize = 1024
tcp_server = socket(AF_INET, SOCK_STREAM)
try :
    tcp_server.bind(ip_port)
    tcp_server.listen(5)
    print('還沒有人連接')
    #   等待多個連接
    while True:
        try:
            conn, addr = tcp_server.accept()
            print('客戶端連接信息', conn)
            print('客戶端地址', addr)

            #   收發消息
            while True:
                mesg = conn.recv(buffsize)
                if not mesg:
                    print('客戶端中斷連接了,服務器等待下次連接')
                    break
                print('客戶端發送的是', mesg.decode('utf-8'))

                data = dealwith(mesg)
                #   將這些封裝成了一個函數
                '''
                # # 處理從客戶端接收的信息
                # #   res是接收的信息,經過shell腳本處理后的結果
                # res = subprocess.Popen(mesg.decode('utf-8'), shell=True,
                #                        stdin=subprocess.PIPE,
                #                        stdout=subprocess.PIPE,
                #                        stderr=subprocess.PIPE
                #                        )
                # #   獲取錯誤信息
                # err = res.stderr.read().decode('utf-8')
                # if not err:
                #     data = res.stdout.read().decode('utf-8')
                # else:
                #     data = err
                '''

                conn.sendall(data)
                print('服務端已發送')
        except KeyboardInterrupt as e:
            print('外部強制結束')
            break

        conn.close()
except OSError as e:
    print('端口以用過')

tcp_server.close()
模擬終端_產生粘包_服務端
#   存在粘包問題

from socket import *
tcp_client = socket(AF_INET, SOCK_STREAM)
ip_port = (('127.0.0.1', 9000))
buffsize = 1024
tcp_client.connect(ip_port)

while True:
    # mesg = input('>>>').strip()
    #   去除左右空格
    mesg = input('>>>').lstrip().rstrip()
    if not mesg: continue
    tcp_client.send(mesg.encode('utf8'))
    print('客戶端已發送')
    data = tcp_client.recv(buffsize)
    
    if not data:
        print('服務端發來的是空信息')
        continue
    print('服務端發來的信息是:',data.decode('utf-8'))

tcp_client.close()
模擬終端_產生粘包_客戶端
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 18-5-15 下午5:23
# @Author  : LK
# @File    : tcp_模擬終端處理數據.py
# @Software: PyCharm
import subprocess
def dealwith(mesg):
    # 處理從客戶端接收的信息
        #   res是接收的信息,經過shell腳本處理后的結果
        res = subprocess.Popen(mesg.decode('utf-8'), shell=True,
                               stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE
                               )
        #   獲取錯誤信息
        err = res.stderr.read()
        if not err:
            data = res.stdout.read()
            if not data:
                # data = '你輸入的數據返回值為空'.encode('utf-8')
                return '你輸入的數據返回值為空'.encode('utf-8')
        else:
            data = err
        return data
#   這里返回的是一個字節的形式

# res = subprocess.Popen('ls', shell=True,
#                        stdin=subprocess.PIPE,
#                        stdout=subprocess.PIPE,
#                        stderr=subprocess.PIPE
#                        )
# # stdin=subprocess.PIPE,   標准輸入, 輸入的內容會放在管道中
# # stdout=subprocess.PIPE,  標准輸出,就是結果會放在pipe這個管道中
# # stderr=subprocess.PIPE,  標准錯誤,如果結果錯誤 會放在這個管道中
#
# #   讀取管道內容
# # print(res.stdout.read()), 讀取之后,管道里面的就沒有輸出了
數據處理

第一種處理粘包的方法:

思路: 在發送端首先發送給接受端一個4個字節大小的數據,內容是要發的真是數據的大小.接收端在取數據的時候取4個字節,

然后給發送端發送一個准備接受的信號,當發送端接收到這個信號時,開始發送信息.接收端開始循環接收信息.

struct的用法

import subprocess
from socket import *
from tcp_模擬終端處理數據 import dealwith

ip_port = (('127.0.0.1', 9000))
buffsize = 1024
tcp_server = socket(AF_INET, SOCK_STREAM)
try:
    tcp_server.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
    tcp_server.bind(ip_port)
    tcp_server.listen(5)
    print('還沒有人連接')
    #   等待多個連接
    while True:
        try:
            conn, addr = tcp_server.accept()
            print('客戶端連接信息', conn)
            print('客戶端地址', addr)

            #   收發消息
            while True:
                mesg = conn.recv(buffsize)
                if not mesg:
                    print('客戶端中斷連接了,服務器等待下次連接')
                    break
                print('客戶端發送的是', mesg.decode('utf-8'))

                data = dealwith(mesg)
                #   解決粘包:,發送端先發送一個消息的大小,接收端回復一個准備接收的信號,然后發送端開始循環發送信息
                length = len(data)
                conn.send(str(length).encode('utf-8'))
                read_send = conn.recv(buffsize).decode('utf-8')

                #   注意這里的問題,就是發送一個大的文件,用sendall,,如何發送,還是在於如何取
                if read_send == 'read_recv':
                    conn.sendall(data)
                    print('服務端已發送')

        except KeyboardInterrupt as e:
            print('外部強制結束')
            break

        conn.close()
except OSError as e:
    print('端口以用過')
    # tcp_server.close()
tcp_server.close()
解決粘包問題_low版_服務端
  # low版解決粘包問題
from socket import *
import sys

tcp_client = socket(AF_INET, SOCK_STREAM)
ip_port = (('127.0.0.1', 9000))
buffsize = 1024

tcp_client.connect(ip_port)
#3  用connect_ex()比較好, 可以重復利用 端口
while True:

    #   去除左右空格
    mesg = input('>>>').lstrip().rstrip()
    if not mesg: continue
    tcp_client.send(mesg.encode('utf8'))
    print('客戶端已發送')

    data_lenth = int(tcp_client.recv(1024).decode('utf-8'))

    #   解決粘包問題
    tcp_client.send('read_recv'.encode('utf-8'))
    recv_leng = 0
    data = b''
    while recv_leng < data_lenth:
        if data_lenth - recv_leng > buffsize:
            data += tcp_client.recv(buffsize)
            recv_leng = len(data)
        else:
            #   當剩余的數據 小於緩沖區大小時
            data += tcp_client.recv(data_lenth - recv_leng)
            recv_leng = len(data)

    if not data:
        print('服務端發來的是空信息')
        continue
    print('服務端發來的信息是:', data.decode('utf-8'))

tcp_client.close()
解決粘包問題_low版_客戶端

第二種處理粘包方法:推薦使用

思路struct,發送端用struct.pack()  (一般用int類型占4個字節) 將要發送的數據的大小打包后發送過去,然后在發送真實數據.兩個必然會粘包.

但是接收端在接收的時候先接收4個字節,然后用struct.unpack()解包,得到數據大小,再利用循環接受真實數據.

struct用法:

http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html
# 用struct 解決粘包
import struct
import subprocess
from socket import *
from tcp_模擬終端處理數據 import dealwith

ip_port = (('127.0.0.1', 9000))
buffsize = 1024
tcp_server = socket(AF_INET, SOCK_STREAM)
#   這一句是 重復利用 端口

try:
    tcp_server.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
    tcp_server.bind(ip_port)
    tcp_server.listen(5)
    print('還沒有人連接')
    #   等待多個連接
    while True:
        try:
            conn, addr = tcp_server.accept()
            print('客戶端連接信息', conn)
            print('客戶端地址', addr)

            #   收發消息
            while True:
                mesg = conn.recv(buffsize)
                if not mesg:
                    print('客戶端中斷連接了,服務器等待下次連接')
                    break
                print('客戶端發送的是', mesg.decode('utf-8'))

                data = dealwith(mesg)
                #   解決粘包:,發送端先發送一個消息的大小,接收端回復一個准備接收的信號,然后發送端開始循環發送信息
                length = len(data)
                conn.send(struct.pack('i', length))
                conn.sendall(data)
                print('服務端已發送')

        except KeyboardInterrupt as e:
            print('外部強制結束')
            break

        conn.close()
except OSError as e:
    print('端口以用過')
    # tcp_server.close()
tcp_server.close()
解決粘包-服務端_推薦使用
#   struct解決粘包
from socket import *
import  struct
tcp_client = socket(AF_INET, SOCK_STREAM)
ip_port = (('127.0.0.1', 9000))
buffsize = 1024
tcp_client.connect(ip_port)

while True:
    # mesg = input('>>>').strip()
    #   去除左右空格
    mesg = input('>>>').lstrip().rstrip()
    if not mesg: continue
    tcp_client.send(mesg.encode('utf8'))
    print('客戶端已發送')

    #   解決粘包
    #   服務端會發送過來一個 數據的大小,和 一個真實數據的包,兩個必定會粘包,但是解包是分開來解,先解4個字節(對方發來的數據包大小)
    data_length = tcp_client.recv(4)
    length = struct.unpack('i', data_length)[0]

    #   循環接收數據
    recv_leng = 0
    data = b''
    while recv_leng < length:
        if length - recv_leng > buffsize:
            data += tcp_client.recv(buffsize)
            recv_leng = len(data)
        else:
            data += tcp_client.recv(length-recv_leng)
            recv_leng = len(data)

    if not data:
        print('服務端發來的是空信息')
        continue
    print('服務端發來的信息是:', data.decode('utf-8'))

tcp_client.close()
解決粘包-客戶端

 udp 的丟包問題 

 

#   udp傳輸數據是以數據報的方式發送,每次發送都是發送完整的數據,但是接收方接收的可能小於發送放,就會產生丟包
# 因為每次都是發送一個完整的,取時也是取完緩沖區,所以不會發生粘包,但是會發生丟包,意味着,這是不安全的
from socket import *
udp_server = socket(AF_INET, SOCK_DGRAM)
ip_port = (('127.0.0.1',8080))
udp_server.bind(ip_port)

#   收發消息
while True:

    #   這里的6 出現兩個漢字是因為, 漢字的編碼方式不同(3個字節,在win中不同)
    data, addr = udp_server.recvfrom(6)
    print('第一次接收的信息是:',data.decode('utf-8'))
    data, addr = udp_server.recvfrom(6)
    print('第二次接收的信息是',data.decode('utf-8'))
    # udp_server.sendto(data,addr)
服務端
from socket import  *
udp_client = socket(AF_INET, SOCK_DGRAM)
ip_port = (('127.0.0.1', 8080))
buffsize = 1024

while True:
    data = '第一次發送的信息'
    udp_client.sendto(data.encode('utf-8'), ip_port)
    data = '第二次發送的信息'
    udp_client.sendto(data.encode('utf-8'), ip_port)
    udp_client.recvfrom(10)
客戶端

socket_server實現並發

基於tcp的套接字,關鍵就是兩個循環,一個鏈接循環,一個通信循環

socketserver模塊中分兩大類:server類(解決鏈接問題)和request類(解決通信問題)

 

#   這樣就tcp服務器就能被多個用戶鏈接了, 客戶端流程不變
class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        # self.request # 相當於  conn
        # self.client_address   #  相當於 addr
        while True:
            '''在這里寫收發消息'''

s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyServer)  # 將上面的類實例化一個
s.serve_forever()  #    一直開啟狀態

 

tcp_socket_server並發版_服務端

import socketserver
import struct
from 封裝處理粘包_拆包 import UnPacket, SendData


class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        self.request
        print('連接人的信息')
        print('conn是', self.request)  # conn
        print('addr是', self.client_address)  # addr

        while True:
            '''收發消息'''
            head_data = self.request.recv(4)
            if not head_data: break
            real_data = UnPacket(head_data, self.request)
            print('客戶端發來的是:%s客戶端地址是是%s' % (real_data.decode('utf-8'), self.client_address))

            #   回復客戶端,防止粘包
            send_data = 'aaaaaaaa'
            SendData(send_data, self.request)


if __name__ == '__main__':
    # pass
    print('還沒有人連接')
    s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyServer)  # 多線程
    # s = socketserver.ForkingTCPServer(('127.0.0.1', 8080), MyServer)  # 多進程win中不行

    #   服務器一直開着
    s.serve_forever()

客戶端

from socket import  *
from 封裝處理粘包_拆包 import UnPacket, SendData
import struct
tcp_server = socket(AF_INET, SOCK_STREAM)
ip_port = ('127.0.0.1', 8080)
buffsize = 1024
tcp_server.connect_ex(ip_port)

while True:
    '''收發信息'''
    mesg = input('>>>').strip()
    #   防止粘包處理函數
    SendData(mesg, tcp_server)
    mesg= tcp_server.recv(4)
    if not mesg: break
    recv_data = UnPacket(mesg, tcp_server)
    print('服務端發送的信息是',recv_data.decode('utf-8'))
tcp_server.close()

處理粘包的問題,封裝了一下后是: 

封裝處理粘包_拆包
import struct
#   傳過來包的頭部信息,和連接,返回接受的數據是一個byte類型
def UnPacket(head_data ,conn):
    buffsize = 4
    '''解決粘包,需要傳送過數據頭,和連接'''
    real_data_length = struct.unpack('i',head_data)[0]
    real_data = b''
    recv_length = 0

    #   這個少了點優化
    # while recv_length < real_data_length:
    #     real_data += conn.recv(1024)
    #     recv_length = len(real_data)

    while recv_length < real_data_length:
        if real_data_length - recv_length > buffsize:
            real_data += conn.recv(buffsize)
            recv_length = len(real_data)
        else:
            real_data += conn.recv(real_data_length-recv_length)
            recv_length = len(real_data)

    return real_data

#   接受要發送的數據,是字符串,和要發送的鏈接(客戶端發送tcp_server, 服務端發送conn或self.request))
def SendData(real_data,conn):
    '''發送包,發兩次,'''
    head_length = struct.pack('i', len(real_data))  #   后面是一個整數,所以是四個字節, 這里有個優化
    conn.send(head_length)
    conn.sendall(real_data.encode('utf-8'))
    print('客戶端發送成功')

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM