TCP客户、服务端程序示例
2016-07-13 15:00
239 查看
参考学习:《unix网络编程 卷1》
书中的例子中,作者自己封装了很多方法和头文件(本书有源码,需要按要求编译运行)。我做了修改,自己照着敲,可能方法上不太严谨,不会应当是可以运行的,自己敲出来也是一种学习。
我们要实现如下的TCP客户/服务器
服务端 server.c
客户端 client.c
运行图:
我在代码中添加了显示连接的客户端的信息。
关于Write readline函数也是书中的
知识点:
当不使用封装的函数Write,readline函数,而直接使用 read,write时,程序运行的结果如上图所示。有乱码
字节流套接字字上调用read和write输入或输出的字节数可能比请求的数量少,多的现象。这是因为在内核中用于套接字的缓冲区可能已经到达了极限,会出现不足一个字节的计数值,这样就需要自己重新编写,实现一个一个字节的读取和写入。(作者说明了原因,也封装了上面的方法)。
子进程结束后,父进程并没有对子进程进行任何处理(调用wait来收集僵尸进程),显然会存在僵尸进程。
各种问题及原因,要自己阅读图书并实践。。。
信号处理,解决僵尸进程,wait / waitpid
server.c
利用waitpid
wait waitpid 的区别和作用可以参考图书讲解 或 博文:
http://blog.chinaunix.net/uid-25365622-id-3045460.html
tcp 三次握手和四次挥手,参考学习如下博文
http://blog.csdn.net/whuslei/article/details/6667471
书中的例子中,作者自己封装了很多方法和头文件(本书有源码,需要按要求编译运行)。我做了修改,自己照着敲,可能方法上不太严谨,不会应当是可以运行的,自己敲出来也是一种学习。
我们要实现如下的TCP客户/服务器
服务端 server.c
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <sys/shm.h> #include <arpa/inet.h> #define MYPORT 9877 #define QUEUE 20 #define BUFFER_SIZE 1024 /* write n bytes to a descrpiter */ ssize_t Write(int fd,const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if( (nwritten = write(fd, ptr, nleft)) <= 0) { if( nwritten < 0) nwritten = 0; else return -1; // error } nleft = -nwritten; ptr += nwritten; } return n; } void str_echo(int sockfd) { ssize_t n; char buf[BUFFER_SIZE]; while( (n = read(sockfd, buf, BUFFER_SIZE)) > 0 ) Write(sockfd, buf, n); if(n < 0) perror("read error"); } int main() { // 定义sockfd int server_sockfd = socket(AF_INET,SOCK_STREAM, 0); // 定义sockaddr_in struct sockaddr_in server_sockaddr; server_sockaddr.sin_family = AF_INET;//协议族 server_sockaddr.sin_port = htons(MYPORT); //端口 server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); //地址 // bind,成功返回0,出错返回-1 if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1) { perror("bind"); exit(1); } // listen,成功返回0,出错返回-1 if(listen(server_sockfd,QUEUE) == -1) { perror("listen"); exit(1); } while(1){ ///客户端套接字 struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); ///成功返回非负描述字,出错返回-1 int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length); if(conn<0) { perror("connect"); exit(1); } char buff[BUFFER_SIZE]; // 显示连接的客户端 printf("connection from %s, port %d\n", inet_ntop(AF_INET, &client_addr.sin_addr, buff, sizeof(buff)), ntohs(client_addr.sin_port)); pid_t child_pid ; if( (child_pid = fork()) == 0 ) // 为客户端fork子进程 { close(server_sockfd); str_echo(conn); exit(0); } close(conn); } return 0; }
客户端 client.c
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <sys/shm.h> #define MYPORT 9877 #define BUFFER_SIZE 1024 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) { *ptr = 0; return (n-1); }else{ return -1; } } *ptr = 0; return n; } void str_cli(FILE *fp, int sockfd) { char sendline[BUFFER_SIZE], recvline[BUFFER_SIZE]; while( fgets(sendline, BUFFER_SIZE , fp) != NULL) { write(sockfd,sendline,strlen(sendline)); //if(read(sockfd, recvline, BUFFER_SIZE ) < 0) if(readline(sockfd, recvline, BUFFER_SIZE ) < 0) perror("over"); fputs(recvline,stdout); } } int main() { ///定义sockfd int sock_cli = socket(AF_INET,SOCK_STREAM, 0); ///定义sockaddr_in struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(MYPORT); ///服务器端口 servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); ///服务器ip ///连接服务器,成功返回0,错误返回-1 if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { perror("connect"); exit(1); } str_cli(stdin,sock_cli); //回射客户端的程序 exit(0); }
运行图:
我在代码中添加了显示连接的客户端的信息。
关于Write readline函数也是书中的
知识点:
1 Write readline函数封装?
当不使用封装的函数Write,readline函数,而直接使用 read,write时,程序运行的结果如上图所示。有乱码
字节流套接字字上调用read和write输入或输出的字节数可能比请求的数量少,多的现象。这是因为在内核中用于套接字的缓冲区可能已经到达了极限,会出现不足一个字节的计数值,这样就需要自己重新编写,实现一个一个字节的读取和写入。(作者说明了原因,也封装了上面的方法)。
2 tcp 连接终止(四次握手) 出现了 TIME_WAIT状态
3 出现了僵尸进程
子进程结束后,父进程并没有对子进程进行任何处理(调用wait来收集僵尸进程),显然会存在僵尸进程。
各种问题及原因,要自己阅读图书并实践。。。
信号处理,解决僵尸进程,wait / waitpid
server.c
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <sys/shm.h> #include <arpa/inet.h> #include <signal.h> #define MYPORT 9877 #define QUEUE 20 #define BUFFER_SIZE 1024 void sig_child(int signo) { pid_t pid; int stat; pid = wait(&stat); printf("child %d terminated\n", pid); return; } /* write n bytes to a descrpiter */ ssize_t Write(int fd,const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if( (nwritten = write(fd, ptr, nleft)) <= 0) { if( nwritten < 0) nwritten = 0; else return -1; // error } nleft = -nwritten; ptr += nwritten; } return n; } void str_echo(int sockfd) { ssize_t n; char buf[BUFFER_SIZE]; while( (n = read(sockfd, buf, BUFFER_SIZE)) > 0 ) Write(sockfd, buf, n); if(n < 0) perror("read error"); } int main() { // 定义sockfd int server_sockfd = socket(AF_INET,SOCK_STREAM, 0); // 定义sockaddr_in struct sockaddr_in server_sockaddr; server_sockaddr.sin_family = AF_INET;//协议族 server_sockaddr.sin_port = htons(MYPORT); //端口 server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); //地址 // bind,成功返回0,出错返回-1 if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1) { perror("bind"); exit(1); } // listen,成功返回0,出错返回-1 if(listen(server_sockfd,QUEUE) == -1) { perror("listen"); exit(1); } signal(SIGCHLD, sig_child);// 信号处理 while(1){ ///客户端套接字 struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); ///成功返回非负描述字,出错返回-1 int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length); if(conn<0) { perror("connect"); exit(1); } char buff[BUFFER_SIZE]; // 显示连接的客户端 printf("connection from %s, port %d\n", inet_ntop(AF_INET, &client_addr.sin_addr, buff, sizeof(buff)), ntohs(client_addr.sin_port)); pid_t child_pid ; if( (child_pid = fork()) == 0 ) // 为客户端fork子进程 { close(server_sockfd); str_echo(conn); exit(0); } close(conn); } return 0; }
利用waitpid
void sig_child(int signo) { pid_t pid; int stat; while( (pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminated\n", pid); return; }
wait waitpid 的区别和作用可以参考图书讲解 或 博文:
http://blog.chinaunix.net/uid-25365622-id-3045460.html
tcp 三次握手和四次挥手,参考学习如下博文
http://blog.csdn.net/whuslei/article/details/6667471
相关文章推荐
- TCP网络库:Acceptor、TcpServer、TcpConnection
- 动态注册监听网络变化
- (MyStudy:一)三级缓存:首先是内存-文件(外存)-网络三级缓存机制。
- 使用Android的OkHttp包实现基于HTTP协议的文件上传下载
- 【转】2012年6月26 – PPS网络电视PHP工程师最新面试题
- angularJS 发起$http.post和$http.get请求
- Http进行网络通信
- HTTP请求报文格式
- 用PHP操作http中Etag、lastModified和Expires标签
- 格式工厂下载地址http://www.33lc.com/soft/1958.html
- Ubuntu下移植Tcpdump到Android平台
- Learning Deconvolution Network for Semantic Segmentation
- 《Deep Learning》译文 第六章 深度前馈网络 从异或函数说起
- 使用HttpSessionListener接口监听Session的创建和失效
- HTTPS
- 如何创建物化视图-摘抄自网络
- 深度神经网络实现图像理解的原理
- HTTP Status 404 - /chp-adapter-web/ 问题解决
- java网络socket编程(二)之Socket客户端
- http://www.cnblogs.com/liqing1009/p/4143789.html