この記事の例では、参照用にudp送信イメージを実現するために、pythonの特定のコードを共有しています。具体的な内容は次のとおりです。
まず、UDPの動作モードを理解する必要があります
サーバーの場合、最初にIPとポートをバインドします。このマシンをテストするときは、マシンのプライベートIPである127.0.0.1を使用できます。1024より大きいポート番号はカスタマイズされているため、1024より大きいポート番号を使用してから、クライアントを受信します。データ、処理、返品
クライアントの場合、UDPは接続を確立する必要はなく、受信したかどうかに関係なく送信するだけなので、サーバーのIPアドレスとポート番号に直接情報を送信し、応答を待つことができます。
送信されるデータはバイナリストリームデータであるため、送信する必要のあるデータをバイナリストリームにエンコードし、渡した後にデコードする方法を見つけてください。ここでは、opencvを使用して画像をnumpy配列形式に読み取り、エンコードして送信しました。 、そして最後にそれを受け取った後にそれをデコードします。
画像全体を一度に転送する方法について説明しましょう。受け入れられるパラメータ設定が大きく、画像が比較的小さいため、実装は比較的簡単です。
1つ目は、受信、表示、応答を実現するサーバースクリプトです。
udp_sever.py
# - *- coding: utf-8-*-import socket
import cv2
import numpy as np
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# バインディングポート:
s.bind(('127.0.0.1',9999))print('Bind UDP on 9999...')while True:
# データを受信する:
data, addr = s.recvfrom(400000)print('Received from %s:%s.'% addr)
# デコード
nparr = np.fromstring(data, np.uint8)
# 画像numpyにデコード
img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
cv2.imshow('result',img_decode)
cv2.waitKey()
reply ="get message!!!"
s.sendto(reply.encode('utf-8'), addr)
cv2.destroyAllWindows()
クライアントスクリプト、写真の送信と応答の受信を実現
udp_client.py
# - *- coding: utf-8-*-import socket
import cv2
import numpy as np
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
img = cv2.imread('/home/xbw/jupyter_notebook/0.jpg')
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = np.array(img_encode)
data = data_encode.tostring()
# データを送る:
s.sendto(data,('127.0.0.1',9999))
# データを受信する:print(s.recv(1024).decode('utf-8'))
s.close()
コードの理解を容易にするために、画像をバイナリに入れてから画像に戻します
import numpy as np
import cv2
img = cv2.imread('0.jpg')
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = np.array(img_encode)
str_encode = data_encode.tostring()
# print(str_encode)
nparr = np.fromstring(str_encode, np.uint8)
img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
cv2.imshow('result',img_decode)
cv2.waitKey()
cv2.destroyAllWindows()
写真をまとめて転送する
長い間取り組んだ後、ようやく写真をまとめて転送する方法がわかりました。まず、写真を転送するために必要なメモリの長さを知る必要があります。そうしないと、受信を停止するタイミングがわかりません。次に、ファイルヘッダーを追加して、サーバーにコードの長さを通知する必要があります。フロー。
実現のアイデアは、クライアントが最初にコードストリームの長さを含むファイルヘッダーを送信し、長いintタイプ番号を使用し、最初にstruct.packでパックして送信し、次に画像のコードストリームをループで送信する必要があるということです。
次に、サーバーは最初にファイルヘッダーを受信し、画像ストリームの長さを確認してから、決定された長さのストリームを周期的に受信し、最後にそれを画像にデコードします。
実装コードは次のとおりです。
1つ目はクライアントスクリプトです
udp_client.py
# - *- coding: utf-8-*-import socket
import cv2
import numpy as np
import struct
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 画像を読み、バイナリバイト形式にエンコードします
img = cv2.imread('/home/xbw/jupyter_notebook/0.jpg')
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = np.array(img_encode)
data = data_encode.tostring()
# ファイルヘッダーを定義し、構造にパッケージ化します
fhead = struct.pack('l',len(data))
# ファイルヘッダーを送信する:
s.sendto(fhead,('127.0.0.1',9999))
# 画像コードストリームを周期的に送信する
for i inrange(len(data)//1024+1):if1024*(i+1)len(data):
s.sendto(data[1024*i:],('127.0.0.1',9999))else:
s.sendto(data[1024*i:1024*(i+1)],('127.0.0.1',9999))
# 応答データを受信する:print(s.recv(1024).decode('utf-8'))
# シャットダウン
s.close()
次に、サーバーは
udp_sever.py
# - *- coding: utf-8-*-import socket
import cv2
import numpy as np
import struct
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# バインディングポート:
s.bind(('127.0.0.1',9999))print('Bind UDP on 9999...')while True:
# ファイルヘッダーを受信します。ファイルヘッダーの長さは、calcsize関数によって決定されます。ここで、recvfromはUDPメッセージを受信し、recvはTCPメッセージを受信することに注意してください。
fhead_size = struct.calcsize('l')
buf,addr = s.recvfrom(fhead_size)if buf:
# ここでの結果はタプルなので、値を取り出します
data_size = struct.unpack('l',buf)[0]
# 画像のビットストリーム長のビットストリームを受信します
recvd_size =0
data_total = b''while not recvd_size == data_size:if data_size -recvd_size 1024:
data,addr = s.recvfrom(1024)
recvd_size +=len(data)else:
data,addr = s.recvfrom(1024)
recvd_size = data_size
data_total += data
# data, addr = s.recvfrom(400000)print('Received')
# reply ='Hello, %s!'% data.decode('utf-8')
# s.sendto(reply.encode('utf-8'), addr)
# 受信したコードストリームをnumpy配列にデコードし、画像を表示します
nparr = np.fromstring(data_total, np.uint8)
img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
cv2.imshow('result',img_decode)
cv2.waitKey()
# 回答
reply ="get message!!!"
s.sendto(reply.encode('utf-8'), addr)
cv2.destroyAllWindows()
以上が基本的な実装です。少し学んだ後、ようやくUDP送信の本質をマスターしました。
1つ目は、クライアントとサーバーの動作メカニズムを決定することです。
クライアント:最初にバインドせずにソケットオブジェクトを定義し、次にメッセージを送信するIPアドレスとポートを指定します。次に、recvfromが使用されている場合、応答の待機を常にブロックします(これは非常に便利です。役割は、相手がそれを受信することを確認してから、新しいものを送信することです。メッセージ、送信頻度の問題を考慮する必要はありません)、ループで送信するために前にしばらくTrueを追加します。非常に大きなメッセージが含まれる場合は、分割して送信できます。トリックは、最初にファイルヘッダーを送信することです。大きさ(stuctライブラリを使用するにはファイルヘッダーをお勧めします。前の例を参照)、ファイルコンテンツを送信する場合は、送信するたびに反対側を1回送信する必要があるため、必ず周期的に送信してください。2048バイトのコンテンツを送信する場合は、反対側が設定されます。 1024を受信するたびに、残りの1024は、次回の受信を待つ代わりに失われます。送信されるバイナリコードストリームもあります。**現在、コードストリームに変換するために使用する方法は、次のとおりです。**画像はopencvを使用してバイナリに変換され、次にnumpyに変換され、次にtostringに変換されます。ファイルヘッダーについては、サーバーが受信しやすいように、どのくらいのメモリを占有しているかを正確に知る必要があります。stuctライブラリが使用されます。内部のpack、unpack、calcsizeの3つの機能は非常に使いやすいです。送信するときは、送信するデータをパックします。リストや辞書などは、ファイルの内容として、少し用途の広いjsonを使用します。最初にjson.dumpsをjsonタイプに変換し、次にバイナリにエンコードしてから送信します。
**サーバー:**最初にソケットオブジェクトを定義し、クライアントが見つけられるようにIPアドレスとポートをバインドしてから、メッセージが受信されるのを待ちます。メッセージを受信した後、メッセージを処理して返信し、クライアントのrecvfromと連携して、受信頻度が一定であることを確認します。常にメッセージを受信します。しばらくTrueが必要です。受信したメッセージはバイナリストリームであるため、デコードする必要があります。上記のエンコード方式のデコードは、実際にはエンコード方式の逆の操作です。画像は、opencv、最初にnp.fromstring、次にcv2.imdecode(data、cv2.IMREAD_COLOR)でデコードされます。ファイルヘッダーを受け取るためのちょっとしたコツがあります。struct.calcsizeを使用してファイルヘッダーの長さを決定し、この長さのコードストリームのみを受け取り、それを解凍します。ここで、unpackはタプルです。 jsonの場合、デコードは最初にデコードし、次にjson.loadsをデコードします。これは、上記のエンコードの逆の操作です。
次に、ハイエンド操作で、同じスクリプトが複数のプロセスで機能するため、threading.Threadを使用して複数のプロセスを作成します。複数のサーバーを作成し、それらを異なるプロセスに割り当てるという考え方です。IPアドレスは同じでもかまいません。ポート番号が同じではない場合、同じスクリプトで並行して作業できます。UDPは接続を確立する必要がないため、ここではTCPとは異なります。
次に、実装したソースコードを添付します。サーバースクリプトには2つのプロセスがあり、1つはクライアント1の画像を受信し、もう1つはクライアント2のリストを受信します。
サーバ
udp_server.py
# - *- coding: utf-8-*-import socket
import cv2
import numpy as np
import struct
import threading
import json
# IPアドレス、2つのサーバーポート番号を設定します
dest_ip ='127.0.0.1'
img_port =9999
msg_port =6666
# サーバー1の処理および応答機能、画像の受信、表示、および応答
def receive_img(rec_img):while True:
# データを受信する:
fhead_size = struct.calcsize('l')
buf,addr = rec_img.recvfrom(fhead_size)if buf:
data_size = struct.unpack('l',buf)[0]print(data_size)
recvd_size =0
data_total = b''while not recvd_size == data_size:if data_size -recvd_size 1024:
data,addr = rec_img.recvfrom(1024)
recvd_size +=len(data)else:
data,addr = rec_img.recvfrom(1024)
recvd_size = data_size
data_total += data
# data, addr = rec_img.recvfrom(400000)print('Received')
# reply ='Hello, %s!'% data.decode('utf-8')
# rec_img.sendto(reply.encode('utf-8'), addr)
nparr = np.fromstring(data_total, np.uint8)
img_decode = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
cv2.imshow('result',img_decode)
cv2.waitKey(100)
reply ="get message!!!"
rec_img.sendto(reply.encode('utf-8'), addr)
# cv2.destroyAllWindows()
# サーバー2の機能、メッセージの受信、出力、返信
def receive_msg(rec_msg):while True:
msg_data ,msg_addr = rec_msg.recvfrom(1024)
msg_str = msg_data.decode('utf-8')
msg = json.loads(msg_str)print(msg)
reply ='get the msg'
rec_msg.sendto(reply.encode('utf-8'),msg_addr)
rec_msg.close()
# main関数は、サーバーを作成し、ポートをバインドし、2つのプロセスを作成して実行し、上記の2つの関数を呼び出します。
def main():
# ソケットを作成する
rec_img = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
rec_msg = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# ローカルアドレスポートをバインドする
rec_img.bind((dest_ip, img_port))
rec_msg.bind((dest_ip, msg_port))
# プロセスの作成
t_recimg = threading.Thread(target=receive_img, args=(rec_img,))
t_recmsg = threading.Thread(target=receive_msg, args=(rec_msg,))
# プロセスを開始します
t_recimg.start()
t_recmsg.start()print('プログラムは正常に実行されます! ! !')if __name__ =='__main__':main()
クライアント1
udp_client_1.py
# - *- coding: utf-8-*-import socket
import cv2
import numpy as np
import struct
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
cap = cv2.VideoCapture(0)
# cap.set(3,320)
# cap.set(4,240)while True:if cap.isOpened():
flag, img = cap.read()
# img = cv2.imread('/home/xbw/jupyter_notebook/0.jpg')
img_encode = cv2.imencode('.jpg', img)[1]
data_encode = np.array(img_encode)
data = data_encode.tostring()
# 定義ファイルヘッダー
fhead = struct.pack('l',len(data))
# ファイルヘッダーとデータを送信する:
s.sendto(fhead,('127.0.0.1',9999))for i inrange(len(data)//1024+1):if1024*(i+1)len(data):
s.sendto(data[1024*i:],('127.0.0.1',9999))else:
s.sendto(data[1024*i:1024*(i+1)],('127.0.0.1',9999))
# 返信を受け取る:
cv2.waitKey(1)print(s.recv(1024).decode('utf-8'))
s.close()
クライアント2
udp_client_2.py
import socket
import cv2
import numpy as np
import struct
import json
import time
# ソケットを定義する
send_msg = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# ターゲットIPアドレスとポート番号を設定します
target_ip ='127.0.0.1'
target_port =6666
# データを送信し、応答を待つ
while True:
data =[0,0,0,1]
data_str = json.dumps(data)
send_msg.sendto(data_str.encode(),(target_ip,target_port))
time.sleep(0.01)print(send_msg.recv(1024).decode('utf-8'))
以上が本稿の内容ですので、皆様のご勉強に役立てていただければ幸いです。
Recommended Posts