Socket.Select()函数使用不当引发的问题(关于套接字在线程间传递是否可行的验证)

来源:互联网 发布:语义分割数据集 编辑:程序博客网 时间:2024/05/08 09:49

使用环境:VS2010 C#

做东西的时候碰到一个问题:

在后台中,创建了一个监听线程,用来监听是否有连接到监听地址的请求,如果有,则加入监听套接字集中;(这样的话,一个线程就可以接收N个套接字的信息)

创建了一个接收线程,对监听套接字集中的套接字进行消息接收,并将该消息显示出来;

其中,监听套接字集中有与服务器连接的套接字srvCommSock

出错状况:

类似下图所示


即,后台只收到了连接请求,但是却接收不到消息

发送端则显示成功发送了消息,使用的是TCP/IP协议,所以是安全的

但是消息跑哪儿了呢?

分析原因:

刚开始的时候毫无头绪,后来请教了下高手,说可能说线程A创建的套接字在线程B中可能访问不了(之后验证是可以的)

而且单步执行的时候,发现确实是有问题的。新加进来的套接字属性Socket.Connected为false(后来发现该属性不能作为套接字是否连接的依据,详细见文章Socket.Connected不能在调试时作为套接字是否连接的判断依据

为了验证,做了一个简单的测试程序

测试代码如下:

监听线程代码:

       private void _lstnThreadProc()        {            //for check            Console.WriteLine("_lstnThreadProc线程开始执行,监听其他用户的连接请求");            //开始监听            try            {                while (true)                {                    Socket commSock = m_lstnSock.Accept();                    //for test                    Console.WriteLine("接收到其他用户的连接请求\n");                    //添加到监听套接字集中                    m_readSet.Add(commSock);                }//end of while            }//end of try            catch (SocketException e)            {                m_lstnSock.Close();                Console.WriteLine(e.Message);            }        }
接收线程代码:

        private void _threadProc()        {            //for check            Console.WriteLine("_threadProc线程启动,准备接收数据信息\n");            byte[] recvBuf = new byte[1024];            while (true)            {                //获取监听套接字集                ArrayList readSet = m_readSet;                int count = readSet.Count;                //for test                 Console.WriteLine("_threadProc:监听集中有{0}个监听对象\n", count);                if (count <= 0)                {                    continue;                }                try                {                    //阻塞等待                    Socket.Select(readSet, null, null, -1);                    for (int nIter = 0; nIter < count; nIter++)                    {                        Socket sock = (Socket)readSet[nIter];                        //表示有消息接收                        int nRecv = sock.Receive(recvBuf);                        //正常处理                        if (nRecv > 0)                        {                            string recvMsg = ASCIIEncoding.ASCII.GetString(recvBuf, 0, nRecv);                            Console.WriteLine("接收到的字节数为{0}消息如下\n{1}", nRecv, recvMsg);                            continue;                        }                        //其他用户与自己的连接断开                        else                        {                            Console.WriteLine("与其他客户的连接断开\n");                            m_readSet.Remove(sock);                            continue;                        }//end of else                    }//end of for                }//end of try                 catch (SocketException e)                {                    Console.WriteLine(e.Message);                    continue;                }            }//end of while(1)        }

但是刚才在做实例的时候是可以执行的

正确执行的显示:


如果是示例可运行的话,那么原程序应该也没有问题呀

后来想起来,Socket.Select()方法的最后一个参数为-1时,表示阻塞执行的,而且初始化时,把与服务器通信的套接字srvCommSock加入到了套接字集中

这才分析得通:初始监听套接字集中有srvCommSock,而服务器没有消息发送过来,所以程序阻塞在了这里,后面的消息接收不到

Socket.Select()函数原型如下:

public static void Select(IList checkRead,IList checkWrite,IList checkError,int microSeconds)

Parameters

checkRead
Type: System.Collections.IList
An IList of Socket instances to check for readability. 
checkWrite
Type: System.Collections.IList
An IList of Socket instances to check for writability. 
checkError
Type: System.Collections.IList
An IList of Socket instances to check for errors. 
microSeconds
Type: System.Int32
The time-out value, in microseconds. A -1 value indicates an infinite time-out.

问题解决:

将Socket.Select(readSet, null, null, -1);中的最后一个参数修改为0,即检测完如果没有消息就返回

需要解决的问题:

该示例虽然能正确执行,但是在_threadProc()函数中的while(true)循环会一直执行,所以会导致高CPU使用率。

合理的应该是监听线程_lstnThreadProc()函数中检测到连接之后,触发_threadProc()执行,这样的话就通过线程间的同步降低了CPU使用率

原创粉丝点击