select模型用来轮询处理多个连接的读写
- 给文件描述符设置读写集合
- 读完之后将对应描述符放入写集合内,下一次循环到来时处理被放入写集合内所有描述符
问题:
- 下面server代码在gdb打断的情况下,无法成功进入FD_SET(fds[i], &reads)条件判断
- 如果直接运行server,再用client连接的话会进入阻塞状态,这里怀疑是某个系统调用阻塞住了进程
- 因为accept之后,server没有做read调用,client在write之后阻塞在read
- select是否为阻塞调用取决于timeout [ref]
- client一旦进程退出主动断开连接,server就会卡死,server.c这里没有做这块逻辑处理
client.c
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/time.h>#include <unistd.h>#define LEN 1024const int PORT = 8080;const char *IP = "127.0.0.1";struct sockaddr_in server;int clientSock;char buf[LEN];int main() {clientSock = socket(AF_INET, SOCK_STREAM, 0);if (clientSock < 0) {perror("socket");exit(0);}server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(IP);server.sin_port = htons(PORT);if (0 > connect(clientSock, (struct sockaddr *)&server, sizeof(server))){perror("connect");exit(0);}while (1) {memset(buf, 0, LEN);printf("please input: ");gets(buf);write(clientSock, buf, strlen(buf));memset(buf, 0, LEN);int ret = read(clientSock, buf, LEN);buf[ret] = 0;printf("[+]: %s\n", buf);}return 0;}
server.c
#include <sys/time.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/types.h>#include <unistd.h>#include <signal.h>#define LEN 1024const int PORT = 8080;struct sockaddr_in client;struct sockaddr_in local;int listenSock;int linkSock = -1;int fds[64];int size_client = sizeof(client);int ListenSock() {listenSock = socket(AF_INET, SOCK_STREAM, 0);if (listenSock < 0) {perror("socket");exit(1);}local.sin_family = AF_INET;local.sin_port = htons(PORT);local.sin_addr.s_addr = htonl(INADDR_ANY);if (0 > bind(listenSock, (struct sockaddr *)&local, sizeof(local))) {perror("bind");exit(2);}if (0 > listen(listenSock, 5)) {perror("listen");exit(3);}return listenSock;}void Shutdown(int arg) {int i;for (i = 0; i < 64; i++) {close(fds[i]);}printf("[-] shutdown !\n");}int main() {listenSock = ListenSock();signal(SIGTERM, Shutdown);char buf[LEN];memset(buf, 0, LEN);while (1) {fd_set reads, writes;int fds_max;int i = 0;int fds_num = sizeof(fds) / sizeof(fds[0]);for (; i < fds_num; i++) {fds[i] = -1;}fds[0] = listenSock;fds_max = fds[0];struct timeval times;while (1) {FD_ZERO(&reads);FD_ZERO(&writes);FD_SET(listenSock, &reads);times.tv_sec = 1;times.tv_usec = 0;for (i = 1; i < fds_num; i++) {if (fds[i] > 0) {FD_SET(fds[i], &reads);if (fds[i] > fds_max) {fds_max = fds[i];}}}switch (select(fds_max + 1, &reads, &writes, NULL, ×)) {case 0:printf("[-] timeout\n");break;case -1:perror("select");break;default:for (i = 0; i < fds_num; i++) {if (fds[i] == listenSock && FD_ISSET(fds[i], &reads)) {linkSock = accept(listenSock, (struct sockaddr *)&client, &size_client);if (0 > linkSock) {perror("accept");continue;}for (i = 0; i < fds_max; i++) {if (0 > fds[i]) {fds[i] = linkSock;FD_SET(linkSock, &writes);break;}}if (i == fds_max - 1) {printf("[!] fds is full. Please close some links to keep program running successfully.\n");}}else if (fds[i] > 0 && FD_ISSET(fds[i], &writes)) {memset(buf, 0, LEN);int ret = read(fds[i], buf, LEN);if (0 > ret) {perror("read");continue;}else if (0 == ret) {printf("[-] client is closed\n");continue;}else {printf("[+] %s\n", buf);}if (0 > write(fds[i], buf, strlen(buf))) {perror("write");continue;}}}break;}}}return 0;}
