通过多线程或多进程可以减弱阻塞IO的负面作用。
/* For sockaddr_in */ #include <netinet/in.h> /* For socket functions */ #include <sys/socket.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #define MAX_LINE 16384 char rot13_char(char c) { /* We don't want to use isalpha here; setting the locale would change * which characters are considered alphabetical. */ if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M')) return c + 13; else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z')) return c - 13; else return c; } void child(int fd) { char outbuf[MAX_LINE+1]; size_t outbuf_used = 0; ssize_t result; while (1) { char ch; result = recv(fd, &ch, 1, 0); if (result == 0) { break; } else if (result == -1) { perror("read"); break; } /* We do this test to keep the user from overflowing the buffer. */ if (outbuf_used < sizeof(outbuf)) { outbuf[outbuf_used++] = rot13_char(ch); } if (ch == '\n') { send(fd, outbuf, outbuf_used, 0); outbuf_used = 0; continue; } } } void run(void) { int listener; struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; sin.sin_port = htons(40713); listener = socket(AF_INET, SOCK_STREAM, 0); #ifndef WIN32 { int one = 1; setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); } #endif if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) { perror("bind"); return; } if (listen(listener, 16)<0) { perror("listen"); return; } while (1) { struct sockaddr_storage ss; socklen_t slen = sizeof(ss); int fd = accept(listener, (struct sockaddr*)&ss, &slen); if (fd < 0) { perror("accept"); } else { if (fork() == 0) { child(fd); exit(0); } } } } int main(int c, char **v) { run(); return 0; }
1. 函数 rot13_char(char) 是用来转换字符的,{A,a}~{M,m} -> {N,n}~{Z,z} 其余符号不变
2. server监听端口并响应连接
2.1 listener = socket(AF_INET, SOCKET_STREAM, 0) 创建套接字
2.2 bind(listener, (sockaddr*)&sin, size(sin)) 绑定套接字到端口
2.3 listen(listener, 16) 开始监听
2.4 struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listener, (struct sockaddr*)&ss, &slen);
每个fd标识响应的一个请求
2.5 if (fork() == 0) { child(fd); exit(0);}
创建一个子进程,响应一个请求
3. server端子进程的处理函数
3.1 void child(int fd) 处理函数接口
3.2 result = recv(fd, &ch, 1, 0); 从client端读取一个字符
send(fd, outbuf, outbuf_used, 0); 发送一个字符串到client端
注
1. 创建子进程的过程
pid_t pid; switch (pid = fork()) { case -1: perror("The fork failed!"; break; case 0: printf("I'm a child!"; system("touch /home/tom/abc.txt"; _exit(0); default: printf("Child's pid is %d\n",pid); }
程序会输出default的内容,并且创建 abc.txt
原因是:创建子进程时,子进程复制了父进程的内容。子进程和父进程同时进入switch语句,但父子进程的pid并不同,因此进入不同的case。