这是展示select,poll,epoll的例子,并添加注释,代码来自:
[dm href=’http://www.cnblogs.com/ggjucheng/archive/2012/01/17/2324584.html’]参考链接[/dm]
客户端代码:
#include <unistd.h> #include <sys/types.h> /* basic system data types */ #include <sys/socket.h> /* basic socket definitions */ #include <netinet/in.h> /* sockaddr_in{} and other Internet defns */ #include <arpa/inet.h> /* inet(3) functions */ #include <netdb.h> /*gethostbyname function */ #include <stdlib.h> #include <errno.h> #include <stdio.h> #include <string.h> #define MAXLINE 1024 void handle(int connfd); int main(int argc, char **argv) { char * servInetAddr = "127.0.0.1"; // 服务器端的IP地址 int servPort = 6888; // 连接服务器端的端口 char buf[MAXLINE]; int connfd; struct sockaddr_in servaddr; if (argc == 2) { servInetAddr = argv[1]; } if (argc == 3) { servInetAddr = argv[1]; servPort = atoi(argv[2]); } if (argc > 3) { printf("usage: echoclient <IPaddress> <Port>\n"); return -1; } connfd = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字 bzero(&servaddr, sizeof(servaddr)); // 清空服务器端地址 servaddr.sin_family = AF_INET; //协议族 servaddr.sin_port = htons(servPort); // 端口号 inet_pton(AF_INET, servInetAddr, &servaddr.sin_addr); // 连接服务器端 if (connect(connfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { perror("connect error"); return -1; } printf("welcome to echoclient\n"); handle(connfd); /* do it all */ close(connfd); printf("exit\n"); exit(0); } void handle(int sockfd) { char sendline[MAXLINE], recvline[MAXLINE]; int n; for (;;) { if (fgets(sendline, MAXLINE, stdin) == NULL) { // 从标准输入获取字符串 break;//read eof } /* //也可以不用标准库的缓冲流,直接使用系统函数无缓存操作 if (read(STDIN_FILENO, sendline, MAXLINE) == 0) { break;//read eof } */ n = write(sockfd, sendline, strlen(sendline)); // 想套接字中的写入字符串 n = read(sockfd, recvline, MAXLINE); // 从套接字中读取 if (n == 0) { printf("echoclient: server terminated prematurely\n"); break; } write(STDOUT_FILENO, recvline, n); //如果用标准库的缓存流输出有时会出现问题 //fputs(recvline, stdout); } }
阻塞模式服务器端
#include <unistd.h> #include <sys/types.h> /* basic system data types */ #include <sys/socket.h> /* basic socket definitions */ #include <netinet/in.h> /* sockaddr_in{} and other Internet defns */ #include <arpa/inet.h> /* inet(3) functions */ #include <stdlib.h> #include <errno.h> #include <stdio.h> #include <string.h> #define MAXLINE 1024 //typedef struct sockaddr SA; void handle(int connfd); int main(int argc, char **argv) { int listenfd, connfd; // 监听套接字,已连接套接字 int serverPort = 6888; // 服务器端口 int listenq = 1024; pid_t childpid; char buf[MAXLINE]; socklen_t socklen; // 服务器地址,客户端地址 struct sockaddr_in cliaddr, servaddr; socklen = sizeof(cliaddr); // 给服务器地址这个结构体的各个字段赋值 bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(serverPort); // 创建监听套接字 listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd < 0) { // 创建失败,小于0 perror("socket error"); return -1; } // 绑定监听套接字和服务器地址 if (bind(listenfd, (struct sockaddr *) &servaddr, socklen) < 0) { perror("bind error"); return -1; } // 大概是监听套接字最大连接数 if (listen(listenfd, listenq) < 0) { perror("listen error"); return -1; } // 打印服务器端端口号 printf("echo server startup,listen on port:%d\n", serverPort); for ( ; ; ) { // 死循环,用于客户端连接 // 如果监听套接字接收到有客户端连接,则返回一个已连接套接字 connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &socklen); if (connfd < 0) { perror("accept error"); continue; } // 打印对等方信息 sprintf(buf, "accept form %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port); printf(buf,""); childpid = fork(); if (childpid == 0) { /* child process */ close(listenfd); /* close listening socket */ handle(connfd); /* process the request */ exit (0); } else if (childpid > 0) { close(connfd); /* parent closes connected socket */ } else { perror("fork error"); } } } void handle(int connfd) { size_t n; char buf[MAXLINE]; for(;;) { // 从已连接套接字中读取消息 n = read(connfd, buf, MAXLINE); if (n < 0) { // 如果读取的消息数目小于0,则说明出错了 if(errno != EINTR) { perror("read error"); break; } } // 读取到的消息等于0,说明套接字中没有消息,关闭已连接套接字 if (n == 0) { //connfd is closed by client close(connfd); printf("client exit\n"); break; } //client exit // 特殊符号,退出 if (strncmp("exit", buf, 4) == 0) { close(connfd); printf("client exit\n"); break; } write(connfd, buf, n); //write maybe fail,here don't process failed error } }
select模式服务器
#include <unistd.h> #include <sys/types.h> /* basic system data types */ #include <sys/socket.h> /* basic socket definitions */ #include <netinet/in.h> /* sockaddr_in{} and other Internet defns */ #include <arpa/inet.h> /* inet(3) functions */ #include <sys/select.h> /* select function*/ #include <stdlib.h> #include <errno.h> #include <stdio.h> #include <string.h> #define MAXLINE 10240 void handle(int * clientSockFds, int maxFds, fd_set* pRset, fd_set* pAllset); int main(int argc, char **argv) { int servPort = 6888; // 服务器端口 int listenq = 1024; int listenfd, connfd; // 监听套接字,已连接套接字 struct sockaddr_in cliaddr, servaddr; // 定义服务器,客户端地址结构体 socklen_t socklen = sizeof(struct sockaddr_in); int nready, nread; char buf[MAXLINE]; int clientSockFds[FD_SETSIZE]; fd_set allset, rset; int maxfd; listenfd = socket(AF_INET, SOCK_STREAM, 0); // 创建监听套接字 if (listenfd < 0) { perror("socket error"); return -1; } int opt = 1; if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("setsockopt error"); } // 向服务器地址结构体中的各个字段赋值 bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(servPort); // 绑定监听套接字和服务器地址 if(bind(listenfd, (struct sockaddr*)&servaddr, socklen) == -1) { perror("bind error"); exit(-1); } if (listen(listenfd, listenq) < 0) { perror("listen error"); return -1; } int i = 0; for (i = 0; i< FD_SETSIZE; i++) clientSockFds[i] = -1; FD_ZERO(&allset); // 情况文件描述符集合 FD_SET(listenfd, &allset); // 将监听套接字加入文件描述符集合 maxfd = listenfd; printf("echo server use select startup, listen on port %d\n", servPort); printf("max connection: %d\n", FD_SETSIZE); for ( ; ; ) { rset = allset; // 将套接字集合赋值给reset // nready是.. nready = select(maxfd + 1, &rset, NULL, NULL, NULL); if (nready < 0) { perror("select error"); continue; } // 如果监听套接字有返回 if (FD_ISSET(listenfd, &rset)) { // 建立连接,返回已连接套接字 connfd = accept(listenfd, (struct sockaddr*) &cliaddr, &socklen); if (connfd < 0) { perror("accept error"); continue; } // 打印对等方信息 sprintf(buf, "accept form %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port); printf(buf, ""); // 将已连接套接字加入套接字存放的数组 for (i = 0; i< FD_SETSIZE; i++) { if (clientSockFds[i] == -1) { clientSockFds[i] = connfd; break; } } // 如果套接字存放数组中没有空闲的地方,则关闭 if (i == FD_SETSIZE) { fprintf(stderr, "too many connection, more than %d\n", FD_SETSIZE); close(connfd); continue; } // 已连接套接字的文件描述符是否是最大的,如果是,则更新 if (connfd > maxfd) maxfd = connfd; // 将已连接套接字放入套接字集合 FD_SET(connfd, &allset); if (--nready <= 0) continue; } // 存放套接字数组,最大套接字文件描述符,套接字集合 handle(clientSockFds, maxfd, &rset, &allset); } } void handle(int * clientSockFds, int maxFds, fd_set* pRset, fd_set* pAllset) { int nread; int i; char buf[MAXLINE]; for (i = 0; i< maxFds; i++) { if (clientSockFds[i] != -1) { if (FD_ISSET(clientSockFds[i], pRset)) { nread = read(clientSockFds[i], buf, MAXLINE);//读取客户端socket流 if (nread < 0) { perror("read error"); close(clientSockFds[i]); FD_CLR(clientSockFds[i], pAllset); clientSockFds[i] = -1; continue; } if (nread == 0) { printf("client close the connection\n"); close(clientSockFds[i]); FD_CLR(clientSockFds[i], pAllset); clientSockFds[i] = -1; continue; } write(clientSockFds[i], buf, nread);//响应客户端 有可能失败,暂不处理 } } } }
Poll函数服务器
#include <unistd.h> #include <sys/types.h> /* basic system data types */ #include <sys/socket.h> /* basic socket definitions */ #include <netinet/in.h> /* sockaddr_in{} and other Internet defns */ #include <arpa/inet.h> /* inet(3) functions */ #include <stdlib.h> #include <errno.h> #include <stdio.h> #include <string.h> #include <poll.h> /* poll function */ #include <limits.h> #define MAXLINE 10240 #ifndef OPEN_MAX #define OPEN_MAX 40960 #endif void handle(struct pollfd* clients, int maxClient, int readyClient); int main(int argc, char **argv) { int servPort = 6888; // 端口信息 int listenq = 1024; // listen等待的最大文件描述符 int listenfd, connfd; struct pollfd clients[OPEN_MAX]; // poll打开的最大文件描述符 int maxi; socklen_t socklen = sizeof(struct sockaddr_in); // 地址长度 struct sockaddr_in cliaddr, servaddr; // 服务器地址和客户端地址 char buf[MAXLINE]; // bug长度 int nready; // 初始化服务器地址 bzero(&servaddr, socklen); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(servPort); // 创建监听套接字 listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd < 0) { perror("socket error"); } // 保证地址可以重复利用 int opt = 1; if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("setsockopt error"); } // 绑定监听套接字和服务器地址 if(bind(listenfd, (struct sockaddr *) &servaddr, socklen) == -1) { perror("bind error"); exit(-1); } if (listen(listenfd, listenq) < 0) { perror("listen error"); } // clients中的第一个套接字是监听套接字 clients[0].fd = listenfd; // clients中的第一个套接字,我们关心的事件是POLLIN事件 clients[0].events = POLLIN; int i; // 其他套接字置为-1 for (i = 1; i< OPEN_MAX; i++) clients[i].fd = -1; maxi = listenfd + 1; printf("pollechoserver startup, listen on port:%d\n", servPort); printf("max connection is %d\n", OPEN_MAX); for ( ; ; ) { printf("For\n"); // poll 函数,clients集合,最大文件描述符,不超时 nready = poll(clients, maxi + 1, -1); printf("Poll End\n"); //printf("nready is %d\n", nready); if (nready == -1) { perror("poll error"); } // 如果clients中的第一个文件描述符有POLLIN事件 if (clients[0].revents & POLLIN) { // 建立连接,返回已连接套接字 connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &socklen); sprintf(buf, "accept form %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port); printf(buf, ""); // 将已连接套接字加入空闲位置,并对POLLIN事件感兴趣 for (i = 0; i < OPEN_MAX; i++) { if (clients[i].fd == -1) { clients[i].fd = connfd; clients[i].events = POLLIN; break; } } // 如果没有空闲位置了,则表明clients集合已经满了,关闭套接字 if (i == OPEN_MAX) { fprintf(stderr, "too many connection, more than %d\n", OPEN_MAX); close(connfd); continue; } if (i > maxi) maxi = i; --nready; } handle(clients, maxi, nready); } } void handle(struct pollfd* clients, int maxClient, int nready) { int connfd; int i, nread; char buf[MAXLINE]; printf("Print\n"); if (nready == 0) return; for (i = 1; i< maxClient; i++) { connfd = clients[i].fd; if (connfd == -1) continue; if (clients[i].revents & (POLLIN | POLLERR)) { nread = read(connfd, buf, MAXLINE);//读取客户端socket流 if (nread < 0) { perror("read error"); close(connfd); clients[i].fd = -1; continue; } if (nread == 0) { printf("client close the connection"); close(connfd); clients[i].fd = -1; continue; } write(connfd, buf, nread);//响应客户端 if (--nready <= 0)//没有连接需要处理,退出循环 break; } } }