我不能生成一个新线程来处理新的UDP“连接”?

时间:2017-10-05 16:40:32

标签: c multithreading sockets server udp

我知道UDP是无连接的,但是我可以生成一个新线程来处理连接到我的服务器的新UDP客户端吗?

我正在尝试这样做:

void *handle_connection(void *param)
{
    // read data
    ret = recvfrom(..,0,..);
}


// Wait for connection using MSG_PEEK
ret = recvfrom(.., MSG_PEEK,..);

// New thread to handle client connection
pthread_t conn_thread;
ret = pthread_create(..., handle_connection);

有没有办法为每个客户端建立UDP“连接”并分叉一个新线程来处理它,就像使用DTLS时由OpenSSL完成一样?

使用OpenSSL,我可以使用DTLS_listen()等待UDP客户端连接,然后启动一个新线程,将UDP套接字传递给该线程并使用通常的读/写操作。为了实现这个目的,你必须在udp套接字上做一些额外的bind()和connect()调用。

我在这里尝试了相同的方法,最后为从服务器上的客户端收到的每个数据报打开了一个新线程。

1 个答案:

答案 0 :(得分:2)

我认为可以做到。但我不确定保证是什么。

基本理念是:

  1. 创建服务器套接字,在其上设置SO_REUSEADDR,将其绑定到 服务器端口,并等待接收数据。
  2. recvfrom为您提供下一个收到的数据报和来自的地址 它被发送了。
  3. 将初始数据报和地址传递给新线程。
  4. 在新线程中,创建一个新套接字(再次使用SO_REUSEADDR并绑定它)。现在connect新的套接字到对等地址。
  5. 返回第2步
  6. 这里的想法是你总是保留一个“未连接”的套接字。当您获得新的对等体时,您将创建一个连接到该对等体的新套接字。当套接字专门连接到远程对等体时,内核将从该对等体向该套接字传递传入的数据报。否则,它会将数据报传递给未连接的套接字。

    我不是100%确定的事情是:OS 是否保证仅将后续数据报传送到连接的套接字而不是未连接的套接字?它确实有效,它似乎可以在我的linux盒子上工作 - 请参阅下面的python3代码(可以使用nc -u localhost 9999作为客户端)。

    #!/usr/bin/env python3
    from threading import Thread
    import socket
    
    def client_thread(addr, msg):
        # Create a new socket and connect it to the peer.
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('', 9999))   # Maybe omit -- see below
        sock.connect(addr)
        # Maybe set SO_RCVTIMEO to cause timeout on receive and 
        # cause client thread to bail?
        while True:
            sock.send(msg)
            msg = sock.recv(1024)
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('', 9999))
    
    threads = []
    
    while True:
        # Receive datagram on unconnected socket
        msg, addr = sock.recvfrom(1024)
    
        # Send connected socket and datagram
        th = Thread(target=client_thread, args=(addr, msg))
        th.start()
        threads.append(th)
    

    你可能会有一些方法可以最终杀掉子线程,这样如果你的客户端消失,它们就不会永远停留。

    或者,您可以在client_thread中省略绑定新套接字,这将导致内核为新套接字中的本地地址选择未使用的端口,但是客户端需要注意该地址从中收到了初始响应,并将未来的数据报直接发送到该地址。这将完成同样的事情,并在许多方面更清洁。

    客户端可能是这样的(再次使用python进行演示):

    #!/usr/bin/env python3
    import socket
    
    # Initial request sent to well-known port
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    addr = ('localhost', 9999)
    
    for n in range(5):
        print('Send to {}'.format(addr))
        sock.sendto(b'Hello', addr)
        response, addr = sock.recvfrom(1024)
        # Subsequent requests sent to whoever responded to last request
        print('Got response {} from {}'.format(response, addr))