非阻塞select

本文展示了如何在C++中使用非阻塞select实现客户端和服务端的连接。客户端通过非阻塞方式进行连接尝试,遇到EINPROGRESS错误时,使用select进行超时控制。服务端使用非阻塞select监听客户端连接,并处理接收和发送数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

非阻塞select下载

链接:https://pan.baidu.com/s/1v3wuohyelqGFIRVbUAJ4sg  密码:49vw

select_client客户端

//

//  main.cpp

//  111

//

//  Created by LXBig on 18/4/9.

//  Copyright © 2018 LXBig. All rights reserved.

//


#include <iostream>

#include <string.h>

#include <stdlib.h>

#include <arpa/inet.h>

#include <sys/socket.h>

#include <unistd.h>

#define BUF_SIZE 1024


#include <fcntl.h>

#include <sys/select.h>

#include<sys/ioctl.h>

#include <sys/types.h>

#include <errno.h>

#include <unistd.h>

//客户端

void setNonBlock(int s,bool noblock)

{

    /*

     1、获取文件的flags,即open函数的第二个参数:

     flags = fcntl(fd,F_GETFL,0);

     2、设置文件的flags:

     fcntl(fd,F_SETFL,flags);

     3、增加文件的某个flags,比如文件是阻塞的,想设置成非阻塞:

     */

    int flags = fcntl(s, F_GETFL,&noblock);

    flags |= O_NONBLOCK;

    fcntl(s,F_SETFL,flags);

}

#define PEER_IP     "127.0.0.1"

#define PEER_PORT   1234

void select_sock_client()

{

    int ret = 0;

    int sock_fd;

    struct sockaddr_in addr;

    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    setNonBlock(sock_fd, true);

    memset(&addr, 0, sizeof(sockaddr));

    addr.sin_family = AF_INET;

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    addr.sin_port = htons(1234);

    int res = connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr));


    if (res == 0) {

        printf("socket connect succeed immediately.\n");

        ret = 0;

    }

    else

    {

        printf("get the connect result by select().\n");

        if (errno == EINPROGRESS) //EINPROGRESS: Operation now in progress(套接字为非阻塞套接字,且连接请求没有立即完成)

        {

            int times = 0;

            while (true)

            {

                fd_set rfds, wfds;

                struct timeval tv;

                

                printf("errno = %d\n", errno);

                FD_ZERO(&rfds);

                FD_ZERO(&wfds);

                FD_SET(sock_fd, &rfds);

                FD_SET(sock_fd, &wfds);

                

                /* set select() time out */

                tv.tv_sec = 10;

                tv.tv_usec = 0;

                int selres = select(sock_fd + 1, &rfds, &wfds, NULL, &tv);

                switch (selres)

                {

                    case -1:

                        printf("select error\n");

                        ret = -1;

                        break;

                    case 0:

                        printf("select time out\n");

                        ret = -1;

                        break;

                    default:

                        if (FD_ISSET(sock_fd, &rfds) || FD_ISSET(sock_fd, &wfds))

                        {

                            connect(sock_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));

                            int err = errno;

                            if  (err == EISCONN) //Transport endpoint is already connected(已经连接到该套接字)

                            {

                                printf("connect finished 111.\n");

                                ret = 0;

                            }

                            else

                            {

                                printf("connect failed. errno = %d\n", errno);

                                printf("FD_ISSET(sock_fd, &rfds): %d\n FD_ISSET(sock_fd, &wfds): %d\n", FD_ISSET(sock_fd, &rfds) , FD_ISSET(sock_fd, &wfds));

                                ret = errno;

                            }


                        }

                        else

                        {

                            printf("haha\n");

                        }

                }

                

                if (-1 != selres && (ret != 0))

                {

                    printf("check connect result again... %d\n", times);

                    continue;

                }

                else

                {

                    break;

                }

            }

        }

        else

        {

            printf("connect to host %s:%d failed.\n", PEER_IP, PEER_PORT);

            ret = errno;

        }

    }

    if (0 == ret)

    {

        //获取用户输入的字符串并发送给服务器

        char bufSend[40];

        printf("input a string: ");

        scanf("%s",bufSend);

        write(sock_fd, bufSend, strlen(bufSend));

    }

    else

    {

        printf("connect to host %s:%d failed.\n", PEER_IP, PEER_PORT);

    }

    

    close(sock_fd);

    

}


int main(int argc, const char * argv[]) {


    select_sock_client();


    

    

    return 0;

}


运行结果

get the connect result by select().

errno = 36

connect finished 111.

input a string: 12345

Program ended with exit code: 0


select_server服务端

//

//  main.cpp

//  222

//

//  Created by LXBig on 18/4/9.

//  Copyright © 2018 LXBig. All rights reserved.

//


#include <iostream>

//using namespace std;

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <netinet/in.h>


#include <sys/socket.h>

#include <netdb.h>    //DNS头文件

#define BUF_SIZE 1024

#include <set>

#include <fcntl.h>

//服务端

fd_set g_inset;

std::set<int> g_clients;

void setNonBlock(int s,bool noblock)

{

    /*

     1、取得文件描述符状态 、获取文件的flags,即open函数的第二个参数:

     flags = fcntl(fd,F_GETFL,0);

     

     O_NONBLOCK 无延迟。/ 非阻塞

     2、设置文件的flags:

     fcntl(fd,F_SETFL,flags);

     3、增加文件的某个flags,比如文件是阻塞的,想设置成非阻塞:

     */

    int flags = fcntl(s, F_GETFL,&noblock);

    flags |= O_NONBLOCK;

    fcntl(s,F_SETFL,flags);

}


//处理客户端连接

void processAccept(int & server,int & maxfd)

{

    struct sockaddr_in clnt_addr;

    socklen_t clnt_addr_size = sizeof(clnt_addr);

    int clnt_sock = accept(server, (struct sockaddr*)&clnt_addr, &clnt_addr_size);

    //添加到g_clients进行管理,并设置到g_inset中,在下次循环时监听该套接字

    g_clients.insert(clnt_sock);

    FD_SET(clnt_sock,&g_inset);//将套接字添加到set

    setNonBlock(clnt_sock, true);//将客户端设置为非阻塞

    

    if (maxfd < clnt_sock) {

        maxfd = clnt_sock;

    }

}


//客户端发送消息

bool prvessClient(int & client)

{

    char buf[512];

    //接收并且打印数据

    ssize_t n = read(client, buf, sizeof(buf)-1);

    if (n > 0) {

        printf("server recv %zd byte: %s\n",n,buf);

        //发送回客户端并关闭客户端

        n = write(client, buf, n);

    }

    else if (n == 0)

    {

        close(client);

        return false;

    }

    return true;

}


void select_sock_server()

{

    int err = 0;

    int maxfd = 0;

    //创建一个tcp Socket

    int serv_sock = socket(AF_INET, SOCK_STREAM, 0);

    maxfd = serv_sock;

    setNonBlock(serv_sock, true); //设置 非阻塞

    //将套接字和IP、端口绑定

    struct sockaddr_in serv_addr;

    memset(&serv_addr, 0, sizeof(serv_addr));//每个字节都用0填充

    serv_addr.sin_family = AF_INET;//使用IPV4地址

    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//具体的IP地址

    serv_addr.sin_port = htons(1234);//端口

    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    //进入监听状态,等待用户发起请求

    err = listen(serv_sock, 20);

    if (err == -1) {

        std::cout<<"error!"<<std::endl;

        return ;

    }

    //设置select函数的等待时间

    timeval t;

    t.tv_sec = 0;

    t.tv_usec = 1000;

    

    //清空全局的fd可读集合,将服务器的监听socket()函数添加进去

    FD_ZERO(&g_inset);

    FD_SET(serv_sock,&g_inset);

    do {

        //每次都重置 inset  这样只需要维护好 g_inset即可

        fd_set inset = g_inset;

        int ret = select(maxfd+1, &inset, nullptr, nullptr, &t);

        if (ret > 0) {

            printf("******************\n");

            //如果是服务器准备就绪,说明有客户端连接

            if (FD_ISSET(serv_sock,&inset)) {

                processAccept(serv_sock, maxfd);

                --ret;

            }

            //判断是否有客户端套接字就绪,有则处理

            for (std::set<int>::iterator iter = g_clients.begin(); iter != g_clients.end() && ret > 0 ;)

            {

                int client = *iter;

                if (FD_ISSET(client,&inset)) {

                    --ret;

                    //如果 客户端 关闭 g_inset g_clients中清空该客户端

                    if(!prvessClient(client))

                    {

                        FD_CLR(client,&g_inset);

                        g_clients.erase(iter++);

                        continue;

                    }

                }

                ++iter;

            }

            //select()函数触发的事件 处理完 ,会提前 结束遍历。

        }

    } while (true);


}


int main(int argc, const char * argv[]) {

    



    select_sock_server();

    

    return 0;

}


的出结果

******************

******************

******************

server recv 5 byte: 12345

******************

******************


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值