当前位置: 首页 > news >正文

适合个人网站wordpress自定义上传图片

适合个人网站,wordpress自定义上传图片,dede 企业网站模板下载,互联网营销方案策划写作概述 TCP客户端/服务器程序示例是执行如下步骤的一个回射服务器#xff1a; 客户端从标准输入读入一行文本#xff0c;并写给服务器。服务器从网络输入读入这行文本#xff0c;并回射给客户端。客户端从网络输入读入这行回射文本#xff0c;并显示在标准输出上。 TCP服务器…概述 TCP客户端/服务器程序示例是执行如下步骤的一个回射服务器 客户端从标准输入读入一行文本并写给服务器。服务器从网络输入读入这行文本并回射给客户端。客户端从网络输入读入这行回射文本并显示在标准输出上。 TCP服务器程序 #include stdio.h #include stdlib.h #include unistd.h #include string.h #include time.h #include errno.h #include arpa/inet.h #include arpa/inet.h#define MAXLINE 4096 #define SERV_PORT 9877 #define LISTENQ 1024 #define SA struct sockaddr// 从客户端读入数据并把它们回射给客户端 void str_echo(int sockfd) {ssize_t n;char buf[MAXLINE]; again:// 从套接字读入数据// 套接字中接收缓冲区和发送缓冲区是分开的因此读和写不会发生混淆while ((n read(sockfd, buf, MAXLINE)) 0)write(sockfd, buf, n); // 把套接字中的内容回射给客户端// 如果n0表示读取数据出错或到达文件末尾// 如果errno等于EINTR表示读取操作被信号中断// 如果上述两个条件同时满足则重新尝试读取数据if (n 0 errno EINTR)goto again;// 如果表示文件描述符到达文件末尾else if (n 0)printf(str_echo: read error); }int main(int argc, char **argv) {int listenfd, connfd;pid_t childpid;socklen_t clilen;struct sockaddr_in cliaddr, servaddr;/* --------------------------------------------- *///1) 创建一个TCP连接套接字listenfd socket(AF_INET, SOCK_STREAM, 0);if (listenfd 0) {printf(socket error);return -1;}/* --------------------------------------------- *///2) 把服务器对应端口绑定到套接字 bzero(servaddr, sizeof(servaddr)); // 开辟内存servaddr.sin_family AF_INET; // 地址族// 指定IP地址为INADDR_ANY这样要是服务器主机有多个网络接口服务器进程就可以在任意网络接口上接受客户端连接servaddr.sin_addr.s_addr htonl(INADDR_ANY);servaddr.sin_port htons(SERV_PORT);if (bind(listenfd, (SA *) servaddr, sizeof(servaddr)) 0) {printf(bind error);return -1;}/* --------------------------------------------- *///3) 把套接字转换为监听套接字// LISTENQ表示系统内核允许在这个监听描述符上排队的最大客户端连接数if(listen(listenfd, LISTENQ) 0) {printf(listen error);return -1;}/* --------------------------------------------- *///4) 接受客户端连接发送应答for ( ; ; ) {clilen sizeof(cliaddr);// connfd为已连接描述符用于和客户端进行通信connfd accept(listenfd, (SA *) cliaddr, clilen);if(connfd 0) {printf(accept error);return -1;}if ((childpid fork()) 0) {// 子进程关闭监听套接字if (close(listenfd) -1) {printf(child close listenfd error);return -1; }str_echo(connfd); // 子进程处理客户端请求exit(0); // 清理描述符 }/* --------------------------------------------- *///5) 父进程关闭已连接套接字if (close(connfd) -1) {printf(parent close connfd error);return -1;}} } TCP客户端程序 #include stdio.h #include stdlib.h #include unistd.h #include string.h #include arpa/inet.h #include sys/socket.h /* basic socket definitions */#define MAXLINE 4096 #define SERV_PORT 9877 #define SA struct sockaddr char *Fgets(char *ptr, int n, FILE *stream) {char *rptr;// 当遇到文件结束符或错误时fgets函数将返回一个空指针于是客户端处理循环终止if ( (rptr fgets(ptr, n, stream)) NULL ferror(stream)) {printf(fgets error);return NULL; }return (rptr); }ssize_t readline(int fd, void *vptr, size_t maxlen) {ssize_t n, rc;char c, *ptr;ptr vptr;for (n 1; n maxlen; n) {if ( (rc read(fd, c, 1)) 1) {*ptr c;if (c \n)break;} else if (rc 0) {if (n 1)return(0); /* EOF, no data read */elsebreak; /* EOF, some data was read */} elsereturn(-1); /* error */}*ptr 0;return(n); } /* end readline */void str_cli(FILE *fp, int sockfd) {char sendline[MAXLINE], recvline[MAXLINE];// 从控制台读入一行文本while (Fgets(sendline, MAXLINE, fp) ! NULL) {// 把该行文本发送给服务器if (write(sockfd, sendline, strlen(sendline)) ! strlen(sendline)) {printf(writen error);return; }// 从服务器读入回射行if (readline(sockfd, recvline, MAXLINE) 0){printf(readline error);return; }// 把它写到标准输出if (fputs(recvline, stdout) EOF) {printf(fputs error);return; }} }int main(int argc, char **argv) {int sockfd;char recvline[MAXLINE 1];struct sockaddr_in servaddr;if (argc ! 2)exit(1);/* --------------------------------------------- *///1) 创建一个TCP连接套接字sockfd socket(AF_INET, SOCK_STREAM, 0);if (sockfd 0) {printf(socket error);return -1;}/* --------------------------------------------- *///2) 指定服务器的IP地址和端口bzero(servaddr, sizeof(servaddr)); // 初始化内存servaddr.sin_family AF_INET; // 地址族servaddr.sin_port htons(SERV_PORT); // 时间获取服务器端口为13// 注意此处的IP和端口是服务器的IP和端口// 把点分十进制的IP地址如206.168.112.96转化为合适的格式if (inet_pton(AF_INET, argv[1], servaddr.sin_addr) 0) {printf(inet_pton error for %s, argv[1]);return -1;}/* --------------------------------------------- *///3) 建立客户端sockfd与服务器servaddr的连接TCP连接if (connect(sockfd, (SA *) servaddr, sizeof(servaddr)) 0) {printf(connect error);return -1;}// 完成剩余部分的客户端处理工作str_cli(stdin, sockfd);/* --------------------------------------------- *///5) 终止程序运行关闭该进程打开的所有描述符和TCP套接字exit(0); } 正常启动 1启动TCP服务器程序 gcc -o tcpserv tcpserv.c gcc -o tcpcli tcpcli.c ./tcpserv 服务器启动后它调用socked、bind、listen和accept并阻塞于accept调用。 2启动TCP客户端程序 ./tcpcli 127.0.0.1// 输入字符串 kaikaixinxinxuebiancheng 启动客户端程序并指定服务器主机的IP地址。客户端调用socket和connect后者引起TCP三次握手过程。当三次握手完成后客户端中的connect和服务器中的accept均返回连接于是被建立。 接着发生步骤如下 客户端调用str_cli函数该函数将阻塞于fgets调用因为我们还未曾键入过一行文本。当服务器中的accept返回时服务器调用fork再由子进程调用str_echo。该函数调用readlinereadline调用read而read在等待客户端送入一行文本期间阻塞。服务器父进程再次调用accept并阻塞等待下一个客户端连接。 连接建立后不论在客户端中输入什么都会回射到它的标准输出中。 接着在终端输入EOF字符CtrlD以终止客户端。 此时如果立刻执行netstat命令则将看到如下结果 // 服务器本地端口为9877客户端本地端口为42758 netstat -a | grep 9877 当前连接的客户端它的本地端口号为42758进入了TIME_WAIT状态而监听服务器仍在等待另一个客户端连接。 正常终止 正常终止客户端与服务器步骤 1当键入EOF字符时fgets返回一个空指针于是str_cli函数返回。 2当str_cli返回到客户端的main函数时main通过调用exit终止。 3进程终止处理的部分工作是关闭所有打开的描述符因此客户端打开的套接字由内核关闭。这导致客户端TCP发送一个FIN给服务器服务器则以ACK响应这就是TCP连接终止序列的前半部分。至此服务器套接字处于CLOSE_WAIT状态客户端套接字则处于FIN_WAIT_2状态。 4当服务器TCP接收FIN时服务器子进程阻塞于read调用于是read返回0这导致str_echo函数返回服务器子进程的main函数。 5服务器子进程通过调用exit来终止。 6服务器子进程中打开的所有描述符包括已连接套接字随之关闭。子进程关闭已连接套接字时会引发TCP连接终止序列的最后两个分节一个从服务器到客户端的FIN和一个从客户端到服务器的ACK。至此连接完全终止客户端套接字进入TIME_WAIT状态允许老的重复分节在网络中消逝。 7进程终止处理的另一部分内容是在服务器进程终止时给父进程发送一个SIGCHLD信号这一点在上述程序示例中发生了但是没有在代码中捕获该信号而信号的默认行为是被忽略。既然父进程未加处理子进程于是进入僵死状态僵尸进程。可以通过ps命令进行验证 // 查看当前终端编号 tty// 查看子进程状态 ps -t /dev/pts/0 -o pid,ppid,tty,stat,args,wchan 查看结果 子进程状态表现为Z表示僵死。针对僵死进程僵尸进程必须清理。 POSIX信号处理 信号signal就是告知某个进程发生了某个事件的通知有时也称为软件中断。信号通常是异步发生的也就是说进程预先不知道信号的准确发生时刻。 注意 1信号可以由一个进程发给另一个进程或自身。 2信号可以由内核发给某个进程。 上一小节提到的SIGCHLD信号就是由内核在任何一个进程终止时发给它的父进程的一个信号。 每个信号都有一个与之关联的处置也称为行为。 SIGCHLD信号处理 思考为什么必须要处理僵死进程 答因为僵死进程占用内核空间最终可能导致耗尽进程资源。所以无论何时针对fork出来的子进程都得使用wait函数处理它们以防止它们变为僵死进程。 TCP服务器程序 #include stdio.h #include stdlib.h #include unistd.h #include string.h #include time.h #include errno.h #include arpa/inet.h #include arpa/inet.h #include signal.h #include sys/types.h #include sys/wait.h#define MAXLINE 4096 #define SERV_PORT 9877 #define LISTENQ 1024 #define SA struct sockaddrtypedef void Sigfunc(int); /* for signal handlers */// SIGCHLD信号处理函数防止子进程变为僵死进程 void sig_chld(int signo) {pid_t pid;int stat;// 等待子进程结束并获取子进程的PID和退出状态pid wait(stat);// 在此处调用诸如printf这样的标准I/O是不合适的此处只是作为查看子进程何时终止的诊断手段printf(child %d terminated\n, pid);return; }Sigfunc *signal(int signo, Sigfunc *func) {// 定义信号动作struct sigaction act, oact;act.sa_handler func; // 设置信号处理函数sigemptyset(act.sa_mask); // 清空信号掩码集act.sa_flags 0; // 设置信号处理方式为默认if (signo SIGALRM) { #ifdef SA_INTERRUPTact.sa_flags | SA_INTERRUPT; /* SunOS 4.x */ #endif} else { #ifdef SA_RESTARTact.sa_flags | SA_RESTART; /* SVR4, 44BSD */ #endif}if (sigaction(signo, act, oact) 0)return(SIG_ERR);return(oact.sa_handler); } /* end signal */// 捕捉指定信号并采取行动 Sigfunc *Signal(int signo, Sigfunc *func) /* for our signal() function */ {Sigfunc *sigfunc;if ( (sigfunc signal(signo, func)) SIG_ERR) {printf(signal error); }return(sigfunc); }// 从客户端读入数据并把它们回射给客户端 void str_echo(int sockfd) {ssize_t n;char buf[MAXLINE]; again:// 从套接字读入数据// 套接字中接收缓冲区和发送缓冲区是分开的因此读和写不会发生混淆while ((n read(sockfd, buf, MAXLINE)) 0)write(sockfd, buf, n); // 把套接字中的内容回射给客户端// 如果n0表示读取数据出错或到达文件末尾// 如果errno等于EINTR表示读取操作被信号中断// 如果上述两个条件同时满足则重新尝试读取数据if (n 0 errno EINTR)goto again;// 如果表示文件描述符到达文件末尾else if (n 0)printf(str_echo: read error); }int main(int argc, char **argv) {int listenfd, connfd;pid_t childpid;socklen_t clilen;struct sockaddr_in cliaddr, servaddr;/* --------------------------------------------- *///1) 创建一个TCP连接套接字listenfd socket(AF_INET, SOCK_STREAM, 0);if (listenfd 0) {printf(socket error);return -1;}/* --------------------------------------------- *///2) 把服务器对应端口绑定到套接字 bzero(servaddr, sizeof(servaddr)); // 开辟内存servaddr.sin_family AF_INET; // 地址族// 指定IP地址为INADDR_ANY这样要是服务器主机有多个网络接口服务器进程就可以在任意网络接口上接受客户端连接servaddr.sin_addr.s_addr htonl(INADDR_ANY);servaddr.sin_port htons(SERV_PORT);if (bind(listenfd, (SA *) servaddr, sizeof(servaddr)) 0) {printf(bind error);return -1;}/* --------------------------------------------- *///3) 把套接字转换为监听套接字// LISTENQ表示系统内核允许在这个监听描述符上排队的最大客户端连接数if(listen(listenfd, LISTENQ) 0) {printf(listen error);return -1;}// 捕捉指定信号并采取行动Signal(SIGCHLD, sig_chld); /* must call waitpid() *//* --------------------------------------------- *///4) 接受客户端连接发送应答for ( ; ; ) {clilen sizeof(cliaddr);// connfd为已连接描述符用于和客户端进行通信connfd accept(listenfd, (SA *) cliaddr, clilen);if(connfd 0) {if (errno EINTR) {continue; // 重启被中断的accept } else {printf(accept error);return -1; }}if ((childpid fork()) 0) {// 子进程关闭监听套接字if (close(listenfd) -1) {printf(child close listenfd error);return -1; }str_echo(connfd); // 子进程处理客户端请求exit(0); // 清理描述符 }/* --------------------------------------------- *///5) 父进程关闭已连接套接字if (close(connfd) -1) {printf(parent close connfd error);return -1;}} } 注意如果connect函数返回EINTR则不能重启否则将立即返回一个错误。当connect被一个捕获的信号中断而且不自动重启时必须调用select来等待连接完成。 TCP客户端程序 #include stdio.h #include stdlib.h #include unistd.h #include string.h #include arpa/inet.h #include sys/socket.h /* basic socket definitions */#define MAXLINE 4096 #define SERV_PORT 9877 #define SA struct sockaddr char *Fgets(char *ptr, int n, FILE *stream) {char *rptr;// 当遇到文件结束符或错误时fgets函数将返回一个空指针于是客户端处理循环终止if ( (rptr fgets(ptr, n, stream)) NULL ferror(stream)) {printf(fgets error);return NULL; }return (rptr); }ssize_t readline(int fd, void *vptr, size_t maxlen) {ssize_t n, rc;char c, *ptr;ptr vptr;for (n 1; n maxlen; n) {if ( (rc read(fd, c, 1)) 1) {*ptr c;if (c \n)break;} else if (rc 0) {if (n 1)return(0); /* EOF, no data read */elsebreak; /* EOF, some data was read */} elsereturn(-1); /* error */}*ptr 0;return(n); } /* end readline */void str_cli(FILE *fp, int sockfd) {char sendline[MAXLINE], recvline[MAXLINE];// 从控制台读入一行文本while (Fgets(sendline, MAXLINE, fp) ! NULL) {// 把该行文本发送给服务器if (write(sockfd, sendline, strlen(sendline)) ! strlen(sendline)) {printf(writen error);return; }// 从服务器读入回射行if (readline(sockfd, recvline, MAXLINE) 0){printf(readline error);return; }// 把它写到标准输出if (fputs(recvline, stdout) EOF) {printf(fputs error);return; }} }int main(int argc, char **argv) {int sockfd;char recvline[MAXLINE 1];struct sockaddr_in servaddr;if (argc ! 2)exit(1);/* --------------------------------------------- *///1) 创建一个TCP连接套接字sockfd socket(AF_INET, SOCK_STREAM, 0);if (sockfd 0) {printf(socket error);return -1;}/* --------------------------------------------- *///2) 指定服务器的IP地址和端口bzero(servaddr, sizeof(servaddr)); // 初始化内存servaddr.sin_family AF_INET; // 地址族servaddr.sin_port htons(SERV_PORT); // 时间获取服务器端口为13// 注意此处的IP和端口是服务器的IP和端口// 把点分十进制的IP地址如206.168.112.96转化为合适的格式if (inet_pton(AF_INET, argv[1], servaddr.sin_addr) 0) {printf(inet_pton error for %s, argv[1]);return -1;}/* --------------------------------------------- *///3) 建立客户端sockfd与服务器servaddr的连接TCP连接if (connect(sockfd, (SA *) servaddr, sizeof(servaddr)) 0) {printf(connect error);return -1;}// 完成剩余部分的客户端处理工作str_cli(stdin, sockfd);/* --------------------------------------------- *///5) 终止程序运行关闭该进程打开的所有描述符和TCP套接字exit(0); } 执行流程 // 启动服务器程序 ./tcpserv02 // 启动客户端程序 ./tcpserv02 127.0.0.1 hi there hi there ^D 键入EOF字符 child 16942 terminated 信号处理函数中的printf输出 accept error:Interrupted system call main函数终止执行 具体各步骤如下 1键入EOF字符终止客户端。客户端发送一个FIN给服务器服务器响应一个ACK。 2收到客户端的FIN导致服务器TCP递送一个EOF给子进程阻塞中的readline从而子进程终止。 3当SIGCHLD信号递交时父进程阻塞与accept调用。sig_chld函数信号处理函数执行其wait调用渠道子进程的PID和终止状态随后是printf调用最后返回。 4既然该信号是在父进程阻塞于慢系统调用accept时由父进程捕获的内核就会使accept返回一个EINTR错误被中断的系统调用。父进程不处理该错误于是父进程中止无法接受新的连接。 wait和waitpid函数 问1什么是孤儿进程什么是僵尸进程二者分别会带来什么危害 答 1孤儿进程如果父进程在子进程结束前退出那么子进程就会成为孤儿进程。在这种情况下父进程没有机会调用wait或waitpid函数。每当出现一个孤儿进程的时候内核就把孤儿进程交给init进程管理。即init进程会代替该孤儿进程的父进程回收孤儿进程的资源因此孤儿进程并不会有什么危害。 2僵尸进程如果子进程结束时父进程未调用wait或waitpid函数回收其资源那么子进程就会称为僵尸进程。如果释放僵尸进程的相关资源其进程号就会被一致占用但是系统所能使用的进程号是有限的如果产生大量的僵尸进程最终将会因为没有可用的进程号而导致系统不能产生新的进程所以应该避免僵尸进程的产生。 问2为什么父进程需要在fork之前调用wait或waitpid函数等待子进程退出 答父进程使用fork函数创建子进程是为了处理多个客户端连接。fork会创建一个与父进程几乎完全相同的子进程包括内存空间、文件描述符等。这样做的好处是父进程可以继续监听新的连接请求而子进程可以专注于处理已接受的连接。因此父进程调用wait或waitpid函数主要是为了防止出现僵尸进程。 wait和waitpid函数 #include sys/wait.h pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int options);返回若成功则返回已终止的进程ID若出错则返回0或-1 函数wait和waitpid均返回两个值已终止的进程ID号以及通过statloc指针返回的子进程终止状态一个整数。 可以调用三个宏来检查终止状态并辨别子进程是正常终止、由某个信号杀死还是仅仅由作业控制停止而已。另有些宏用于接着获取子进程的推出状态、杀死子进程的信号值或停止子进程的作业控制号值。 如果调用wait的进程没有已终止的子进程不过有一个或多个子进程仍在执行那么wait将阻塞到有子进程第一个终止为止。 wait和waitpid的区别 客户端程序 TCP客户端程序修改后 #include stdio.h #include stdlib.h #include unistd.h #include string.h #include arpa/inet.h #include sys/socket.h /* basic socket definitions */#define MAXLINE 4096 #define SERV_PORT 9877 #define SA struct sockaddr char *Fgets(char *ptr, int n, FILE *stream) {char *rptr;// 当遇到文件结束符或错误时fgets函数将返回一个空指针于是客户端处理循环终止if ( (rptr fgets(ptr, n, stream)) NULL ferror(stream)) {printf(fgets error);return NULL; }return (rptr); }ssize_t readline(int fd, void *vptr, size_t maxlen) {ssize_t n, rc;char c, *ptr;ptr vptr;for (n 1; n maxlen; n) {if ( (rc read(fd, c, 1)) 1) {*ptr c;if (c \n)break;} else if (rc 0) {if (n 1)return(0); /* EOF, no data read */elsebreak; /* EOF, some data was read */} elsereturn(-1); /* error */}*ptr 0;return(n); } /* end readline */void str_cli(FILE *fp, int sockfd) {char sendline[MAXLINE], recvline[MAXLINE];// 从控制台读入一行文本while (Fgets(sendline, MAXLINE, fp) ! NULL) {// 把该行文本发送给服务器if (write(sockfd, sendline, strlen(sendline)) ! strlen(sendline)) {printf(writen error);return; }// 从服务器读入回射行if (readline(sockfd, recvline, MAXLINE) 0){printf(readline error);return; }// 把它写到标准输出if (fputs(recvline, stdout) EOF) {printf(fputs error);return; }} }int main(int argc, char **argv) {int sockfd[5];char recvline[MAXLINE 1];struct sockaddr_in servaddr;if (argc ! 2)exit(1);for (int i 0; i 5; i) {/* --------------------------------------------- *///1) 创建一个TCP连接套接字sockfd[i] socket(AF_INET, SOCK_STREAM, 0);if (sockfd 0) {printf(socket error);return -1;}/* --------------------------------------------- *///2) 指定服务器的IP地址和端口bzero(servaddr, sizeof(servaddr)); // 初始化内存servaddr.sin_family AF_INET; // 地址族servaddr.sin_port htons(SERV_PORT); // 时间获取服务器端口为13// 注意此处的IP和端口是服务器的IP和端口// 把点分十进制的IP地址如206.168.112.96转化为合适的格式if (inet_pton(AF_INET, argv[1], servaddr.sin_addr) 0) {printf(inet_pton error for %s, argv[1]);return -1;}/* --------------------------------------------- *///3) 建立客户端sockfd与服务器servaddr的连接TCP连接if (connect(sockfd[i], (SA *) servaddr, sizeof(servaddr)) 0) {printf(connect error);return -1;}}// 完成剩余部分的客户端处理工作str_cli(stdin, sockfd[0]);/* --------------------------------------------- *///5) 终止程序运行关闭该进程打开的所有描述符和TCP套接字exit(0); } 客户端建立5个与服务器的连接随后在调用str_cli函数时仅用第一个连接sockfd[0]。建立多个连接的目的是从并发服务器上派生多个子进程如下图所示 当客户端终止时所有打开的文件描述符由内核自动关闭无需调用close仅调用exit且所有5个连接基本在同一时刻终止。这就引发了5个FIN每个连接一个它们反过来使服务器的5个子进程基本在同一时刻终止。这又导致差不多在同一时刻有5个SIGCHLD信号递交给父进程如图所示 注意如上所述由于调用了exit函数5个连接几乎同时产生SIGCHLD信号即多个SIGCHLD信号同时递交给服务器。 测试结果 ./tcpserv 启动服务器程序 ./tcpcli 127.0.0.1 启动客户端程序 hello hello ^D 键入EOF字符 child 31591 terminated 服务器输出 从执行结果可以看出只有一个printf输出而并非5个即信号处理函数只处理了一个SIGCHLD信号剩下四个子进程变为僵尸进程。 问1为什么只处理了一个SIGCHLD信号 答建立一个信号处理函数并在其中调用wait并不足以防止出现僵尸进程。因为所有5个信号都在信号处理函数执行之前产生而信号处理函数只执行一次因为Unix信号一般不排队。更严重的是本问题是不确定的。因为本实验是在同一个主机上信号处理函数执行1次留下4个僵尸进程。但是如果客户端程序和服务端程序不在同一个主机上那么信号处理函数一般执行2次一次是第一个产生的信号引起的由于另外4个信号在信号处理函数第一次执行时发生因此该处理函数仅仅再被调用一次从而留下3个僵尸进程。不过有的时候依赖于FIN到达服务器主机的时机信号处理函数可能会执行3次甚至4次。 问2如何让信号处理函数调用多次以防止出现僵尸进程 答调用waitpid而不是wait函数。当在一个循环内调用waitpid以获取所有已终止子进程的状态时必须指定WNOHANG选项它告知waitpid在有尚未终止的子进程在运行时不要阻塞。不能在循环内调用wait因为没有办法防止wait在正运行的子进程尚有未终止时阻塞。 服务端程序 修改后的服务端程序 #include stdio.h #include stdlib.h #include unistd.h #include string.h #include time.h #include errno.h #include arpa/inet.h #include arpa/inet.h #include signal.h #include sys/types.h #include sys/wait.h#define MAXLINE 4096 #define SERV_PORT 9877 #define LISTENQ 1024 #define SA struct sockaddrtypedef void Sigfunc(int); /* for signal handlers */// SIGCHLD信号处理函数防止子进程变为僵死进程 void sig_chld(int signo) {pid_t pid;int stat;// 等待子进程结束并获取子进程的PID和退出状态while (pid waitpid(-1, stat, WNOHANG)) 0) {// 在此处调用诸如printf这样的标准I/O是不合适的此处只是作为查看子进程何时终止的诊断手段printf(child %d terminated\n, pid);}return; }Sigfunc *signal(int signo, Sigfunc *func) {// 定义信号动作struct sigaction act, oact;act.sa_handler func; // 设置信号处理函数sigemptyset(act.sa_mask); // 清空信号掩码集act.sa_flags 0; // 设置信号处理方式为默认if (signo SIGALRM) { #ifdef SA_INTERRUPTact.sa_flags | SA_INTERRUPT; /* SunOS 4.x */ #endif} else { #ifdef SA_RESTARTact.sa_flags | SA_RESTART; /* SVR4, 44BSD */ #endif}if (sigaction(signo, act, oact) 0)return(SIG_ERR);return(oact.sa_handler); } /* end signal */// 捕捉指定信号并采取行动 Sigfunc *Signal(int signo, Sigfunc *func) /* for our signal() function */ {Sigfunc *sigfunc;if ( (sigfunc signal(signo, func)) SIG_ERR) {printf(signal error); }return(sigfunc); }// 从客户端读入数据并把它们回射给客户端 void str_echo(int sockfd) {ssize_t n;char buf[MAXLINE]; again:// 从套接字读入数据// 套接字中接收缓冲区和发送缓冲区是分开的因此读和写不会发生混淆while ((n read(sockfd, buf, MAXLINE)) 0)write(sockfd, buf, n); // 把套接字中的内容回射给客户端// 如果n0表示读取数据出错或到达文件末尾// 如果errno等于EINTR表示读取操作被信号中断// 如果上述两个条件同时满足则重新尝试读取数据if (n 0 errno EINTR)goto again;// 如果表示文件描述符到达文件末尾else if (n 0)printf(str_echo: read error); }int main(int argc, char **argv) {int listenfd, connfd;pid_t childpid;socklen_t clilen;struct sockaddr_in cliaddr, servaddr;/* --------------------------------------------- *///1) 创建一个TCP连接套接字listenfd socket(AF_INET, SOCK_STREAM, 0);if (listenfd 0) {printf(socket error);return -1;}/* --------------------------------------------- *///2) 把服务器对应端口绑定到套接字 bzero(servaddr, sizeof(servaddr)); // 开辟内存servaddr.sin_family AF_INET; // 地址族// 指定IP地址为INADDR_ANY这样要是服务器主机有多个网络接口服务器进程就可以在任意网络接口上接受客户端连接servaddr.sin_addr.s_addr htonl(INADDR_ANY);servaddr.sin_port htons(SERV_PORT);if (bind(listenfd, (SA *) servaddr, sizeof(servaddr)) 0) {printf(bind error);return -1;}/* --------------------------------------------- *///3) 把套接字转换为监听套接字// LISTENQ表示系统内核允许在这个监听描述符上排队的最大客户端连接数if(listen(listenfd, LISTENQ) 0) {printf(listen error);return -1;}// 捕捉指定信号并采取行动Signal(SIGCHLD, sig_chld); /* must call waitpid() *//* --------------------------------------------- *///4) 接受客户端连接发送应答for ( ; ; ) {clilen sizeof(cliaddr);// connfd为已连接描述符用于和客户端进行通信connfd accept(listenfd, (SA *) cliaddr, clilen);if(connfd 0) {if (errno EINTR) {continue; // 重启被中断的accept } else {printf(accept error);return -1; }}if ((childpid fork()) 0) {// 子进程关闭监听套接字if (close(listenfd) -1) {printf(child close listenfd error);return -1; }str_echo(connfd); // 子进程处理客户端请求exit(0); // 清理描述符 }/* --------------------------------------------- *///5) 父进程关闭已连接套接字if (close(connfd) -1) {printf(parent close connfd error);return -1;}} } 小结 问SIGCHLD信号是怎么产生的有什么作用 答SIGCHLD 信号是由操作系统产生的当一个子进程结束无论是正常退出还是被终止时操作系统都会向父进程发送这个信号。这个信号的目的是通知父进程子进程的状态已经改变父进程可以采取相应的行动比如回收子进程使用的资源。 注意父进程调用wait函数时会阻塞整个父进程的执行直到某一个或几个子进程结束才会结束阻塞。上述服务器程序是通过异步调用wait函数所以看上去不是那么直观非异步调用wait如下 for ( ; ; ) {clilen sizeof(cliaddr);// connfd为已连接描述符用于和客户端进行通信connfd accept(listenfd, (SA *) cliaddr, clilen);if(connfd 0) {if (errno EINTR) {continue; // 重启被中断的accept } else {printf(accept error);return -1; }}if ((childpid fork()) 0) {// 子进程关闭监听套接字if (close(listenfd) -1) {printf(child close listenfd error);return -1; }str_echo(connfd); // 子进程处理客户端请求exit(0); // 清理描述符 }// 等待子进程结束并回收子进程资源int status;wait(status);/* --------------------------------------------- *///5) 父进程关闭已连接套接字if (close(connfd) -1) {printf(parent close connfd error);return -1;} } UNIX网络编程总结 1当fork子进程时必须捕获SIGCHLD信号。 2当捕获信号时父进程必须处理被中断的系统调用如accept函数。 3SIGCHLD的信号处理函数必须正确书写并使用waitpid函数以免留下僵尸进程。 如果需要代码包请在评论区留言  如果需要代码包请在评论区留言  如果需要代码包请在评论区留言
http://www.dnsts.com.cn/news/178812.html

相关文章:

  • 一级a做爰片免费网站性恔郑州网络营销
  • 帝国网站管理系统枣庄企业网站推广
  • 个人备案经营网站备案注册公司电话咨询
  • 网站正在备案中沧州快速关键词排名优化
  • c 网站开发流程域名抢注
  • 装修网站合作广州网站建站公司
  • 自己创建网站赚钱文章类网站后台
  • 如何做好网站建设工作百度关键词竞价排名
  • 扬州广陵区城乡建设局网站宜兴做网站
  • 网站推广方式组合学做网站论坛会员
  • wordpress网站商务通wordpress好用的插件
  • 网站制作方案要点二级建造师考试报名官网
  • 厦门仿站定制模板建站东营市建设管理局
  • 怎么seo网站排名广州微型网站建设
  • 四川 网站建设番禺网站建设
  • dw做网站步骤一人可以申请两个营业执照吗
  • 白云企业网站建设百度站长平台链接提交
  • 自动做网站设计公司logo最重要的是什么
  • 道客网站建设推广制作相册软件
  • 中文网站建设方案佛山服务类网站建设
  • 商城网站建设 优帮云做网站推广前途
  • 家纺公司网站模版小学校园门户网站建设方案
  • 建筑网站翻译编辑怎么做兼职类网站吗
  • 重庆网站推广营销代理视频一页网站怎么做
  • 做百度移动网站排名百度新闻官网
  • 网站建设求职简历模板广州的互联网公司
  • 公会网站建设wordpress放视频教程
  • 单位门户网站是什么意思做车身拉花的网站
  • 网站有很多304状态码搬瓦工快照恢复wordpress
  • 深圳做地铁的公司网站aso优化师主要是干嘛的