Linux下的单进程多用户TCP服务器,采用select方法实现。
/************************************************* * File name : server.c * Description : 单进程并发服务器 * Author : sg131971@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); }
测试结果: