网络编程select

select模型实现了单线程上处理多个套接字的可能

1.select 函数定义如下

int select(

     int nfds;  

     fd_set* readfds;   //指向一个套接字集合

     fd_set* writefds;

     fd_set* exceptfds;  //指向一个套接字集合,用来检查错误

    const struct timeval* timeout    //结构体  指定函数等待的最长时间   如果为NULL 则最长时间为无限长

);

2.套接字集合

fd_set结构可以把多个套接字连在一起,形成一个套接字集合。而select函数可以测试这个集合中哪些套接字有事件发生

typedef struct fd_set{

    u_int fd_count;

   SOCKET fd_array[FD_SETSIZE];   //套接字句柄数组  套接字集合  默认的是64

}fd_set;  

3.操作fd_set的宏

    FD_ZERO(*set)   很显然,初始化set为空集合,集合在使用前应该清空

    FD_CLR(*set)     从set移除套接字s

    FD_ISSET(s,*set)     检查s是不是set的成员,如果是返回TRUE

    FD_SET(s,*set)   添加套接字到集合


主要程序代码:

#include"initsock.h"
#include<stdio.h>

CInitSock theSock;  //初始化Winsock库

int main()
{
    USHORT nPort=4567; //服务器监听的端口
    //创建监听端口
    SOCKET sListen=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    sockaddr_in sin;
    sin.sin_family=AF_INET;
    sin.sin_port=htons(nPort);
    sin.sin_addr.S_un.S_addr=INADDR_ANY;
    //绑定套接字到本地机器
    if(::bind(sListen,(sockaddr*)&sin,sizeof(sin))==SOCKET_ERROR)
    {
        printf("Failed bind()");
        return -1;
    }
    //进入监听模式
    ::listen(sListen,5);

    //select模型处理过程
    //第一步:初始化一个套接字 集合 fdSocket,添加监听套接字句柄到这个集合
    fd_set fdSocket;   //所有可用套接字集合
    FD_ZERO(&fdSocket);
    FD_SET(sListen,&fdSocket);
    while(TRUE)
    {
        //第二步:将fdSocket集合的一个拷贝fdRead传递给select函数
        //当有事件发生时,select函数移除fdRead集合中没有未决I/O操作的套接字句柄,然后返回
        fd_set fdRead=fdSocket;
        int nRet=::select(0,&fdRead,NULL,NULL,NULL);
        if(nRet>0)
        {
            //第三步:通过将原来fdSocket集合与select处理过的fdRead集合比较
            //确定有哪些套接字有未决I/O,并进一步处理这些I/O
            for(int i=0;i<(int)fdSocket.fd_count;i++)
            {
                if(fdSocket.fd_array[i]==sListen) //监听套接字接收到新连接
                {
                    if(fdSocket.fd_count<FD_SETSIZE)
                    {
                        sockaddr_in addrRemote;
                        int nAddrLen=sizeof(addrRemote);
                        SOCKET sNew=::accept(sListen,(SOCKADDR*)&addrRemote,&nAddrLen);
                        FD_SET(sNew,&fdSocket);
                        printf("接收到连接(%s)\n",::inet_ntoa(addrRemote.sin_addr));
                    }
                    else
                    {
                        printf("Too much connections!\n");
                        continue;
                    }
                }
                else
                {
                    char szText[256];
                    int nRecv=::recv(fdSocket.fd_array[i],szText,strlen(szText),0);
                    if(nRecv>0)
                    {
                        szText[nRecv]='\0';
                        printf("接收到数据:%s\n",szText);
                    }
                    else
                    {
                        ::closesocket(fdSocket.fd_array[i]);
                        FD_CLR(fdSocket.fd_array[i],&fdSocket);
                    }
                }
            }
        }
        else
        {
            printf("Failed select()\n");
            break;
        }
    }
    return 0;
}


其中 initsock

#include<WinSock.h>
#pragma comment(lib,"WS2_32")
class CInitSock
{
public:
    CInitSock(BYTE minorVer=2,BYTE majorVer=2)
    {
        //初始化Wsh_32.dll
        WSADATA wsaData;
        WORD sockVersion=MAKEWORD(minorVer,majorVer);
        if(::WSAStartup(sockVersion,&wsaData)!=0)
        {
            exit(0);
        }
    }
    ~CInitSock()
    {
        ::WSACleanup();
    }
};


在C语言网络编程里,select模型是实现IO多路复用的方式之一,可实现多客户端连接通信。 select函数是该模型的核心,在Linux下,它也是一个系统函数,其原型如下: ```c #include <sys/select.h> int select(int ndfs, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout); ``` 该函数的工作流程为:把已连接的socket都放到一个句柄集合(fd_set)中,接着调用select函数将句柄集合拷贝到内核,由内核检查是否有网络事件产生,此检查通过遍历集合的方式进行。当检查到有事件产生后,将对应的socket标记为可读或可写,随后把整个句柄集合拷贝回用户态,用户态还需再次遍历集合,找到就绪的句柄并做后续处理 [^1]。 使用select模型时,select会遍历socket数组,取出有响应的socket,若没有则持续遍历,虽然select()函数的执行是阻塞的,但每次都只处理有响应的socket,所以能实现多客户端连接通信 [^2]。 以下是一个简单的使用示例步骤及代码框架: 1. 初始化socket,绑定地址,监听连接等基本操作。 2. 创建fd_set集合,将监听socket加入集合。 3. 调用select函数进行监听。 4. 遍历集合,处理就绪的socket。 示例代码框架如下: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/select.h> #include <unistd.h> #define PORT 8888 #define MAX_CLIENTS 10 int main() { int server_fd, new_socket; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buffer[1024] = {0}; fd_set readfds; int max_sd; // 创建socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置socket选项 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("setsockopt"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 绑定地址 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 监听连接 if (listen(server_fd, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } while (1) { FD_ZERO(&readfds); FD_SET(server_fd, &readfds); max_sd = server_fd; // 这里可以添加其他客户端socket到readfds集合 // 调用select函数 int activity = select(max_sd + 1, &readfds, NULL, NULL, NULL); if ((activity < 0) && (errno != EINTR)) { perror("select error"); } // 处理监听socket上的新连接 if (FD_ISSET(server_fd, &readfds)) { if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { perror("accept"); exit(EXIT_FAILURE); } // 可以将新的客户端socket加入集合等操作 } // 遍历其他socket,处理读写事件 // ... } return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值