UDPとソケットプログラミング
- 関連testコマンド
- ポートUDPのウィンドウをアクティブにするかどうかを検索します。
netstart -anp udp | find "9999"
netstart -anbp udp | findstr 9999
- Linuxでサーバーに送信されたデータ
echo "123abc" | nc -u 172.0.0.1 9999
- ポートUDPのウィンドウをアクティブにするかどうかを検索します。
UDPサーバー側のプログラミング
- UDPサーバサイドのプログラミングプロセス
- ソケットオブジェクトを作成します。socket.SOCK_DGRAM
- バインドIPとポート、バインド()メソッド
- データ送信
- 受信データ、socket.recvfromタプル(列アドレス)を取得するために、([、フラグ] bufsizeは)
- 送信データ、socket.sendto(文字列、アドレス)情報がのアドレスに送信され
- リリースリソース
import logging
import sys
import socket
logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)d %(message)s",stream=sys.stdout,level=logging.INFO)
server = socket.socket(type=socket.SOCK_DGRAM) #创建一个基于UDP的socket
server.bind(("127.0.0.1",3999)) #立即绑定一个udp端口
# data = server.recv(1024) #阻塞等待数据
data,radde = server.recvfrom(1024) #阻塞等待数据(value,(ip,port))
logging.info("{}-{}".format(radde,data))
server.sendto("{} server msg = {}".format(server.getsockname(),data).encode(),radde)
server.close()
-
UDPクライアントオーサリングプロセス
- ソケットオブジェクトを作成します。socket.SOCK_DGRAM
- 送信データ、socket_sendto(文字列、アドレス)情報がアドレスに送信され
- 受信データ、socket.recvfromタプル(列アドレス)を取得するために、([、フラグ] bufsizeは)
- リリースリソース
-
最初のバージョン
import logging
import sys
import socket
logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)d %(message)s",stream=sys.stdout,level=logging.INFO)
client = socket.socket(type=socket.SOCK_DGRAM)
raddr = "127.0.0.1",3999
client.connect(raddr) #connect方法会自动分配一个本地的UDP地址,和设置UDP的链接对象raddr地址
logging.info(client)
client.send(b"hello") #由于使用了connect方法,所以不指定终端也能发送
client.sendto(b"why",raddr) #也可以使用指定地址发送
data,radde = client.recvfrom(1024)
logging.info("{}-{}".format(radde,data))
client.close()
- 第二のバージョンは、接続対象を使用しないでください
import logging
import sys
import socket
logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)d %(message)s",stream=sys.stdout,level=logging.INFO)
client = socket.socket(type=socket.SOCK_DGRAM)
raddr = "127.0.0.1",3999
client.sendto(b"hello",raddr)
logging.info(client)
data,laddr = client.recvfrom(1024)
logging.info("{}-{}".format(data,laddr))
client.close()
- 注意:UDPプロトコルには、リンクではありませんので、あなただけのどちらかの端、サーバーに送信されたクライアントデータは、サーバが存在するかのような問題ではありませんすることができます。
- UDPプログラミングバインド、送信、接続のsendto、RECV、のrecvfrom方法
- UDPソケットオブジェクトの作成後は、ローカルアドレスとポートを占有しません。
方法 | 説明 |
---|---|
バインド(LADDR) | あなたは、ローカルアドレスとポートLADDRを指定することができ、すぐに取り組をLADDRなり、(IP、PROT) |
接続(RADDR) | ランダムローカルポートLADDR、リモートアドレスとポートを割り当てバインドRADDR、タプルRADDR、(IP、PROT) |
sendto(MSG、RADDR) | あなたは、遠位端にすぐLADDRローカルアドレスとポート、および指定されたデータを占めることができます。唯一のローカルポートが結合して、のsendtoは、任意のリモートにデータを送信することができ 、送信するデータのmsg#。バイトタイプ。 タプル(IP、PROT)RADDR#リモートアドレスとポートの構成 |
(MSG)を送ります | とを有する方法を接続する必要はローカルポートデータから、既に使用され得るRADDR遠位指定 バイトは、メッセージタイプを送信する必要のMSG# |
RECV(BUFFERSIZE) | 、ローカルポートを占有データのリターンを受け入れる、BUFFERSIZEすることは法律によって要求された後、バッファのサイズを指定します |
recvfrom(BUFFERSIZE) | ローカルポート、受信したデータを取り込むために、法律によって必要とピアタプルのアドレスを返した後(MSG、RADDR) BUFFERSIZEは、バッファサイズを指定します。 |
グループチャットの行使-udpバージョン
- サーバ・コード
"""
author:xdd
date:2019-06-17 09:20
"""
import logging
import sys
import socket
import threading
import datetime
logging.basicConfig(format="%(asctime)s %(threadName)s %(thread)d %(message)s",stream=sys.stdout,level=logging.INFO)
class ChatUDPServer:
def __init__(self,ip="127.0.0.1",port=3999,timeout=10):
self.socket = socket.socket(type = socket.SOCK_DGRAM)
self.laddr = ip,port
self.event = threading.Event()
self.timeout = timeout
self.clients = {}
def start(self):
socket = self.socket
socket.bind(self.laddr)
threading.Thread(target=self.run,name="run").start()
def run(self):
socket = self.socket
clients = self.clients
while not self.event.is_set():
try:
data,raddr = socket.recvfrom(1024)
except:
continue
utctime = datetime.datetime.now().timestamp()
if data.strip() == b"by": #如果用户自己发送by表示要退出
self.clients.pop(raddr)
continue
clients[raddr] = utctime
if data.strip() == b"^hh^": #如果是心跳,就忽略
continue
msg = "[{}] {}".format(raddr,data)
logging.info(msg)
outclient = [] #记录超时的链接地址
for cr,tm in clients.items():
if 0 <= utctime - tm <= self.timeout:
socket.sendto(msg.encode(),cr)
else:
outclient.append(cr)
for cr in outclient: #超时后删除
self.clients.pop(cr)
def stop(self):
try:
self.event.set()
self.socket.close()
finally:
self.clients.clear()
@classmethod
def main(cls):
chserver = cls()
chserver.start()
while True:
cmd = input(">>>>")
if cmd.strip() == "quit":
chserver.stop()
threading.Event().wait(1)
break
logging.info(threading.enumerate())
logging.info(chserver.clients)
ChatUDPServer.main()
- UDPクライアントコードのバージョン
"""
author:xdd
date:2019-06-17 10:26
"""
import logging
import sys
import socket
import threading
logging.basicConfig(format="%(asctime)s %(thread)d %(threadName)s %(message)s",stream=sys.stdout,level=logging.INFO)
class UdpClient:
def __init__(self,ip="127.0.0.1",prost = 3999,heartbeattime = 5):
self.socket = socket.socket(type=socket.SOCK_DGRAM)
self.raddr = ip,prost
self.event = threading.Event()
self.heartbeattime = heartbeattime
def start(self):
self.socket.connect(self.raddr)
self.send("hello")
threading.Thread(target=self.heartbeat,name="heartbeat").start()
threading.Thread(target=self.run,name="client").start()
#发送心跳包,保持链接
def heartbeat(self):
while not self.event.wait(self.heartbeattime):
self.send("^hh^")
logging.info("心跳结束")
def run(self):
while not self.event.is_set():
try:
data,raddr = self.socket.recvfrom(1024)
except:
continue
logging.info("[ {} msg ] {}".format(raddr,data))
def send(self,msg):#发送消息
socket = self.socket
socket.send(msg.encode())
def stop(self):#停止
self.event.set()
self.socket.close()
@classmethod
def main(cls):
client = cls()
client.start()
while True:
cmd = input(">>>")
if cmd.strip() == "quit":
client.stop()
break
else:
client.send(cmd)
UdpClient.main()
- サーバ・コード
- ハートビートハートビートACK機構または機構の増加。これらのメカニズムは、また時にTCP通信を使用することができます。
- 心拍情報は、タイミング端の他方の端部に送られ、各時間データは、一般的に良好です。困難になろうとしてハートビート間隔大会。
- すなわち、ACK応答確認は、受信したメッセージの他端の終了後に戻されます。
- ハートビートメカニズム
- 一般的に、定期的にサーバーへのクライアントは、サーバーがACK必要とするクライアントに応答しない、あなただけのクライアントがライン上で生きている記録する必要があります。
- サーバーがクライアントに送信タイミングされている場合は、一般的な必要性は、クライアントがackを受信しない場合、サーバはその情報を削除し、生きているACK応答でクライアントを表現します。この実装は、以下で、より複雑です。
- また、あまり使用双方向のハートビートを作ったことができます。
UDPアプリケーションプロトコル
- UDPは、以下の仮定に基づいているコネクションレスプロトコルで、次のとおりです。
- ネットワークは十分です
- メッセージの損失ではないでしょう
- パケットは順不同ではありません
- しかし、ローカルエリアでは、我々は、パケットロスを保証することはできませんし、パケット到着が順序である必要はありません。
- シナリオ
- ビデオ、オーディオ伝送、一般的には、いくつかのパケットではなく、大きな問題を失い、これらの画像のほとんどを失った、言葉を聞いて、言葉が解決するために再送信することができます。大規模なデータ収集、例えばために送られたセンサデータは、数十人を失った、データの何百もの問題ではありません。
- DNSプロトコル、小さなコンテンツデータは、パケットが結果を照会することができるであろう、いかなる障害、損失、再要求解像度がありません。
- 一般的には、UDPの性能はTCPよりも良いですが、機会の高い信頼性要件またはTCPプロトコルを選択します。