2023_11_01_select

发布时间 2023-11-01 17:40:12作者: cao137831

Hello World

#include <iostream>
#include <strings.h>
#include <cstring>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define PORT 8001
#define MESSAGE_LEN 1024
#define FD_SIZE 1024

int main(int argc, char* argv[])
{
    int ret = -1;
    int on = 1;
    int backlog = 10;   // 缓冲长度
    int socket_fd, accept_fd;
    struct sockaddr_in serveraddr, clientaddr;
    
    char in_buff[MESSAGE_LEN] = {'\0'};
    
    pid_t pid;
    int flags;  // fcntl函数返回值
    int events = 0;
    int max_fd = -1;
    fd_set fd_sets;
    int accept_fds[FD_SIZE] = {'\0'};
    int curpos = -1;
    
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_fd == -1){
        std::cout <<"Failed to create socket!\n";
        exit(-1);
    }

    flags = fcntl(socket_fd,F_GETFL,0);
    fcntl(socket_fd,F_SETFL,flags | O_NONBLOCK);    // 设置为非阻塞
    max_fd = socket_fd;

    // 这段代码的主要目的是确保套接字可以快
    // 速地重新使用相同的地址和端口,通常用
    // 于在服务器程序中,以便服务器能够更快
    // 地重新启动或者处理新的连接。
    ret = setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
    if (ret == -1){
        std::cout <<"Failed to set socket options!\n";
    }

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = PORT;
    serveraddr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(serveraddr.sin_zero), 8);

    ret = bind(socket_fd, (struct sockaddr*)&serveraddr,sizeof(struct sockaddr));
    if (ret == -1){
        std::cout <<"Failed to bind addr!\n";
        exit(-1);
    }

    ret = listen(socket_fd, backlog);
    if (ret == -1){
        std::cout <<"Failed to listen addr!\n";
        exit(-1);
    }

    int col = 0;
    for (;;){
        FD_ZERO(&fd_sets);
        FD_SET(socket_fd,&fd_sets);
        for (int i = 0; i < FD_SIZE; i ++ ){
            if (accept_fds[i] != 0){
                if (accept_fds[i] > max_fd){
                    max_fd = accept_fds[i];
                }
                FD_SET(accept_fds[i], &fd_sets);
            }
        }
        
        // 文件描述符就绪的个数
        /*无限期阻塞,并测试文件描述符变动 */
        events = select(max_fd + 1,&fd_sets,nullptr,nullptr,nullptr);

        if (events < 0){
            std::cout <<"Using select error!\n";
            break;
        }else if (events == 0){
            std::cout <<"timeout...\n";
            continue;
        }else if (events){
            if (FD_ISSET(socket_fd, &fd_sets)){ // 侦听,来连接了
                curpos = -1;
                for (int i = 0; i < FD_SIZE; i ++ ){
                    if (accept_fds[i] == 0){   // 查找一个空位
                        curpos = i;
                        break;
                    }
                }
                if (curpos == -1){
                    std::cout <<"new client is failed to add!\n";
                    continue;
                }
                
                socklen_t addr_len = sizeof(struct sockaddr);
                accept_fd = accept(socket_fd,(struct sockaddr*)&clientaddr,&addr_len);

                flags = fcntl(accept_fd,F_GETFL,0);
                fcntl(accept_fd,F_SETFL,flags | O_NONBLOCK);    // 设置为非阻塞   

                accept_fds[curpos] = accept_fd;
            }
            for (int i = 0; i < FD_SIZE; i++){
                if (accept_fds[i] != 0 && FD_ISSET(accept_fds[i], &fd_sets)){  // 开始读数据
                    memset(in_buff, 0, MESSAGE_LEN);
                    ret = recv(accept_fds[i],in_buff,MESSAGE_LEN, 0);
                    
                    if(ret > 0) // 接受数据成功
                    {
                        // printf("message form client[%d]:%s\n", i, resv_message);
                        std::cout <<"----- 信息 "<< ++ col <<" -----\n";
                        std::cout <<"客户端发来的数据: "<< in_buff <<'\n';
                        
                        std::string str = in_buff; 
                        str = "!@#$%^&*- " + str + " -*&^%$#@!";

                        send(accept_fds[i],str.c_str(),MESSAGE_LEN,0);
                    }
                    else if(ret < 0)    // 接收失败
                    {
                        printf("rescessed error!");
                    }
                    //某个客户端退出
                    else  //cancel fdset and set fd = 0
                    {
                        FD_CLR(accept_fds[i], &fd_sets);
                        accept_fds[i] = 0;
                        continue;  //这里如果用break的话一个客户端退出会造成服务器也退出。  
                    }
                }
            }
                
            

        }
        
    }

    close(socket_fd);

    return 0;
}