请教windows平台上多线程对同一个套接字的send/recv操作 是否存在问题

82 views
Skip to first unread message

Zhou dingpin

unread,
Feb 13, 2009, 7:20:16 AM2/13/09
to dev4s...@googlegroups.com
大家好!
       在windows平台上,如果多个线程对同一个套接字发起send/recv操作,那么数据是否会被破坏? 我写了个程序测试了下,似乎winsock是做了线程安全的,不知道理解的是否正确,请各位大虾指教。

附程序代码:
[code]
DWORD WINAPI SendThread(LPVOID lpParam)
{
    printf("enter send thread.\n");
    SOCKET s = (SOCKET)lpParam;
    for (int i = 0; i < 50; i++)
    {
        char buf[32] = "SendThread==SendThread";

        if (SOCKET_ERROR == send(s,buf,strlen(buf),0))
        {
            printf("send failed. error:%d\n", ::GetLastError());
        }
        else
        {
            printf("[%u]send sucssed. buf:%s\n",::GetCurrentThreadId(), buf);
        }
    }

    return 0;
}

DWORD WINAPI RecvThread(LPVOID lpParam)
{
    printf("enter recv thread.\n");
    SOCKET s = (SOCKET)lpParam;
    char buf[1024] = {0};
    for (int i = 0; i < 50; i++)
    {
        if (SOCKET_ERROR != recv(s,&buf[0],1024,0))   
        {
            printf("[%u]received data:%s\n",GetCurrentThreadId(), buf);
        }
        else
        {
            printf("received failed. error:%d\n",::GetLastError());
        }
    }

    return 0;
}
[/code]

运行结果如下:(中间数据有省略)
[2288]received data:SendThread==SendThreadSendThread==SendThreadSendThread==Send
ThreadSendThread==SendThreadSendThread==SendThreadSendThread==SendThreadSendThre
ad==SendThread
[1940]received data:SendThread==SendThreadSendThread==SendThread
[1832]send sucssed. buf:SendThread==SendThread
[1836]send sucssed. buf:SendThread==SendThread
[1460]send sucssed. buf:SendThread==SendThread
[884]received data:SendThread==SendThreadSendThread==SendThreadSendThread==SendT
hreadSendThread==SendThreadSendThread==SendThread
[1716]received data:SendThread==SendThreadSendThread==SendThreadSendThread==Send
Thread
[1672]send sucssed. buf:SendThread==SendThread
[3416]send sucssed. buf:SendThread==SendThread
[1924]received data:SendThread==SendThreadSendThread==SendThreadSendThread==Send
ThreadSendThread==SendThreadSendThread==SendThread
[2288]received data:SendThread==SendThreadSendThread==SendThreadSendThread==Send
ThreadSendThread==SendThreadSendThread==SendThreadSendThread==SendThreadSendThre
ad==SendThread
[1940]received data:SendThread==SendThreadSendThread==SendThread
[1832]send sucssed. buf:SendThread==SendThread
[1836]send sucssed. buf:SendThread==SendThread
[1460]send sucssed. buf:SendThread==SendThread
[1716]received data:SendThread==SendThreadSendThread==SendThreadSendThread==Send
Thread

丁锐

unread,
Feb 13, 2009, 7:24:25 AM2/13/09
to dev4s...@googlegroups.com

丁锐

unread,
Feb 13, 2009, 7:24:50 AM2/13/09
to dev4s...@googlegroups.com
关注你这个问题的答案,我也想了解一下


在2009-02-13,"Zhou dingpin" <zhoud...@gmail.com> 写道:
网易邮箱,中国第一大电子邮件服务商

sunway

unread,
Feb 13, 2009, 7:46:18 PM2/13/09
to 高性能网络编程邮件列表
1。winsock 肯定是线程安全的。
2。线程安全的情况下,为了保证逻辑处理的可靠性大多数情况下依然需要同步某些操作,比如read操作,保证接受到的包是完整的等等。
> ThreadSendThread==SendThreadSendThread==SendThreadSendThread==SendThreadSen-dThre
> ad==SendThread
> [1940]received data:SendThread==SendThreadSendThread==SendThread
> [1832]send sucssed. buf:SendThread==SendThread
> [1836]send sucssed. buf:SendThread==SendThread
> [1460]send sucssed. buf:SendThread==SendThread
> [884]received
> data:SendThread==SendThreadSendThread==SendThreadSendThread==SendT
> hreadSendThread==SendThreadSendThread==SendThread
> [1716]received
> data:SendThread==SendThreadSendThread==SendThreadSendThread==Send
> Thread
> [1672]send sucssed. buf:SendThread==SendThread
> [3416]send sucssed. buf:SendThread==SendThread
> [1924]received
> data:SendThread==SendThreadSendThread==SendThreadSendThread==Send
> ThreadSendThread==SendThreadSendThread==SendThread
> [2288]received
> data:SendThread==SendThreadSendThread==SendThreadSendThread==Send
> ThreadSendThread==SendThreadSendThread==SendThreadSendThread==SendThreadSen-dThre

Kouga

unread,
Feb 13, 2009, 8:23:51 PM2/13/09
to dev4s...@googlegroups.com
MSDN上标记的是ThreadSafe,但是你自己得保证不要让自己的逻辑出错了哦~

Zhou dingpin

unread,
Feb 13, 2009, 8:34:46 PM2/13/09
to dev4s...@googlegroups.com
你好,请问你是从send/recv的API说明看到它是线程安全的吗? 在MSDN中哪一篇文档有说明呢?请指教,谢谢

2009/2/14 Kouga <ncw...@gmail.com>
MSDN上标记的是ThreadSafe,但是你自己得保证不要让自己的逻辑出错了哦~




Zhou dingpin

unread,
Feb 13, 2009, 8:35:39 PM2/13/09
to dev4s...@googlegroups.com
 你好,你所说的recv需要同步,我的理解是,如果winsock是线程安全的,那么可以理解为多线程在send的时候,内部缓冲区的数据是有序的。 而多线程在recv的时候,每次copy的数据是不能保证刚好处于数据边界的是吧? 但是多个线程最终recv的数据拼起来的话应该是有序的,我理解的对么?

2009/2/14 sunway <sunh...@gmail.com>

1。winsock  肯定是线程安全的。
2。线程安全的情况下,为了保证逻辑处理的可靠性大多数情况下依然需要同步某些操作,比如read操作,保证接受到的包是完整的等等。

  高性能网络编程
 http://groups.google.com/group/dev4server
-~----------~----~----~----~------~----~------~--~---


Nil

unread,
Feb 15, 2009, 6:27:39 AM2/15/09
to 高性能网络编程邮件列表
对TCP socket来说,应该是不能多线程并行IO的,UDP可以同时进行

On 2月13日, 下午8时35分, Zhou dingpin <zhouding...@gmail.com> wrote:
> 你好,你所说的recv需要同步,我的理解是,如果winsock是线程安全的,那么可以理解为多线程在send的时候,内部缓冲区的数据是有序的。
> 而多线程在recv的时候,每次copy的数据是不能保证刚好处于数据边界的是吧? 但是多个线程最终recv的数据拼起来的话应该是有序的,我理解的对么?
>

> 2009/2/14 sunway <sunhui...@gmail.com>

Kouga

unread,
Feb 15, 2009, 11:42:08 AM2/15/09
to dev4s...@googlegroups.com
正解。如果需要多个线程对数据拼装——是否是在用完成端口模式做S端?

Note  When issuing a blocking Winsock call, such as send, recv, select, accept, or connect function calls, Winsock may need to wait for a network event before the call can complete. Winsock performs an alertable wait in this situation, which can be interrupted by an asynchronous procedure call (APC) scheduled on the same thread, and thereby create unspecified results. Do not issue blocking Winsock function calls without being certain that the current thread is not waiting inside another blocking Winsock function call; doing so results in unpredictable behavior.

MSDN链接:ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.WIN32COM.v10.en/winsock/winsock/recv_2.htm



2009/2/14 Zhou dingpin <zhoud...@gmail.com>



--
签名是什么东西??

sunway

unread,
Feb 16, 2009, 12:49:49 AM2/16/09
to 高性能网络编程邮件列表
TCP是双工的,在WIN下是可以并行I/O的。只是一般情况下为了保证逻辑的可靠,对单连接读写做了同步。

> > > -~----------~----~----~----~------~----~------~--~---- 隐藏被引用文字 -
>
> - 显示引用的文字 -

水的影子

unread,
Feb 16, 2009, 3:06:25 AM2/16/09
to 高性能网络编程邮件列表
像你这样一个线程只发,一个线程只收的,当然没问题了。
要是2个线程发,或者2个线程收,就有问题了。

On 2月13日, 下午8时20分, Zhou dingpin <zhouding...@gmail.com> wrote:

Zhou dingpin

unread,
Feb 16, 2009, 4:03:06 AM2/16/09
to dev4s...@googlegroups.com
哦,我没有写清楚。 我是开了5个发送线程5个接收线程来测试的。我理解sunway的意思了。你是指发送和接收的时候因为线程的切换可能发出去/接收到的东西逻辑上是无序的 是么?

这个问题比较清楚了 谢谢各位

2009/2/16 水的影子 <zengk...@gmail.com>

Nil

unread,
Feb 16, 2009, 7:38:14 AM2/16/09
to 高性能网络编程邮件列表
重新看了一下有关资料,你是对的,呵呵

Nil

unread,
Feb 16, 2009, 7:47:39 AM2/16/09
to 高性能网络编程邮件列表
顺便问一下,那是不是用boost asio的话,应该对同一个socket的读写分别用两个不同的strand来同步以达到双工?

On 2月16日, 下午1时49分, sunway <sunhui...@gmail.com> wrote:

杨光

unread,
Mar 3, 2009, 2:27:41 AM3/3/09
to dev4s...@googlegroups.com
多线程同时SEND/RECV 同一个SOCKET数据会出現问题.


 
2009/2/16 Nil <nil....@gmail.com>

周龙亭

unread,
Mar 15, 2009, 6:55:15 AM3/15/09
to dev4s...@googlegroups.com
Please see: http://tangentsoft.net/wskfaq/intermediate.html#threadsafety

3.10 - Is Winsock thread-safe?
The Winsock specification does not mandate that a Winsock implementation be thread-safe, but it does allow an implementor to create a thread-safe version of Winsock.

Bob Quinn says, on this subject:

?"WinSock, any implementation, is thread safe if the WinSock implementation developer makes it so (it doesn't just happen)." 
?"I don't know of any implementations from Microsoft (or any other vendors) that are not thread safe." 
?"If a WinSock application developer creates a multi-threaded application that shares sockets among the threads, it is that developer's responsibility to synchronize activities between the threads." 
By "synchronize activities", I believe Bob means that it may cause problems if, for example, two threads repeatedly call send() on the same socket. There is no guarantee in the Winsock specification about how the data will be interleaved in this situation. Similarly, if one thread calls closesocket() on a socket, it must somehow signal other threads using that socket that the socket is now invalid.

Anecdotal evidence suggests that one thread calling send() and another thread calling recv() on a single socket is safe on recent Microsoft stacks at least.

Instead of multiple threads accessing a single socket, you may want to consider setting up a pair of network I/O queues. Then, give one thread sole ownership of the socket: this thread sends data from one I/O queue and enqueues received data on the other. Then other threads can access the queues (with suitable synchronization).

Applications that use some kind of non-synchronous socket typically have some I/O queue already. Of particular interest in this case is overlapped I/O or I/O completion ports, because these I/O strategies are also thread-friendly. You can tell Winsock about several OVERLAPPED blocks, and Winsock will finish sending one before it moves on to the next. This means you can keep a chain of these OVERLAPPED blocks, each perhaps added to the chain by a different thread. Each thread can also call WSASend() on the block they added, making your main loop simpler.

3.11 - If two threads in an application call recv() on a socket, will they each get the same data?
No. Winsock does not duplicate data among threads.

Note that if you do call recv() at the same time on a single socket from two different threads, havoc may result. See the previous question for more info.

3.12 - Is there any way for two threads to be notified when something happens on a socket?
No. If two threads call WSAAsyncSelect() on a single socket, only the thread that made the last call to WSAAsyncSelect() will receive further notification messages. Similarly, if two threads call WSAEventSelect() on a socket, only the event object used in the last call will be signaled when an event occurs on that socket. You also can't call WSAAsyncSelect() on a socket in one thread and WSAEventSelect() on that same socket in another thread, because the calls are mutually exclusive for any single socket. Finally, you cannot reliably call select() on a single socket from two threads and get the same notifications in each, because one thread could clear or cause an event, which would change the events that the other thread sees.

avalon xu

unread,
Mar 15, 2009, 7:04:39 AM3/15/09
to dev4s...@googlegroups.com
用异步io可以多线程同时收发的 只要使用不同wsbuf就可以 不过那样的话,拼包就很麻烦了。

2009/3/15 周龙亭 <njdra...@gmail.com>

Adam Jiang

unread,
Mar 15, 2009, 10:52:36 PM3/15/09
to dev4s...@googlegroups.com
Instead of multiple threads accessing a single socket, you may want to consider setting up a pair of network I/O queues. Then, give one thread sole ownership of the socket: this thread sends data from one I/O queue and enqueues received data on the other. Then other threads can access the queues (with suitable synchronization).

This is the right answer!

--
Adam Jiang
------------------------------------------
e-mail:jiang...@gmail.com

chencb060716

unread,
Apr 14, 2009, 11:27:58 AM4/14/09
to dev4s...@googlegroups.com
 
需求是:
server是嵌入式设备对于连接数要求不高(最大支持4个client,每个client最多17个连接),一个server有多路视频数据(已经是编码后的数据了,每个通道的数据量在128kbps~2048kbps之间),每个client可以请求多路视频数据,但每个client只能连接一个server,另外client还可以对server进行相关的参数配置。
 
目前是这样设计的一个生产者,生产多种产品,多个消费者,可以同时消费多种产品,并且为每个消费者的每种消费产品都建立一个队列(每个队列对应一个socket,因为是视频数据不能混合接受),所有消费者都加入到一个统一的map中。
但我自己的实现,同步开销太大了,大家给点建议。另外这种是否是比较好的开源模型可以利用,多谢
 


Linker

unread,
Apr 15, 2009, 7:21:49 AM4/15/09
to dev4s...@googlegroups.com
少用锁

--
Sent from my mobile device

Regards,
Linker Lin
linker...@gmail.com

Kouga

unread,
Apr 19, 2009, 10:49:42 AM4/19/09
to dev4s...@googlegroups.com
同步开销太大?是否是你的对象class封装的东西太多导致的?如果是,你可以将自己的class纵向分割一下,就可以避免大量锁了。

2009/4/15 Linker <linker...@gmail.com>



--
签名是什么东西??
Reply all
Reply to author
Forward
0 new messages