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 1024
const 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 1024
const 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;
}