TCP回射程序

来源:互联网 发布:python编辑器 知乎 编辑:程序博客网 时间:2024/05/16 18:13

服务器:

#include<strings.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<stdarg.h>/* ANSI C header file */#include<syslog.h>/* for syslog() */#include<errno.h>#include<stdio.h>#include<string.h>#include<unistd.h>#include<stdlib.h>int daemon_proc;/* set nonzero by daemon_init() */#defineSAstruct sockaddr#defineMAXLINE4/* max text line length */#defineSERV_PORT 9877/* TCP and UDP */#defineLISTENQ1024/* 2nd argument to listen() */void err_doit(int errnoflag, int level, const char *fmt, va_list ap){int errno_save, n;char buf[MAXLINE + 1];errno_save = errno;/* value caller might want printed */#ifdefHAVE_VSNPRINTFvsnprintf(buf, MAXLINE, fmt, ap);/* safe */#elsevsprintf(buf, fmt, ap);/* not safe */#endifn = strlen(buf);if (errnoflag)snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));strcat(buf, "\n");if (daemon_proc) {syslog(level, "%s",buf);} else {fflush(stdout);/* in case stdout and stderr are the same */fputs(buf, stderr);fflush(stderr);}return;}void err_sys(const char *fmt, ...){va_list ap;va_start(ap, fmt);err_doit(1, LOG_ERR, fmt, ap);va_end(ap);exit(1);}int Socket(int family, int type, int protocol){int n;if ((n = socket(family, type, protocol)) < 0)err_sys("socket error");return (n);}void Bind(int fd, const struct sockaddr *sa, socklen_t salen){if (bind(fd, sa, salen) < 0)err_sys("bind error");}void Listen(int fd, int backlog){char *ptr;/*4can override 2nd argument with environment variable */if ((ptr = getenv("LISTENQ")) != NULL)backlog = atoi(ptr);if (listen(fd, backlog) < 0)err_sys("listen error");}int Accept(int fd, struct sockaddr *sa, socklen_t * salenptr){int n;again:if ((n = accept(fd, sa, salenptr)) < 0) {#ifdefEPROTOif (errno == EPROTO || errno == ECONNABORTED)// Protocol error or Connection aborted#elseif (errno == ECONNABORTED)#endifgoto again;elseerr_sys("accept error");}return (n);}pid_t Fork(void)Connection aborted{pid_t pid;if ((pid = fork()) == -1)err_sys("fork error");return (pid);}void Close(int fd){if (close(fd) == -1)err_sys("close error");}ssize_t/* Write "n" bytes to a descriptor. */writen(int fd, const void *vptr, size_t n){size_t nleft;ssize_t nwritten;const char *ptr;ptr = (const char *)vptr;nleft = n;while (nleft > 0) {if ((nwritten = write(fd, ptr, nleft)) <= 0) {if (nwritten < 0 && errno == EINTR)//表示中断,处理nwritten = 0;/* and call write() again */elsereturn (-1);/* error */}nleft -= nwritten;ptr += nwritten;}return (n);}void Writen(int fd, void *ptr, int nbytes){if (writen(fd, ptr, nbytes) != nbytes)err_sys("writen error");}void str_echo(int sockfd){ssize_t n;char buf[MAXLINE];again:while ((n = read(sockfd, buf, MAXLINE)) > 0)Writen(sockfd, buf, n);if (n < 0 && errno == EINTR)//若出错是因为被信号中断,继续读goto again;else if (n < 0)err_sys("str_echo: read error");else if(!n) //当read返回0时,不再读下去break;}int main(){int listenfd, connfd;pid_t childpid;socklen_t clilen;struct sockaddr_in cliaddr, servaddr;listenfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);Bind(listenfd, (SA *) & servaddr, sizeof(servaddr));Listen(listenfd, LISTENQ);for (;;) {clilen = sizeof(cliaddr);connfd = Accept(listenfd, (SA *) & cliaddr, &clilen);if ((childpid = Fork()) == 0) {/* child process */Close(listenfd);/* close listening socket */str_echo(connfd);/* process the request */exit(0);}Close(connfd);/* parent closes connected socket */}}

客户端:

#include<netinet/in.h>/* sockaddr_in{} and other Internet defns */#include<strings.h>#include<stdarg.h>/* ANSI C header file */#include<syslog.h>/* for syslog() */#include<errno.h>#include<string.h>#include<stdio.h>#include<stdlib.h>#include<arpa/inet.h>#include<unistd.h>#defineMAXLINE4096/* max text line length */#defineSERV_PORT 9877/* TCP and UDP */int daemon_proc;/* set nonzero by daemon_init() */#defineSAstruct sockaddrvoid err_doit(int errnoflag, int level, const char *fmt, va_list ap){int errno_save, n;char buf[MAXLINE + 1];errno_save = errno;/* value caller might want printed */#ifdefHAVE_VSNPRINTFvsnprintf(buf, MAXLINE, fmt, ap);/* safe */#elsevsprintf(buf, fmt, ap);/* not safe */#endifn = strlen(buf);if (errnoflag)snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));strcat(buf, "\n");if (daemon_proc) {syslog(level, "%s",buf);} else {fflush(stdout);/* in case stdout and stderr are the same */fputs(buf, stderr);fflush(stderr);}return;}void err_quit(const char *fmt, ...){va_list ap;va_start(ap, fmt);err_doit(0, LOG_ERR, fmt, ap);va_end(ap);exit(1);}void err_sys(const char *fmt, ...){va_list ap;va_start(ap, fmt);err_doit(1, LOG_ERR, fmt, ap);va_end(ap);exit(1);}int Socket(int family, int type, int protocol){int n;if ((n = socket(family, type, protocol)) < 0)err_sys("socket error");return (n);}void Inet_pton(int family, const char *strptr, void *addrptr){int n;if ((n = inet_pton(family, strptr, addrptr)) < 0)err_sys("inet_pton error for %s", strptr);/* errno set */else if (n == 0)err_quit("inet_pton error for %s", strptr);/* errno not set *//* nothing to return */}void Connect(int fd, const struct sockaddr *sa, socklen_t salen){if (connect(fd, sa, salen) < 0)err_sys("connect error");}ssize_t writen(int fd, const void *vptr, size_t n){size_t nleft;ssize_t nwritten;const char *ptr;ptr = (const char *)vptr;nleft = n;while (nleft > 0) {if ((nwritten = write(fd, ptr, nleft)) <= 0) {if (nwritten < 0 && errno == EINTR) //表示中断,处理nwritten = 0;/* and call write() again */elsereturn (-1);/* error */}nleft -= nwritten;ptr += nwritten;}return (n);}void Writen(int fd, void *ptr, int nbytes){if (writen(fd, ptr, nbytes) != nbytes)err_sys("writen error");}char *Fgets(char *ptr, int n, FILE * stream){char *rptr;if ((rptr = fgets(ptr, n, stream)) == NULL && ferror(stream))err_sys("fgets error");return (rptr);}void Fputs(const char *ptr, FILE * stream){if (fputs(ptr, stream) == EOF)err_sys("fputs error");}static int read_cnt;static char *read_ptr;static char read_buf[MAXLINE];static ssize_t my_read(int fd, char *ptr){if (read_cnt <= 0) {again:if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {if (errno == EINTR) //表示中断,处理goto again;return (-1);} else if (read_cnt == 0)  // close and recv FINreturn (0);read_ptr = read_buf;  }read_cnt--;*ptr = *read_ptr++;return (1);}ssize_t readline(int fd, void *vptr, int maxlen){ssize_t n, rc;char c, *ptr;ptr = (char *)vptr;for (n = 1; n < maxlen; n++) {if ((rc = my_read(fd, &c)) == 1) {*ptr++ = c;if (c == '\n')break;/* newline is stored, like fgets() */} else if (rc == 0) {*ptr = 0;return (n - 1);/* EOF, n - 1 bytes were read */} elsereturn (-1);/* error, errno set by read() */}*ptr = 0;/* null terminate like fgets() */return (n);}ssize_t Readline(int fd, void *ptr, size_t maxlen){ssize_t n;if ((n = readline(fd, ptr, maxlen)) < 0)err_sys("readline error");return (n);}void str_cli(FILE * fp, int sockfd){char sendline[MAXLINE], recvline[MAXLINE];while (Fgets(sendline, MAXLINE, fp) != NULL) {Writen(sockfd, sendline, strlen(sendline));if (Readline(sockfd, recvline, MAXLINE) == 0)err_quit("str_cli: server terminated prematurely");Fputs(recvline, stdout);}}int main(int argc, char **argv){int sockfd;struct sockaddr_in servaddr;if (argc != 2)err_quit("usage: tcpcli <IPaddress>");sockfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);Connect(sockfd, (SA *) & servaddr, sizeof(servaddr));str_cli(stdin, sockfd);/* do it all */exit(0);}

最大的问题就是子进程会僵死。


如果要处理僵死进程,父进程就必须捕获SIGCHLD信号。服务器main函数修改如下:

voidsig_chld(int signo){pid_tpid;intstat;pid = wait(&stat);printf("child %d terminated\n", pid);return;}intmain(int argc, char **argv){intlistenfd, connfd;pid_tchildpid;socklen_tclilen;struct sockaddr_incliaddr, servaddr;voidsig_chld(int);listenfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family      = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port        = htons(SERV_PORT);Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));Listen(listenfd, LISTENQ);Signal(SIGCHLD, sig_chld);for ( ; ; ) {clilen = sizeof(cliaddr);if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {if (errno == EINTR)continue;/* back to for() */elseerr_sys("accept error");}if ( (childpid = Fork()) == 0) {/* child process */Close(listenfd);/* close listening socket */str_echo(connfd);/* process the request */exit(0);}Close(connfd);/* parent closes connected socket */}}

各种突发状况处理:

1.accept返回前连接终止:



2.accept返回前连接终止:


3.服务器主机崩溃

客户阻塞于read,不断地重传分节,试图接收一个ACK,最终放弃返回ETIMEOUT

4.服务器崩溃后重启:

服务器重启后丢失了崩溃前的所有连接信息,因此服务器TCP对于来自客户的数据分节响应一个RST.

当客户TCP收到该RST时,阻塞于readline调用。导致该调用返回ECONNRESET.

5.服务器主机关机

当主机关机时,对所有进程KILL,服务器子进程终止,打开的所有描述符关闭。所以和服务器进程终止类似。

原创粉丝点击