ネットワークプログラミングソケットソケット

ソケットソケット

プライマー

互联网的核心就是由一堆协议组成,协议就是标准,比如全世界人通信的标准是英语

如果把计算机比作人,互联网协议就是计算机界的英语。所有的计算机都学会了互联 网协议,那所有的计算机都就可以按照统一的标准去收发信息从而完成通信了。

人们按照分工不同把互联网协议从逻辑上划分了层级,即OSI七层架构

なぜ、ソケットが最初にインターネット・プロトコルを学ばなければならない学びます

    C/S架构的软件(软件属于应用层)是基于网络进行通信的,网络的核心即一堆协议,协议即标准,你想开发一款基于网络通信的软件,就必须遵循这些标准。

ソケットは何ですか

ソケットアプリケーション層とインタフェースのセットである抽象化層を、通信する中間ソフトウェアTCP / IPプロトコルスイート。デザインモードでは、ソケットは、実際にファサードパターンであり、それはTCP / IPプロトコルスイートは、ユーザーのためのソケットインタフェースの背後に隠されているの複雑さで、シンプルなインタフェースがすべてセットで、ソケットが指定を遵守するために、データを整理してみましょう合意。

だから、私たちはTCP / UDPプロトコルの深い理解を必要としない、ソケットが私たちのために良いパッケージを持って、私たちは自然のTCP / UDPの基準を以下のプログラムを作成し、プログラムへのソケットの規定に従う必要があります。

也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序

而程序的pid是同一台机器上不同进程或者线程的标识

ソケットタイプ

ソケットファミリーのファイルタイプに基づいて、

ソケットファミリー名:AF_UNIX

UNIXのすべてが同じマシン上で実行中のプロセス、通信が間接同じファイルシステムにアクセスすることによって行うことができ、ファイル、ファイルのソケットコールは基礎となるファイルシステム上のデータへのアクセスをベースに二つのソケットであります

ネットワークソケットファミリのタイプに基づいて、

ソケットファミリー名:AF_INET

(AF_INET6もIPv6のために使用することがすべてのアドレスは、他のいくつかのアドレス家族が暮らしているが、それらはいずれかのみ、特定のプラットフォーム用に使用されている、またはそれが放棄されていないか、ほとんど使用されている、あるいは単に本当ではありません家族は、AF_INETは、我々は唯一のネットワークプログラミングを気にしているため、最も広く使わ1、Pythonはほとんどの時間は、私が唯一のAF_INET)それを使用するので、アドレスファミリの様々なサポートしていますが、あります

ソケットのワークフロー

- 人生のシナリオ。あなたは友人を呼びたい、最初のダイヤル電話が電話リングを聞いた後、友人をもたらし、その後、あなたとあなたの友人はあなたが話すことができ、接続を確立しました。そして為替のもう一方の端は、この会話を終了するには電話を切ります。人生のシナリオは、この動作を説明します。

サーバ側の話を開始します。その後、サーバーは(バインド)結合ポートは、ポート(聞く)、クライアント接続を待って、ブロックされたコールを受け入れるに耳を傾け、ソケットを初期化します。接続が成功した場合は、クライアントはソケットを開始している場合は、この時点では、サーバー(接続)に接続し、その後、クライアントを接続し、サーバが確立されています。クライアントは、サーバがリクエストを受信し、要求を処理し、クライアントに応答データを送信し、クライアントがデータを読み込み、そして最終的に接続を閉じ、対話の終了、データ要求を送信します。

ソケット()関数の使用モジュール


 1 import socket
 2 socket.socket(socket_family,socket_type,protocal=0)
 3 socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。
 4 
 5 获取tcp/ip套接字
 6 tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 7 
 8 获取udp/ip套接字
 9 udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
10 
11 由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。
12 例如tcpSock = socket(AF_INET, SOCK_STREAM)
# 服务端套接字函数
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ベースのソケット

TCPはリンクされ、それが最初にサーバーを起動し、サーバーベースのリンクにクライアントを起動する必要があります。

注意:ヘア空気ベースのTCPソケットの終わりには、サーバーがクライアントを受け入れることができない、クライアントは空の乗客/サーバ・カードが所定の位置になります送信します。そして、顧客サービス側が切断され、サーバが与えられていないため、あなたはこのヒューマンエラー(試すキャプチャする必要がある、文句を言うでしょう:例外を除いを:)

サーバーを起動すると、プログラムは、ADDR = server.acceptを()CONN内の位置をブロックして、クライアントの状態を待ち受け、サーバが起動している場合は、この時点で、サーバを再起動し、それが次のエラーを報告します。

    OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。

原因:サーバーは、まだ4つの状態が高い同時実行の下2.syn 4つのフラッド攻撃3.サーバーを振っ占有アドレス(スリーウェイハンドシェイク1.tcp理解していない場合は、してください深度調査に手を振っTIME_WAITが存在するためですTIME_WAIT状態の最適化方法の多くがあるでしょう)

TCPサーバ

import socket
server = socket.socket() #创建服务器套接字  # 生成一个socket对象(server)对象.方法,
 server.bind()      #把地址绑定到套接字
 server.listen()      #监听链接
 inf_loop:      #服务器无限循环
     conn,addr = server.accept() #接受客户端链接
     comm_loop:         #通讯循环
         conn.recv()/conn.send() #对话(接收与发送)
    con.close()    #关闭客户端套接字
 server.close()        #关闭服务器套接字(可选)

TCPクライアント

import socket
client =  socket.socket()   # 创建客户套接字
 client.connect()    # 尝试连接服务器
 comm_loop:        # 通讯循环
     client.send()/client.recv()    # 对话(发送/接收)
 client.close()            # 关闭客户套接字

シンプル通信

# 客户端
import socket

client=socket.socket()  #拿电话

client.connect(('127.0.0.1',8080))#拨号

client.send(b'hello!')  #对别人说话
data=client.recv(1024)  #听别人说话

print(data)
client.close()  #挂电话


# 服务端
import socket  # socket套接字

server=socket.socket()  # 买手机  生成一个套接字对象

server.bind(('127.0.0.1',8080))  # 插电话卡  绑定IP和端口

server.listen(5)  # 开机   半连接池,括号内表示同时接受的等待的连接个数,加上正在服务的一个,一共是6个,如果同时开启超过括号内指定的个数,会报错

conn,addr=server.accept()  #接听电话
# conn 双向通道(管道)  addr 客户端的IP和端口  ('192.168.12.68', 50451)
print(conn,addr)
'''
conn  -->  <socket.socket fd=228, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 52599)>
 addr  -->  ('127.0.0.1', 52599)  # 客户端的IP和port
'''

data=conn.recv(1024)  # 听别人说话
print(data)

conn.send(data.upper())  # #给别人回话

conn.close()  # 挂电话  关闭管道
server.close()  # 关机

リンクと通信ループサイクルプラス

# 服务端

#coding:utf-8
import socket
ip_port=('127.0.0.1',8889)#电话卡
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
s.bind(ip_port) #手机插卡
s.listen(5)     #手机待机


while True:                         #新增接收链接循环,可以不停的接电话
    conn,addr=s.accept()            #手机接电话
    # print(conn)
    # print(addr)
    print('接到来自%s的电话' %addr[0])
    while True:                         #新增通信循环,可以不断的通信,收发消息
        msg=conn.recv(BUFSIZE)             #听消息,听话

        # if len(msg) == 0:break        #如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生

        print(msg,type(msg))

        conn.send(msg.upper())          #发消息,说话

    conn.close()                    #挂电话

s.close()                       #手机关机


# 客户端

#_*_coding:utf-8_*_
import socket
ip_port=('127.0.0.1',8889)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect_ex(ip_port)           #拨电话

while True:                             #新增通信循环,客户端可以不断发收消息
    msg=input('>>: ').strip()
    if len(msg) == 0:continue
    s.send(msg.encode('utf-8'))         #发消息,说话(只能发送字节类型)

    feedback=s.recv(BUFSIZE)                           #收消息,听话
    print(feedback.decode('utf-8'))

s.close()  

UDPベースのソケット

最後にエラーが開始されません何のリンクは、UDPではありません

特徴:信頼できない、ないリンクは、パックをスティックではないだろう、何の双方向チャネルは、ヘッダは、あなたが空(TCPは、仕事の空立ち往生送信されません)に送信することができていない、プロトコルが同時サポートしています。

# udp服务端
 ss = socket()   #创建一个服务器的套接字
 ss.bind()       #绑定服务器套接字
 inf_loop:       #服务器无限循环
     cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
 ss.close()   
# udp客户端
cs = socket()   # 创建客户套接字
comm_loop:      # 通讯循环
    cs.sendto()/cs.recvfrom()   # 对话(发送/接收)
cs.close()                      # 关闭客户套接字

単純なUDPソケット通信

# udp服务端
#coding:utf-8
import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

udp_server_client.bind(ip_port)

while True:
    msg,addr=udp_server_client.recvfrom(BUFSIZE)
    print(msg,addr)

    udp_server_client.sendto(msg.upper(),addr)

    
# udp客户端
  
#coding:utf-8
import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:
    msg=input('>>: ').strip()
    if not msg:continue

    udp_server_client.sendto(msg.encode('utf-8'),ip_port)

    back_msg,addr=udp_server_client.recvfrom(BUFSIZE)
    print(back_msg.decode('utf-8'),addr)

UDPプロトコルのサポート同時

# 服务端
import socket

server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(('127.0.0.1',9017))

while True:

    data,addr=server.recvfrom(1024)#客户端发来的消息和
    server.sendto(data.upper(),addr)
    print('数据:',data.decode('utf-8'))#客户端的地址
    print('地址:',addr)
    
# 客户端
#启动服务端,然后对一个客户端启动多次,多个客户端并发运行,只是并发,但给客户的感觉是自己独占服务器

import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1',9017)

while True:

    client.sendto(b'hehe',server_address)
    data, server_addr = client.recvfrom(1024)
    print(data)

QQチャット(接続なしなぜならUDPは、複数のクライアントが同時になる通信サーバを話すことができます)

# udp服务端


#coding:utf-8
import socket
ip_port=('127.0.0.1',8081)
udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #买手机
udp_server_sock.bind(ip_port)

while True:
    qq_msg,addr=udp_server_sock.recvfrom(1024)
    print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))
    back_msg=input('回复消息: ').strip()

    udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
    

# udp客户端(重启多个客户端,模拟多人聊天)

#coding:utf-8
import socket
BUFSIZE=1024
udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

qq_name_dic={
    '狗哥alex':('127.0.0.1',8081),
    '瞎驴':('127.0.0.1',8081),
    '一棵树':('127.0.0.1',8081),
    '武大郎':('127.0.0.1',8081),
}


while True:
    qq_name=input('请选择聊天对象: ').strip()
    while True:
        msg=input('请输入消息,回车发送: ').strip()
        if msg == 'quit':break
        if not msg or not qq_name or qq_name not in qq_name_dic:continue
        udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])

        back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
        print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))

udp_client_socket.close()

スティックパック現象

TCPに基づいて第1のリモートコマンド実行プログラムする(:コマンド実行エラー2:1、LS 3実行します。ipconfigを実行します)

注意注意注意:

RES = subprocess.Popen(cmd.decode( 'UTF-8')、
殻= Trueを、
標準エラー= subprocess.PIPE、
STDOUT = subprocess.PIPE)

符号化システムは、それがWindowsの場合、被検者の現在の位置の結果に基づいて、その後れる(res.stdout.read)GBKが符号化され読み取る受信端で必要GBKデコードを使用します

そして唯一のパイプからの結果一度読むことができます

# TCP服务端

#coding:utf-8
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)
        
        
# CP客户端

#coding:utf-8
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='')

上記の手順は、実行時にソケットTCP、スティック包装に基づいて発生

私たちは、UDPベースのリモートコマンド実行プログラムを作ってみましょう

#_*_coding:utf-8_*_

__author__ = 'Linhaifeng'
from socket import *
import subprocess

ip_port=('127.0.0.1',9003)
bufsize=1024

udp_server=socket(AF_INET,SOCK_DGRAM)
udp_server.bind(ip_port)

while True:
    #收消息
    cmd,addr=udp_server.recvfrom(bufsize)
    print('用户命令----->',cmd)

    #逻辑处理
    res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdin=subprocess.PIPE,stdout=subprocess.PIPE)
    stderr=res.stderr.read()
    stdout=res.stdout.read()

    #发消息
    udp_server.sendto(stderr,addr)
    udp_server.sendto(stdout,addr)
udp_server.close()
from socket import *
ip_port=('127.0.0.1',9003)
bufsize=1024

udp_client=socket(AF_INET,SOCK_DGRAM)


while True:
    msg=input('>>: ').strip()
    udp_client.sendto(msg.encode('utf-8'),ip_port)

    data,addr=udp_client.recvfrom(bufsize)
    print(data.decode('utf-8'),end='')

上記の手順は、ソケット、UDPに基づいて、スティックのパッケージは、実行時に起こることはありません

スティックパッケージとは何ですか

送信側はK K送信データであってもよいし、受信側アプリケーションは、最初の3K又は6Kデータを引き出す、または一度だけデータの数バイトを撤回することも可能コースのデータを引き出すための2つのK 2 K、缶換言すれば、アプリケーションは、全体として、データを見て、またはそれは、ストリーム(ストリーム)、アプリケーションが表示されていないバイト数のメッセージであるため、TCPプロトコルが容易であり、ストリーム指向のプロトコルであります理由スティックパッケージの問題。UDPは、メッセージ指向のプロトコルであるが、各セグメントは、UDPメッセージは、メッセージのデータユニットを抽出する必要があり、アプリケーション内の任意の1バイトのデータを抽出し、それがTCPと異なっていることができません。ニュースを定義するには?他の側面は、使い捨て書き込み/メッセージなどのデータを送信してもよいこと、他方は関係なく、基礎となるタイルセグメントは、TCPプロトコル層が完了した後、メッセージ全体を構成するデータセグメントをソートするかを、メッセージを送信しない場合が理解されるべきカーネルバッファに提示。

サーバーにファイルをアップロードする例TCPソケットベースのクライアントの場合は、ファイルの内容を送信する送信されたバイトストリームのセクションに従っている、私はどこから始めれば、ファイルストリームのバイトで知りませんでした、受信機を見て、どこ停止します

受信機は、メッセージ間の限界を知らないので、いわゆるスティックパッケージの問題は、主に、タイム抽出によるデータのバイト数を知りません。

さらに、送信者スティックパッケージによる自体がTCPプロトコルによって引き起こされる、伝送効率を向上させるためにTCPは、送信者はしばしば、TCPセグメントを送信する前に十分なデータを収集します。連続送信データはほとんど必要ない場合は、通常、TCPの最適化に基づいたアルゴリズム、受信側は、粘着性のデータパケットを受信するように、データ伝送時間合成後のTCPセグメント外へ。

  1. TCP(トランスポート制御プロトコル、伝送制御プロトコル)接続指向ストリーム指向の、信頼性の高いサービスです。受信機と送信機(クライアントおよびサーバー)がソケットの11ペアを持っている必要があり、従って、複数のパケットを終了送信するために最適化法(Nagleアルゴリズム)を使用して、送信するために、他のより効率的な、受信側宛、時間は、小さな間隔及びデータのより小さな量のデータは、データの大きなブロックは、パケットに組み合わせます。このようにして、受信側では、違いを見分けるのは難しいですし、科学的な開梱メカニズムを提供しなければなりません。すなわち、無指向通信メッセージ・ストリームは、境界を保護されています。
  2. UDP(ユーザデータグラムプロトコル、ユーザデータグラムプロトコル)コネクション、メッセージ指向、効率的なサービスを提供することです。合わせた最適化アルゴリズムが原因UDP-多くのモードの支持体に、ブロックを使用しない、各UDPパケットの各UDPパケットの到着を記録するためにチェーン構造を使用skbuff(ソケットバッファ)の受信端そこメッセージ(情報源アドレスおよびポート)のヘッドになるように、受信側のために、プロセスを区別することは容易です。そのメッセージは、メッセージ指向の通信保護の境界です。
  3. 送受信されたメッセージはすべて、あなたが入力した場合でも、UDPデータグラムに基づいてジャミングプログラムを防ぐために、空のクライアントにメカニズムを扱うメッセージやサーバーを追加する必要と、空にすることはできませんので、TCPは、データストリームに基づいています空のコンテンツ(直接キャリッジリターン)は、それが空のメッセージではなかった、UDPプロトコルは、少し実験あなたはメッセージヘッダをパッケージ化するのに役立ちます

recvfromがブロックされているUDP、のrecvfrom(x)は一つだけのsendto(Y)でなければなりませんY> XのデータはそれがUDPパケットを固執しないことを意味している、失われた場合、完了した場合でも、すべてのxバイトのデータを終了それは信頼できない、データを失うことになります

TCPプロトコルのデータが失われない完全なパッケージを受信しませんでした、次の受信は、最後の受信を継続していきます、常にレシートACK時にバッファの内容をクリアします終了しました。データは信頼性があるが、パッケージに固執します。

スティックパッケージは、両方のケースで開催されます

I.

    发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
# 服务端
import socket
import time
server = socket.socket()
server.bind(('127.0.0.1',9999))
server.listen(5)

conn,addr = server.accept()
data1 = conn.recv(1024)
# time.sleep(0.1)
data2 = conn.recv(1024)
print(data1)
print(data2)
conn.close()
# 客户端
import socket

client = socket.socket()
client.connect(('127.0.0.1',9999))

client.send(b'hello')
client.send(b'hello')

'''
b'hellohello'
b''
'''
# 证明是由情况一当中发送数据小、时间间隔短造成的粘包问题,导入time模块,睡眠0.1s足够了,这样就不会产生粘包了。
# 服务端
import socket
import time
server = socket.socket()
server.bind(('127.0.0.1',9999))
server.listen(5)

conn,addr = server.accept()
data1 = conn.recv(1024)
time.sleep(0.1)
data2 = conn.recv(1024)
print(data1)
print(data2)
conn.close()
# 客户端
import socket

client = socket.socket()
client.connect(('127.0.0.1',9999))

client.send(b'hello')
client.send(b'hello')
'''
b'hello'
b'hello'
'''

シナリオ2

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

server = socket.socket()
server.bind(('127.0.0.1',9999))
server.listen(5)

conn,addr = server.accept()
data1 = conn.recv(3)
data2 = conn.recv(1024)
print(data1)
print(data2)
conn.close()

# 客户端
import socket

client = socket.socket()
client.connect(('127.0.0.1',9999))

client.send(b'hello!')
client.send(b'today is a  good weather!')
'''
b'hel'
b'lo!today is a  good weather!'
'''

開梱が起こります。

    当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。

TCPは信頼性の高い伝送であるなぜ、UDPは信頼性の低い伝送であります

    tcp在数据传输时,发送端先把数据发送到自己的缓存中,然后协议控制将缓存中的数据发往对端,对端返回一个ack=1,发送端则清理缓存中的数据,对端返回ack=0,则重新发送数据,所以tcp是可靠的

UDPと送信データ、ピアは確認応答メッセージは、したがって、信頼性の低いない返します

(バイトストリーム)とRECV(1024)を送信し、sendall

recvはキャッシュから外に1024バイトのデータで指定された1024年のこと

バイトストリームは、データが失われ、それがsendallとサイクルコールを送信し、送信されるバイトストリームのサイズは残りのバッファスペースよりも大きい場合、キャッシュは、端部にコンテンツを送信するためのプロトコルによって制御される、ヘキシル側キャッシュに送信することです、データが失われることはありません

パッケージの粘着性の問題を解決するための処理方法

簡単な方法(推奨されません)

問題の根は、受信機が転送される送信元のバイトストリームの長さを知らないということなので、ソリューション・スティックパッケージには、データを送信する前に、送信者を取得する方法、周りにある、バイトストリーム受信を可能にするために送信されます、独自の合計サイズその終わり、その後、死亡のフェッチ受信周期は、すべてのデータを受信しました

# 服务端
import socket,subprocess
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

s.bind(ip_port)
s.listen(5)
while True:
    conn,addr=s.accept()
    print('客户端',addr)
    while True:
        msg=conn.recv(1024)
        if not msg:break
        res=subprocess.Popen(msg.decode('utf-8'),shell=True,\
                            stdin=subprocess.PIPE,\
                         stderr=subprocess.PIPE,\
                         stdout=subprocess.PIPE)
        err=res.stderr.read()
        if err:
            ret=err
        else:
            ret=res.stdout.read()
        data_length=len(ret)
        conn.send(str(data_length).encode('utf-8'))
        data=conn.recv(1024).decode('utf-8')
        if data == 'recv_ready':
            conn.sendall(ret)
    conn.close()
# 客户端
import socket,time
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(('127.0.0.1',8080))

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

    s.send(msg.encode('utf-8'))
    length=int(s.recv(1024).decode('utf-8'))
    s.send('recv_ready'.encode('utf-8'))
    send_size=0
    recv_size=0
    data=b''
    while recv_size < length:
        data+=s.recv(1024)
        recv_size+=len(data)


    print(data.decode('utf-8'))

なぜお勧めしません。

送信バイトストリームの長さの送信前に、いくつかのバイトを送信する前に、本実施形態は、ネットワーク遅延のパフォーマンスコストを増幅して、ネットワークの伝送速度よりもはるかに速い速度を実行します

この方法は、(ヘッダを使用して構造体モジュールパッケージ)回帰しました

バイトのカスタム・ストリームに加え、次いで固定長ヘッダ、バイトストリームに含まれるヘッダ長、及び最後までペアを送信するが、受信側開始バッファ時の固定長ヘッダを削除し、実際のデータを取ります

stuctモジュール

モジュールは、数、バイトの固定長に変換などのタイプであってもよいです

struct.pack( 'i' は、LEN(任意のデータ))

struct.error: '私は' フォーマットは-2147483648 <=番号が必要です<= 2147483647#これは範囲があります

パッケージの粘着性の問題を解決するために考えます

import json,struct
#假设通过客户端上传1T:1073741824000的文件a.txt

#为避免粘包,必须自定制报头
header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T数据,文件路径和md5值

#为了该报头能传送,需要序列化并且转为bytes
head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并转成bytes,用于传输

#为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
head_len_bytes=struct.pack('i',len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度

#客户端开始发送
conn.send(head_len_bytes) #先发报头的长度,4个bytes
conn.send(head_bytes) #再发报头的字节格式
conn.sendall(文件内容) #然后发真实内容的字节格式

#服务端开始接收
head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式
x=struct.unpack('i',head_len_bytes)[0] #提取报头的长度

head_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式
header=json.loads(json.dumps(header)) #提取报头

#最后根据报头的内容提取真实的数据,比如
real_data_len=s.recv(header['file_size'])
s.recv(real_data_len)

ムービーファイルの例を送ります

私たちは、送信される辞書は、実際のデータの詳細が含まれている、ヘッダ辞書を作ることができ、その後、JSONシリアライズは、その後、(自分自身を使用する4十分)4に詰めバイトのシーケンス後のデータ長を打ちました

送信時:
第1の送信の最初の長さ
次に再符号化ヘッダとは、コンテンツを送信し
、最終的に本当のコンテンツを送信します

受信時:
構造体を用いて取り出した上ハンドヘッダの長さが、
除去、ヘッダの長さに基づいてコンテンツを仕込み、次いで、デシリアライズ復号
デシリアライズ結果から抽出されるデータへのアクセスの詳細を、次に実際のデータの内容がフェッチ

# 服务端
port socket
import struct
import json

server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)
while True:
    conn, addr = server.accept()

    while True:
        try:
            header_len = conn.recv(4)
            # print(header_len)
            header_len = struct.unpack('i', header_len)[0]
            header_dic = conn.recv(header_len)
            real_dic = json.loads(header_dic.decode('utf_8'))
            total_size = real_dic.get('file_size')
            recv_size = 0
            with open(real_dic.get('file_name'), 'wb')as f:
                while recv_size < total_size:
                    data = conn.recv(1024)
                    f.write(data)
                    recv_size += data
                    # f.flush()
                print('上传成功')
        except ConnectionResetError as e:
            print(e)
            break
    conn.close()
  
# 客户端
import  os , json ,struct, socket
client=socket.socket()
client.connect(('127.0.0.1',8081))
while True:

    movie_dir=r'F:\python脱产10期视频\第一阶段\day03\视频'
    movie_list=os.listdir(movie_dir)
    for index,movie in enumerate(movie_list,1):
        print(index,movie)
    choice=input('请选择要上传的电影:').strip()
    if choice.isdigit():
        choice=int(choice)-1
        if choice in range(0,len(movie_list)):
            file_path=os.path .join(movie_dir,movie_list[choice])
            file_size=os.path.getsize(file_path)

            d={'file_name':movie_list[choice],'file_size':file_size,'介绍':'内容非常重要'}
            json_d=json.dumps(d)
            json_byte=json_d.encode('utf-8')
            header=struct.pack('i',len(json_byte))
            client.send(header)
            client.send(json_byte)
            with open(file_path,'rb')as f:
                for line in f:
                    client.send(line)
        else:
            print('not in range')
    else:
        print('must be a number')

socketserverモジュール(並行処理)

TCPソケットに基づいて、鍵は2つのサイクル、サイクルのリンク、通信サイクルであります

socketserverモジュールの分類

サーバ・クラス(問題を解決するためのリンク)

1)関連リンク付き

ベースサーバー
TCPServerの
UDPServer

2)マルチスレッドの同時実行

TreadingTCPServer
TreadingUCPServer

3)マルチプロセスの同時実行に基づいて

ForkingTCPServer
ForkingUCPServer

(通信の問題を解決するために)要求クラス

BaseRquestHandler
StreamRquestHandler
DatagramRquestHandler

手順を使用します

1)クラスは、ハンドル方法をオーバーロード、クラスリクエスト処理、要求クラスの継承を作成し、
サーバベース、サーバアドレスおよび着信要求の2)として;
3)server.serve_forever()#開始処理要求をリスニング

ハンドルメソッド

self.request.send()
self.request.recv()
(IP、ソート)= self.client_addressです()

並行性を導入socketserver TCPモジュール

#服务端必须满足三点:1.绑定一个固定的IP和端口;2.一直对外提供服务,稳定运行;3;能够支持并发
import socketserver
#自定义类用来处理通信循环
class MyTCPhandler (socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            try:
                print('只要有客户端连接,就会触发服务端的handle方法运行')
                data=self.request.recv(1024)
                if len(data)==0:break
                print('收到客户端消息:',data)
                self.request.send(data.upper())
            except ConnectionResetError as f:
                print(f)
                break
        self.request.close()
if __name__ == '__main__':
    '''只要有客户端连接,会自动交给自定义类中的handle方法处理'''
    server=socketserver.ThreadingTCPServer(('127.0.0.1',9555),MyTCPhandler)
    server.serve_forever() #链接循环
#客户端(同时启动多个客户端连接服务端实现并发效果)
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',9555))

while True:
    msg=input('>>>:').strip()
    client.send(msg.encode('utf-8'))

    data=client.recv(1024)
    print(data.decode('utf-8'))

 UDPモジュールの同時実行socketserverの紹介

# 服务端
import socketserver
class MyUdphandler(socketserver.BaseRequestHandler):

    def handle(self):
        data,sock=self.request
        print(data)
        sock.sendto(data.upper(),self.client_address)


if __name__ == '__main__':

    server=socketserver.ThreadingUDPServer(('127.0.0.1',8586),MyUdphandler)
    server.serve_forever()
# 客户端
import socket
client=socket.socket(type=socket.SOCK_DGRAM)
while True:

    client.sendto(b'hello',('127.0.0.1',8586))

    data,server_addr=client.recvfrom(1024)
    print(data.decode('utf-8'))

おすすめ

転載: www.cnblogs.com/zhangchaocoming/p/11705016.html