Linux套接字和多线程程序

时间:2012-06-02 09:45:08

标签: linux multithreading sockets

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

我知道一个线程将等待用户输入,它确定我们要发送给服务器的请求,而另一个线程只是等待来自服务器的套接字上的消息,如果是我们的消息请求将它写入标准输出,如果是服务器请求线程将写入所请求的服务器。我会使用互斥锁,因此两个线程不会同时写入同一个套接字。

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

2 个答案:

答案 0 :(得分:10)

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

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

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

P.S如果有疑问,测试这个东西可能会有所帮助 编辑:
如果我所说的错误,请在投票前对其进行评论。


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

答案 1 :(得分:1)

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

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

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

然后只使用我用过send / recv。

的所有地方的包装器

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

//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();
}