4

我必须编写使用 Unix 数据报套接字的简单客户端-服务器应用程序。客户端可以根据服务器的请求向服务器发送信息,或者他可以根据自己的请求从服务器接收信息。

我有一个想法,一个线程将等待用户输入来确定我们要发送到服务器的请求,而另一个线程将只等待来自服务器的套接字上的消息,如果它是我们请求的消息,它将将其写入标准输出,如果是服务器请求线程将写入服务器请求的内容。我会使用互斥锁,这样两个线程就不会同时写入同一个套接字。

我的问题是,如果一个线程从某个套接字读取,同时另一个线程将使用同一个套接字发送数据,那么套接字将如何表现,是否安全?或者我也应该在这种情况下使用互斥锁?

4

2 回答 2

10

内核结构通常以线程安全的方式构建;插座也不例外。如果您应该担心任何事情,那不是使用套接字和线程的安全性,而是您的程序的逻辑。

另外,我想提一下流套接字是全双工的,这意味着读/写可以保证安全地同时发生,这怎么可能发生?内核为您锁定或确保您可以同时进行发送和接收。

对于全双工参数:
http ://www.kernel.org/doc/man-pages/online/pages/man2/socket.2.html 对于内核结构是线程安全的:
我找不到你的链接支持这一点,但我有 99% 的把握。

PS如果有疑问,测试这件事可能会有所帮助
编辑:
如果我所说的有问题,请在投票前发表评论。


EDIT.2:
在这里您可以发现 POSIX 标准指定其所有功能都必须是线程安全的,但第 2.9.1 节
http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02中定义的列表除外.html

于 2012-06-02T15:22:59.357 回答
1

在 linux 和 windows 中,send() 和 recv() 调用(通常用于 TCP,但我认为也可以与 UDP 一起使用)可能会提前返回,而不会发送您想要发送的全部数据。这将导致线程问题,因为您的消息将被拆分。

我发现使用以下属性包装 recv() 和 send() 函数是最简单的:

  • 只有在我们请求的所有数据都写入套接字后才返回
  • 线程安全

然后在我会使用发送/接收的所有地方使用包装器。

这是包装器的代码(随意更改错误处理):

//linux (must change SOCKET types to int)
//#include <sys/socket.h>

//windows 
//#include <Winsock2.h>
//#include <ws2tcpip.h>

//blocks until the full amount of bytes requested are read
//thread safe
//throws exception on error
void recv_bytes(SOCKET socket, char* buf, int len, int flags){

    static std::mutex mtx;

    mtx.lock();

    int bytes_received = 0;

    while (bytes_received != len){

        int bytes = recv(socket, buf + bytes_received, len - bytes_received, flags);

        //error check
        if (bytes == 0){
            throw std::exception("Network Exception");
        }

        bytes_received += bytes;

    }

    mtx.unlock();

}



//blocks until the full amount of bytes requested are sent
//thread safe
//throws exception on error
void send_bytes(SOCKET socket, char* buf, int len, int flags){

    static std::mutex mtx;

    mtx.lock();

    int bytes_sent = 0; 

    while (bytes_sent != len){

        int bytes_s0 = send(socket, buf, len, flags);

        if (bytes_sent == SOCKET_ERROR) {
            mtx.unlock();
            throw std::exception("Network Exception");
        }

        bytes_sent += bytes_s0;

    }

    mtx.unlock();
}
于 2017-05-17T18:51:18.060 回答