在多线程代码中,如果有多个线程同时尝试向一个TCP套接字发送数据,会发生什么情况?它们的数据会混合在一起还是不同的线程最终会逐个发送数据?
在多线程代码中,如果有多个线程同时尝试向一个TCP套接字发送数据,会发生什么情况?它们的数据会混合在一起还是不同的线程最终会逐个发送数据?
这取决于您使用哪些基元来将数据提交到套接字。
如果您正在使用write(2)
、send(2)
、sendto(2)
或sendmsg(2)
,并且消息的大小足够小,可以完全适应套接字的内核缓冲区,则整个写操作将作为一个块发送,而不会出现其他数据插入其中。
如果您正在使用fwrite(3)
(或任何其他更高级别的缓冲IO抽象),则有可能您的数据将被发送而没有其他数据插入其中,但我不会依赖这种行为。
我无法说明sendfile(2)
的行为。我想说sendfile(2)
操作会在该套接字上的任何其他write(2)
请求之前“写入”文件的全部内容,但是我读过的文档没有提到这一点,因此最好不要做任何意义上的“原子性”假设。
最安全的机制是仅有单个线程提交数据到套接字。
正如前面的回答所述:“最安全的机制是只允许单个线程向套接字提交数据。”
但如果你想让多个线程调用read/write,你必须自己保证线程安全。我已经写了一个包装器来处理这个问题,它可以在send和recv周围做到这一点。
在“发送”和“接收”方面,它们是线程安全的,因为它们不会导致崩溃,但不能保证该数据不会与其他线程发送的数据“混合”。由于我们很可能不希望出现这种情况,我们必须阻塞直到所有线程的数据被确认发送或接收完成,或者发生错误。这样一个长时间的阻塞调用可能会有问题,所以请确保你在能够处理长时间阻塞操作的线程上执行此操作。
对于linux:
#include <sys/types.h>
#include <sys/socket.h>
#include <map>
#include <mutex>
/* blocks until the full amount of bytes requested are read
* thread safe
* throws exception on error */
void recv_bytes(int sock, char* buf, int len, int flags){
static std::map<int, std::mutex> mtx;
std::lock_guard<std::mutex> lock(mtx[sock]);
int bytes_received = 0;
while (bytes_received != len){
int bytes = recv(sock, buf + bytes_received, len - bytes_received, flags);
//error check
if (bytes == -1){
throw std::runtime_error("Network Exception");
}
bytes_received += bytes;
}
}
/* blocks until the full amount of bytes requested are sent
* thread safe
* throws exception on error */
void send_bytes(int sock, char* buf, int len, int flags){
static std::map<int, std::mutex> mtx;
std::lock_guard<std::mutex> lock(mtx[sock]);
int bytes_sent = 0;
while (bytes_sent != len){
int bytes_s0 = send(sock, buf, len, flags);
if (bytes_sent == -1) {
throw std::runtime_error("Network Exception");
}
bytes_sent += bytes_s0;
}
}