Pro_5_UNIX下TCP回射服务与客户程序_2016_08_15
2016-08-15 23:21
513 查看
这里回射的意思类似于linux下的回显命令,当我们在控制台下echo hello的时候,屏幕就会显示hello。 回射也是这个意思,当我们运行程序后在控制台下输入一串字符串回车,则显示该字符串。 服务器收到这串字符,再返回给客户端。这个过程很像打乒乓球。不像之前的最简单的CS程序,这个实现了双工的工作模式,两边都有读写。 下面列出TCP回射的服务器程序:
tcpserecho.c
/* * 抄自《UNIX网络编程:卷1》, 稍作修改。 * 仅仅用于学习目的。学无止境,进步每一天。 * * slickedit编辑。 * * 254008829@qq.com * */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> //#include <linux/in.h> #include <netinet/in.h> /* struct sockaddr_in, htons ... */ #define LISTENQ 1024 #define SERV_PORT 9877 #define MAXLINE 1024 #define FUNC_RET(func, val) \ do { \ int rv; \ if ( (rv=func) < 0) { \ printf("LINE = %d, error str: %s\n", __LINE__, strerror(errno)); \ perror("FUNC_RET"); \ exit(-1); \ } \ val = rv; \ } while (0); #define FUNC_RET_PTR(func, val) \ do { \ void* ll_prv; \ if ( (ll_prv=func) == NULL) { \ printf("LINE = %d, error str: %s\n", __LINE__, strerror(errno)); \ perror("FUNC_RET"); \ exit(-1); \ } \ val = ll_prv; \ } while (0); #if 0 void str_echo(int sockfd) { char line[MAXLINE]; FILE *fpin, *fpout; char *rptr; FUNC_RET_PTR(fdopen(sockfd, "r"), fpin); FUNC_RET_PTR(fdopen(sockfd, "w"), fpout); for (;;) { int put_ret; FUNC_RET_PTR(fgets(line, MAXLINE, fpin), rptr); if (ferror(fpin) && rptr == NULL) { exit(0); } FUNC_RET(fputs(line, fpout), put_ret); if (put_ret == EOF) { exit(0); } } } #endif ssize_t writen(int fd, char *vptr, size_t n) { size_t nleft; ssize_t nwrite; char *ptr; nleft = n; ptr = vptr; while (nleft > 0) { /* 内核里面由于缓冲的原因,一次write可能没有写完我们所要求的n个字节。没有关系,返回nwrite>0, 表明实际写了nwrite个字节。下次接着写。 在不出错的情况下,writen返回的一定是n。这个和readn有区别。 */ if ( (nwrite = write(fd, ptr, nleft)) <= 0) { if ( (nwrite < 0 && nwrite == EINTR)) { nwrite = 0; // call write() again. } else return -1; // error } nleft -= nwrite; ptr += nwrite; } return n; } 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) { exit(0); } } int main(int argc, char **argv) { int listenfd, connfd, ret; pid_t childpid; struct sockaddr_in cliaddr, seraddr; socklen_t clilen; FUNC_RET(socket(AF_INET, SOCK_STREAM, 0), listenfd); bzero(&seraddr, sizeof(seraddr)); seraddr.sin_port = htons(SERV_PORT); seraddr.sin_family = AF_INET; seraddr.sin_addr.s_addr = htonl(INADDR_ANY); FUNC_RET(bind(listenfd, (struct sockaddr *)&seraddr, sizeof(seraddr)), ret); FUNC_RET(listen(listenfd, LISTENQ), ret); while (1) { clilen = sizeof(cliaddr); /* 作为accept的第三个参数,在第二个参数不为NULL的时候, 必须赋值为第二个参数的长度 */ // accept args3 # input_return type! FUNC_RET(accept(listenfd, (struct sockaddr *)&cliaddr, &clilen), connfd); if ( (childpid = fork()) == 0) { close(listenfd); str_echo(connfd); exit(0); } close(connfd); } exit(0); }
客户端程序:
tcpcliecho.c
/* * 抄自《UNIX网络编程:卷1》, 稍作修改。 * 仅仅用于学习目的。学无止境,进步每一天。 * * slickedit编辑。 * * 254008829@qq.com * */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> //#include <linux/in.h> #include <netinet/in.h> /* struct sockaddr_in, htons ... */ #include <arpa/inet.h> #define LISTENQ 1024 #define SERV_PORT 9877 #define MAXLINE 1024 #define FUNC_RET_NONNGTV(func, val) \ do { \ int ll_rv; \ if ( (ll_rv=func) < 0) { \ printf("LINE = %d, error str: %s\n", __LINE__, strerror(errno)); \ perror("FUNC_RET"); \ exit(-1); \ } \ val = ll_rv; \ } while (0); #define FUNC_RET_PSTV(func, val) \ do { \ int ll_rv; \ if ( (ll_rv=func) <= 0) { \ printf("LINE = %d, error str: %s\n", __LINE__, strerror(errno)); \ perror("FUNC_RET"); \ exit(-1); \ } \ val = ll_rv; \ } while (0); #define FUNC_RET_ZERO(func, val) \ do { \ int ll_rv; \ if ( (ll_rv=func) != 0) { \ printf("LINE = %d, error str: %s\n", __LINE__, strerror(errno)); \ perror("FUNC_RET"); \ exit(-1); \ } \ val = ll_rv; \ } while (0); #define FUNC_RET_PTR(func, val) \ do { \ void* ll_prv; \ if ( (ll_prv=func) == NULL) { \ printf("LINE = %d, error str: %s\n", __LINE__, strerror(errno)); \ perror("FUNC_RET"); \ exit(-1); \ } \ val = ll_prv; \ } while (0); ssize_t writen(int fd, char *vptr, size_t n) { size_t nleft; ssize_t nwrite; char *ptr; nleft = n; ptr = vptr; while (nleft > 0) { /* 内核里面由于缓冲的原因,一次write可能没有写完我们所要求的n个字节。没有关系,返回nwrite>0, 表明实际写了nwrite个字节。下次接着写。 在不出错的情况下,writen返回的一定是n。这个和readn有区别。 */ if ( (nwrite = write(fd, ptr, nleft)) <= 0) { if ( (nwrite < 0 && nwrite == EINTR)) { nwrite = 0; // call write() again. } else return -1; // error } nleft -= nwrite; ptr += nwrite; } return n; } /* 十分低效的readline函数,不带缓冲,每次只read一个字节, 每read一次都要进行一次系统调用。 */ 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++) { again: if ( (rc = read(fd, &c, 1)) == 1) { *ptr++ = c; if (c == '\n') { break; } } else if (rc == 0) { // eof return n-1; } else { if (errno == EINTR) { goto again; } return -1; } } *ptr = 0; return n; } void str_cli(FILE *fp, int sockfd) { char sendline[MAXLINE]={0}, recvline[MAXLINE]={0}, *pret; int ret; for (;;) { FUNC_RET_PTR(fgets(recvline, MAXLINE, fp), pret); if (!pret) { break; } if (writen(sockfd, recvline, strlen(recvline)) != strlen(recvline)) { exit(0); } if (readline(sockfd, sendline, MAXLINE) == 0) { exit(0); } FUNC_RET_PSTV(fputs(sendline, stdout), ret); } } int main(int argc, char **argv)\ { struct sockaddr_in servaddr; int sockfd, ret; if (argc != 2) { printf("usage: %s ip\n", argv[0]); exit(0); } FUNC_RET_NONNGTV(socket(AF_INET, SOCK_STREAM, 0), sockfd); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); FUNC_RET_PSTV(inet_pton(AF_INET, argv[1], &servaddr.sin_addr), ret); FUNC_RET_ZERO(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)), ret); str_cli(stdin, sockfd); exit(0); }
分别将两个程序编译后生成:tcpserecho和tcpcliecho,打开控制台运行如下命令: ./tcpserecho & ./tcpcliecho 127.0.0.1 此时界面会陷入阻塞状态,在控制台下随便输入一串字符,再回车,有看到有回显的效果,如下:
这个程序有一个缺陷,当服务器子进程终止的时候会给父进程发送SIGCHLD信号,我们没有处理这个信号。此时子进程会陷入僵死的状态。如下:
相关文章推荐
- Android---数据读取、存储、删除(内存储/SD卡存储/网络数据读取存储)
- Oracle 的网络
- Android手机数据读写方法(内部存储、SD卡,网络加载,包内文件读取)
- spring管理httpclient
- 【TCP/IP详解 卷一:协议】第二十章 TCP的成块数据流
- 【计算机网络-2】 【第一章】SDU与PDU 笔记
- iOS开发:网络监测
- NFS网络文件系统
- Http请求辅助工具HttpClient
- 【计算机网络-1】【第一章】计算机网络体系结构
- 多线程、网络总结
- 网络开发技术基础听课笔记
- 2016中国大学生程序设计竞赛 - 网络选拔赛 1008 Special Tetrahedron hdu5839
- 2016中国大学生程序设计竞赛 - 网络选拔赛 1011
- http协议是什么?
- 2016中国大学生程序设计竞赛 - 网络选拔赛 1001
- http://python3-cookbook.readthedocs.io/zh_CN/latest/c14/p01_testing_output_sent_to_stdout.html
- https安全在哪里,原理是什么?
- HDU4888 Redraw Beautiful Drawings(最大流唯一性判定:残量网络删边判环)
- HDU4888 Redraw Beautiful Drawings(最大流唯一性判定:残量网络删边判环)