Select ()使用说明及教程

12 篇文章 0 订阅

最近项目开发中使用到TCP通信,虽然以前编过简单的TCP的Server和Client程序,但是对于如果并发处理多个客户端没有多想过,上网查了一下,使用select()来实现比较方便,但是没找到完整的例子,下面的例子比较完整,所以保存供以后忘了在看看。


select()简述(来自baidu百科)

确定一个或多个套接口的状态,如:需要则等待。
#include <winsock.h>
int PASCAL FAR select( int nfds, fd_set FAR* readfds, fd_set FAR* writefds, fd_set FAR* exceptfds, const struct timeval FAR* timeout);
nfds:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
readfds:(可选)指针,指向一组等待可读性检查的套接口。
writefds:(可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针,指向一组等待错误检查的套接口。
timeout:select()最多等待时间,对阻塞操作则为NULL。

///
fds_set介绍(baidu百科)
       select()机制中提供一fd_set的数据结构,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件发生了可读或可写事件。

结构体原型:
typedef struct fd_set {
  u_int fd_count;
  socket fd_array[FD_SETSIZE];
  } fd_set;

fd_set set;
FD_ZERO(&set); /*将set清零使集合中不含任何fd*/
FD_SET(fd, &set); /*将fd加入set集合*/
FD_CLR(fd, &set); /*将fd从set集合中清除*/
FD_ISSET(fd, &set); /*在调用select()函数后,用FD_ISSET来检测fd在fdset集合中的状态是否变化返回整型,当检测到fd状态发生变化时返回真,否则,返回假(0)*/
以上式子中的fd为socket句柄。

///

Select I/O模型来实现一个并发处理多个客户端的TCP服务器 <转>

#include <winsock2.h> 
#include <stdio.h> 
#pragma comment(lib, “ws2_32.lib”) 
int main() 
{ 
    // 加载win socket 
    WSADATA ws; 
    int ret; 
    ret = WSAStartup(MAKEWORD(2, 2), &ws); 
    if (ret != 0) 
    { 
        printf(”WSAStartup() 失败!\n”); 
        return -1; 
    } 
    // 创建侦听SOCKET 
    SOCKET sListen; 
    sListen = socket(AF_INET, SOCK_STREAM, 0); 
    if (sListen == INVALID_SOCKET) 
    { 
        printf(”socket() 失败!\n”); 
        return -1; 
    } 
    // 填充服务器地址结构 
    sockaddr_in servAddr; 
    servAddr.sin_family = AF_INET; 
    servAddr.sin_addr.s_addr = INADDR_ANY; 
    servAddr.sin_port = htons(8000);

    // 绑定服务器套接字 
    ret = bind(sListen, (sockaddr*)&servAddr, sizeof(servAddr)); 
    if (ret == SOCKET_ERROR) 
    { 
        printf(”bind() 失败!\n”); 
        return -1; 
    }

    // 开始侦听 
    ret = listen(sListen, 5); 
    if (ret == SOCKET_ERROR) 
    { 
        printf(”listen() 失败!\n”); 
        return -1; 
    } 
    printf(”服务器启动成功,在端口%d监听…\n”, ntohs(servAddr.sin_port)); 
    //使用select模型 
    // 创建套接字集合 
    fd_set allSockSet; // 总的套接字集合 
    fd_set readSet; // 可读套接字集合 
    fd_set writeSet; // 可写套接字集合

    FD_ZERO(&allSockSet); // 清空套接字集合 
    FD_SET(sListen, &allSockSet); // 将sListen套接字加入套接字集合中 
    char bufRecv[100]; // 接收缓冲区 
    // 进入服务器主循环 
    while(1) 
    { 
        FD_ZERO(&readSet); // 清空可读套接字 
        FD_ZERO(&writeSet); // 清空可写套接字 
        readSet = allSockSet; // 赋值 
        writeSet = allSockSet; // 赋值 
        // 调用select函数,timeout设置为NULL 
        ret = select(0, &readSet, 0, NULL, NULL); 
        // 
        if (ret == SOCKET_ERROR) 
        { 
            printf(”select() 失败!\n”); 
            return -1; 
        } 
        // 存在套接字的I/O已经准备好 
        if(ret > 0) 
        { 
            // 遍历所有套接字 
            for (int i = 0; i < allSockSet.fd_count; ++i) 
            { 
                SOCKET s = allSockSet.fd_array[i]; 
                // 存在可读的套接字 
                if (FD_ISSET(s, &readSet)) 
                { 
                    // 可读套接字为sListen 
                    if (s == sListen) 
                    { 
                        // 接收新的连接 
                        sockaddr_in clientAddr; 
                        int len = sizeof(clientAddr); 
                        SOCKET sClient = accept(s, (sockaddr*)&clientAddr, &len); 
                        // 将新创建的套接字加入到集合中 
                        FD_SET(sClient, &allSockSet); 
                        printf(”>>>>>有新的连接到来啦…\n”); 
                        printf(”目前客户端数目为:%d\n”, allSockSet.fd_count - 1); 
                    } 
                    else // 接收客户端信息 
                    { 
                        ret = recv(s, bufRecv, 100, 0); 
                        // 接收错误 
                        if (ret == SOCKET_ERROR) 
                        { 
                            DWORD err = WSAGetLastError(); 
                            if (err == WSAECONNRESET) 
                            printf(”客户端被强行关闭\n”); 
                            else 
                            printf(”recv() 失败!”); 
                            // 删除套接字 
                            FD_CLR(s, &allSockSet); 
                            printf(”目前客户端数目为:%d\n”, allSockSet.fd_count - 1); 
                            break; 
                        } 
                        if (ret == 0) 
                        { 
                            printf(”客户端已经退出!\n”); 
                            // 删除套接字 
                            FD_CLR(s, &allSockSet); 
                            printf(”目前客户端数目为:%d\n”, allSockSet.fd_count - 1); 
                            break; 
                        } 
                        bufRecv[ret] = ‘\0′; 
                        printf(”收到的消息:%s\n”, bufRecv); 
                    } // end else

                }// end if

            }// end for 
        } // end if 
    }//end while 
    return 0; 
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值