现在正在写一个客户端的socket程序,多线程通信,但不是windows平台,操作系统没有提供异步消息,所以得用select检测,然后发送异步消息给制定线程。详细说明如下:
线程A处理应用发来消息,进行socket的connect,send,recv,disconnect,dns等的消息,使用非阻塞。比如connect,线程A调用connect后就返回,然后线程B调用select轮询所有系统中已经创建的socket(加到FD_SET里),当FD_ISSET检测到可写的时候,表明connect成功,然后发消息给线程A,执行相应的回调函数。线程B在系统启动时就已经创建,线程B会对所有已经创建的socket进行select,来检测每个socket是否可写,写读。
但是本人对select的理解不深,如果线程B检测到某个socket可读,代表哪些情况?如果可写,又代表哪些情况?我能明白的是,比如对于connect,如果发送成功,这时select会检测到可写。对于send,检测到可写代表什么意思?
请大家帮忙,最好能提供详细的select的资料,说明可读可写各自代表的意思,包括出错的检测等。谢谢!还有一个问题是,比如对于recv,如果检测到可读,就让线程A读取消息,线程B并不读取,在线程A读取之前,会一直检测到读消息的,我该如何区分第二次读到消息,是A还未读取的,还是A已经读取过后收到的新消息?
说得有点乱,但是感觉这里真的挺复杂。windows平台,系统已经提高了异步消息,一旦connect成功,会收到消息,一旦收到disconnect消息,也会收到消息,然后处理就是了。我现在的平台不支持这样的操作,所以,我得实现这样的异步机制,感觉好难啊。主要还是对select的理解不够。看了不少资料,感觉还是理解不透。

解决方案 »

  1.   

    http://www.diybl.com/course/3_program/vc/vc_js/20090307/159189.html
      

  2.   

    FD_SET的时候你带的第一个参数就已经指定了是select哪个socket的了,你可以多次FD_SET然后就能select多个socket,然后你通过FD_ISSET你就知道是哪个socket是可读还是可写了,曾经写过一个UDP的select,貌似FD_SET绑定了writeset的话,对应的FD_ISSET会一直走
      

  3.   

    windows 网络编程   其中的一章 看下就可以了
      

  4.   

    去下载点源码自己分析一下《Visual C++网络程序设计实例详解》
      

  5.   

    谢谢VisualEleven的资料,没错,如果FD_ISSET返回>0,表明有数据读,但是有数据读,并不表示可以使用recv,因为connect成功一样FD_ISSET返回真,同样,如果socket发生错误,可读可写都返回真,这才是我的问题所在。
    另外,我的线程只判断是否可以可写,具体操作得发送给专门处理数据的线程。自己并不recv,或者send,在数据没有recv之前,会一直检测到可读,这是我的第二个问题。另外,如果服务器主动中断连接,这时也会检测到可读,但实际上什么也读不到的。这又是一个情况。
      

  6.   

    windows的代码就不用看了,windows已经替用户实现了异步通信,一旦connect,send,recv成功,会发送异步消息。我现在就是用select在检测这些,然后发送消息给用户,相当与在实现异步消息。所以,我的问题实际上就是,一旦检测到某个socket可读可写,如何判断到底代表哪种情况?这是第一个问题。第二个问题就是,一旦确定属于某种情况,比如recv,在用户recv之前,这个socket会一直检测到可读,我该如何区别这个可读是用户还没来得及recv,还是新的可以recv的消息。个人感觉,还是select的具体含义没搞清楚,比如,检测到某个socket可写,不考虑出错的情况,到底代表socket缓冲区已经有空闲,还是代表用户的数据真正发送成功?
      

  7.   

    select的 检测可读是很傻的
    只要你注册了 可读检查
    只要缓冲区里头有数据 select就会返回
    你的意思我懂 就是在select返回可读的时候 发送一个消息给上层让上层调用recv来读数据对吧
    但是因为是多线程的原因 可能用户还没调用recv的时候 就又执行到select哪里了对吧。。
    很明确的告诉你。。你用select的话 这种程序结构无法实现最好是读了 直接把读的数据以消息的形式发送给上层。。
    硬要这个弄的话 可以换模型 linux 用epoll的边缘模式 windows用完成端口吧- -
      

  8.   

    select最好别检查 可写事件
    select 认为只要socket的发送缓冲区没有满 就是可写的
      

  9.   

    select判断关闭是 可写 但是recv返回0
      

  10.   

    觉得你区别可recv但用户还根本没recv与recv了部分但还有部分没什么意义
    协议只需根据收到的数据来组包 包头文不是会有长度吗?
    长度不够就先存起来 等有足够的数据再返回好了
      

  11.   

    如果你的代码不是商用的话可以考虑用用cmu的ipc
    貌似boost里面也挺供相关的库
      

  12.   

    非常感谢yhlovehx的回复。select检测到可读后,当前线程读,然后以消息的形式发送给上层,这也在我的考虑之中。现在这个问题可以简化为:如何检测connect成功,如何检测socket可以发送数据,如何检测socket有数据需要接受,如何检测socket已经被服务器关闭。
    connect成功:select会检测到可写。相对容易些
    可以send:select会检测到可写。(只要写缓冲区没满,严格来说,是写缓冲区的空余空间大于吃水线water-,就是可以send)
    可以recv:select会检测到可读。(只要读缓冲区不为空,严格来说,是读缓冲区的占用空间超过吃水线water-,就是可以recv)
    disconnect: select 检测到可读,但recv返回0.
    现在初步考虑,使用状态机和回调函数(类似与阻塞)来区别未读和新消息,区别connect成功和可以send。只是对出错的情况,暂时还不太清楚,应该如何考虑。因为出错,select既检测到可读,也检测到可写。烦!
      

  13.   

    回调函数执行完毕之前,select得不到运行,所以,即使一直可读一直可写,也没关系了。只是线程的效率降低了。这不是一个好的解决办法,还在考虑中。
      

  14.   

    select很好用的.不仅仅是否可以收/发, 也可以测到是否接通, 对方是否已经断开等等. 如果你想让CPU不那累, select是个不错的选择. 
      

  15.   

    对于出错的处理很简单
    如果套接字出错
    select会返回 不管你注册了写监听或者读监听
    如果是对方关闭了 你调用recv会返回0 调用send会正常(但是逻辑上不可能收到数据不做判断就send对吧)
    如果是套接字出问题 会返回-1 。。直接closesocket就行了呢
    还有select模型一般不建议监听可写事件0  0!!
      

  16.   

    套接字I/O模型:让winsock对I/O进行管理,主要用与同时管理一个或多个套接字模型
    Select模型:避免在套接字调用过程中被无辜“锁定”的应用程序,采取一种有序的方式,同时进行对多个套接字的管理。
    Select函数:用于判断套接字上是否存在数据,或者能否向一个套接字写入数据。
    设计select的目的:防止应用程序在套接字处于锁定模式时,在一次I/O绑定调用(如期send/recv)中,被迫进入锁定状态,同时防止套接字处于非锁定模式中时,产生WSAEWOULDBLOCK错误。除非满足事先参数定义的条件,否则select会在进行I/O操作时锁定
    Int  select(int  nfds. //可忽略保证与早期版本兼容
    Fd_set  FAR*  readfds,    //用于检查可读性
    Fd_set  FAR*  writefds,    //检查可写性
    Fd_set  FAR*  exceptfds,  //用于例外数据
    Const  struct  timeval  FAR*  timeout)
    中间三个参数任意两个都可以为空,但至不有一个不能为空,在不为空的的集合中要包含至少一个套接字句柄,否则select便没有任何东西可以等待。最后一个timeval结构用于设定select最多等待I/O操作多久的时间,若timeval为NULL则select会无限期锁定或停顿下去。直到至少有一个描述符符合指定条件后结束。
    Struct  timeval {  long  tv_sec;   //以秒为单位
    long  tv_usec;  //以毫秒为单位
     }
    Select 成功完成后:会在fd_set结构中,返回刚好在末完成有I/O操作的所有套接字句柄的总量
    用select对套接字进行侦视之前必须设置好fd_set结构,然后再调用select,winsock提供下列宏来针对I/O活动,对fd_set进行处理与检查:
    FD_CLR(s  , *set):从set中删除套接字
    FD_ISSET(s ,  *set):检查S是否set集合中的一名成员,若是则返回TRUE
    FD_SET(s ,  *set ):将套接字S加入集合set中
    FD_ZERO( *set):将set初始化为空集合
    Select:返回后会修改每一个fd_set结构,删除那些不存在待决I/O操作的套接字句柄。
      

  17.   

    问题已经基本解决。
    1)首先用select对出错检测。检测到错误即退出
    2)线程A执行send并且返回EWOULDBLOCK后,此socket状态置sending;执行connect并且返回EINPROGRESD后,此socket状态置connecting.只有状态在sending或者connecting的时候才用select检测可写。
    3)最后用select监测可读.
    4)如果可以recv,但recv返回0,说明远端断开连接。