Socket programming with UDP

Socket programming with UDP

  • Related test command
    1. Find whether to activate windows udp ports:
      • netstart -anp udp | find "9999"
      • netstart -anbp udp | findstr 9999
    2. data sent to the server under linux
      • echo "123abc" | nc -u 172.0.0.1 9999

UDP server-side programming

  • UDP server-side programming process
    udp_001
  1. Create a socket object. socket.SOCK_DGRAM
  2. Bind IP and Port, bind () method
  3. transfer data
    • Receiving data, socket.recvfrom (bufsize [, flags]), to obtain a tuple (string, address)
    • Transmission data, socket.sendto (string, address) information is sent to an address of a
  4. Release resources
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 client authoring process

    1. Create a socket object. socket.SOCK_DGRAM
    2. Transmission data, socket_sendto (string, address) information is sent to an address
    3. Receiving data, socket.recvfrom (bufsize [, flags]), to obtain a tuple (string, address)
    4. Release resources
  • The first version

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()

udp_002

  • The second version, do not use the connect target
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_003

  • Note : UDP protocol is no link, so you can only either end, such as client data sent to the server, the server exists or not does not matter.
  • UDP programming bind, connect, send, sendto, recv, recvfrom method
  • After the UDP socket object creation, no occupying a local address and port.
method Explanation
bind(laddr) You can specify a local address and port laddr, will take up immediately, laddr a tuple, (ip, prot)
connect(raddr) Randomly assigned a local port laddr, remote address and port binds raddr, raddr a tuple, (ip, prot)
sendto(msg,raddr) You can occupy immediately laddr local address and port, and the specified data to the distal end. Only with local port binding, sendto can send data to any remote
data msg # to be sent. bytes type.
a tuple (ip, prot) raddr # remote address and port composition
send(msg) And with the need to connect method can be used already from the local port data to the raddr distal specified
msg # bytes need to be sent a message type
recv(buffersize) After required by law to occupy the local port, accept the return of data, buffersize specify a buffer size
recvfrom(buffersize) After required by law to take up a local port, the received data and returns the address of the peer-tuple (MSG, RADDR)
buffersize specify a buffer size.

Exercise -UDP version of Group Chat

  • Server code
"""
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 client code version
"""
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()
  1. Server code
    • Increased heartbeat heartbeat ack mechanism or mechanisms. These mechanisms can also be used when TCP communication.
    • Heartbeat information is sent to the other end of the timing end, each time data is generally better. Heartbeat interval convention trying to be difficult.
    • i.e. ack response confirmation is returned after the end of the other end of the received message.
  • Heartbeat mechanism
    1. Generally the client regularly to the server, the server does not respond to client needs ack, you only need to record the client is still alive on the line.
    2. If the server is timed sent to the client, the general need to represent a client in response ack alive, if the client does not receive ack, the server remove their information. This implementation is more complex, with less.
    3. Can also have made two-way heartbeat, less used.

UDP application protocol

  • UDP is a connectionless protocol, which is based on the following assumptions:
    1. Network is good enough
    2. The message will not loss
    3. Packets are not out of order
  • However, even in the local area, we can not guarantee no packet loss, and packet arrival is not necessarily in order.
  • Scenarios
    1. Video, audio transmission, in general, lose some packets, not a big problem, lost most of these images, hear words, words can be re-sent to solve. Massive data acquisition, the sensor data sent to e.g., lost dozens, hundreds of data does not matter.
    2. DNS protocol, a small content data, a packet will be able to query a result, there is no disorder, loss, re-request resolution.
  • In general, UDP performance is better than TCP, but the high reliability requirements of the occasion or to choose TCP protocol.

Guess you like

Origin blog.csdn.net/u013008795/article/details/92588745