luchenqun
12/13/2017 - 2:50 PM

select.cpp

/************************************************* 
* File name   : server.c 
* Description : 单进程并发服务器 
* Author      : 745917886@@qq.com 
* Version     : V1.0 
* Date        :  
* Compiler    : arm-linux-gcc-4.4.3 
* Target      : mini2440(Linux-2.6.32) 
* History     :  
*   <author>  <time>   <version >   <desc> 
*************************************************/  
#include <stdio.h>   
#include <string.h>   
#include <unistd.h>   
#include <sys/types.h>   
#include <sys/socket.h>   
#include <netinet/in.h>   
#include <arpa/inet.h>   
#include <sys/time.h>   
#include <stdlib.h>   
  
#define PORT 1234               // 服务器端口   
#define BACKLOG 5               //listen 队列中等待的连接数   
#define MAXDATASIZE 1024        // 缓冲区大小   
  
typedef struct _CLIENT  
{  
    int fd;                     // 客户端 socket 描述符   
    char name[20];              // 客户端名称   
    struct sockaddr_in addr;    // 客户端地址信息结构体   
    char data[MAXDATASIZE];     // 客户端私有数据指针   
} CLIENT;  
  
void process_client(CLIENT * client, char *recvbuf, int len);   // 客户请求处理函数   
  
/************************************************* 
* Function    : main() 
* Description :  
* Calls       : process_client() 
* Called By   :  
* Input       :  
* Output      :  
* Return      :  
*************************************************/  
void main(int argc ,char **argv)  
{  
    int i, maxi, maxfd, sockfd;  
    int nready;  
    ssize_t n;  
    fd_set rset, allset;        //select 所需的文件描述符集合   
    int listenfd, connectfd;    //socket 文件描述符   
    struct sockaddr_in server;  // 服务器地址信息结构体   
  
    CLIENT client[FD_SETSIZE];  //FD_SETSIZE 为 select 函数支持的最大描述符个数   
    char recvbuf[MAXDATASIZE];  // 缓冲区   
    int sin_size;               // 地址信息结构体大小   
  
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
    {                           // 调用 socket 创建用于监听客户端的 socket   
        perror("Creating socket failed.");  
        exit(1);  
    }  
  
    int opt = SO_REUSEADDR;  
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  // 设置 socket 属性   
  
    bzero(&server, sizeof(server));  
    server.sin_family = AF_INET;  
    server.sin_port = htons(PORT);  
    server.sin_addr.s_addr = htonl(INADDR_ANY);  
  
    if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1)  
    {                           // 调用 bind 绑定地址   
        perror("Bind error.");  
        exit(1);  
    }  
  
    if (listen(listenfd, BACKLOG) == -1)  
    {                           // 调用 listen 开始监听   
        perror("listen() error\n");  
        exit(1);  
    }  
  
    // 初始化 select   
    maxfd = listenfd;  
    maxi = -1;  
    for (i = 0; i < FD_SETSIZE; i++)  
    {  
        client[i].fd = -1;  
    }  
    FD_ZERO(&allset);           // 清空   
    FD_SET(listenfd, &allset);  // 将监听 socket 加入 select 检测的描述符集合   
  
    while (1)  
    {  
        struct sockaddr_in addr;  
        rset = allset;  
        nready = select(maxfd + 1, &rset, NULL, NULL, NULL);    // 调用 select   
        printf("Select() break and the return num is %d. \n", nready);  
  
        if (FD_ISSET(listenfd, &rset))  
        {                       // 检测是否有新客户端请求   
            printf("Accept a connection.\n");  
            // 调用 accept,返回服务器与客户端连接的 socket 描述符   
            sin_size = sizeof(struct sockaddr_in);  
            if ((connectfd =  
                 accept(listenfd, (struct sockaddr *)&addr, (socklen_t *) & sin_size)) == -1)  
            {  
                perror("Accept() error\n");  
                continue;  
            }  
  
            // 将新客户端的加入数组   
            for (i = 0; i < FD_SETSIZE; i++)  
            {  
                if (client[i].fd < 0)  
                {  
                    char buffer[20];  
                    client[i].fd = connectfd;   // 保存客户端描述符   
                    memset(buffer, '0', sizeof(buffer));  
                    sprintf(buffer, "Client[%.2d]", i);  
                    memcpy(client[i].name, buffer, strlen(buffer));  
                    client[i].addr = addr;  
                    memset(buffer, '0', sizeof(buffer));  
                    sprintf(buffer, "Only For Test!");  
                    memcpy(client[i].data, buffer, strlen(buffer));  
                    printf("You got a connection from %s:%d.\n", inet_ntoa(client[i].addr.sin_addr),ntohs(client[i].addr.sin_port));  
                    printf("Add a new connection:%s\n",client[i].name);  
                    break;  
                }  
            }  
              
            if (i == FD_SETSIZE)  
                printf("Too many clients\n");  
            FD_SET(connectfd, &allset); // 将新 socket 连接放入 select 监听集合   
            if (connectfd> maxfd)  
                maxfd = connectfd;  // 确认 maxfd 是最大描述符   
            if (i> maxi)       // 数组最大元素值   
                maxi = i;  
            if (--nready <= 0)  
                continue;       // 如果没有新客户端连接,继续循环   
        }  
  
        for (i = 0; i <= maxi; i++)  
        {  
            if ((sockfd = client[i].fd) < 0)    // 如果客户端描述符小于 0,则没有客户端连接,检测下一个   
                continue;  
            // 有客户连接,检测是否有数据   
            if (FD_ISSET(sockfd, &rset))  
            {  
                printf("Receive from connect fd[%d].\n", i);  
                if ((n = recv(sockfd, recvbuf, MAXDATASIZE, 0)) == 0)  
                {               // 从客户端 socket 读数据,等于 0 表示网络中断   
                    close(sockfd);  // 关闭 socket 连接   
                    printf("%s closed. User's data: %s\n", client[i].name, client[i].data);  
                    FD_CLR(sockfd, &allset);    // 从监听集合中删除此 socket 连接   
                    client[i].fd = -1;  // 数组元素设初始值,表示没客户端连接   
                }  
                else  
                    process_client(&client[i], recvbuf, n); // 接收到客户数据,开始处理   
                if (--nready <= 0)  
                    break;      // 如果没有新客户端有数据,跳出 for 循环回到 while 循环   
            }  
        }  
    }  
    close(listenfd);            // 关闭服务器监听 socket        
}  
  
/************************************************* 
* Function    : process_client() 
* Description : 处理客户端连接函数 
* Calls       :  
* Called By   : main() 
* Input       :  
* Output      :  
* Return      :  
*************************************************/  
void process_client(CLIENT * client, char *recvbuf, int len)  
{  
    char sendbuf[MAXDATASIZE];  
    int i;  
  
    printf("Received client(%s) message: %s\n", client->name, recvbuf);  
     
    for (i = 0; i < len - 1; i++)  
    {  
        sendbuf[i] = recvbuf[len - i - 2];  
    }  
      
    sendbuf[len - 1] = '\0';  
  
    send(client->fd, sendbuf, strlen(sendbuf), 0);  
}