Unix网络编程---第四次作业
2015-11-26 15:32
609 查看
Unix网络编程---第四次作业
要求:
服务器利用I/O复用技术,实现同时向多个客户提供服务。
服务端:
接收客户连接请求,并打印客户IP地址及端口号,然后接收客户发来的字符串,并打印该字符串和其来自与哪个客户。同时向客户返回该字符串。
客户端:
从命令行接收服务器地址,并向服务器发起连接请求,连接成功后,从标准输入接收字符串并发送给服务器,等待服务器响应并打印接收的信息
思路:
针对本次作业,即通过select设置需要等待的描述符。首先将监听套接字描述符在描述符集中打开,当有新的客户请求连接时,select返回,监听套接字可读,于是建立连接,当连接建立以后,产生新的已连接套接字描述符,将其在描述符集中打开,此后每次select返回,检查哪些套接字描述符可用。
针对本题,我没有做题目中的要求功能,而是相对简单类似的,通过用I/O复用解决第三个作业中存在当服务端被kill后,客户端无法得知的问题。
程序实现:
服务器端:my_server4.c
客户端:my_client4.c
运行截图:
编译:gcc -pthread my_client4.c -o my_client4
Gcc -pthread my_server4.c -o my_server4
服务端运行:./my_server3
客户端1运行:./my_client3 192.168.1.128
客户端2运行:./my_client3 192.168.1.128
本实验是在两台虚拟机上操作
服务端ip:192.168.1.128
客户端1 ip:192.168.1.119
客户端2 ip:192.168.1.128
服务端实现:利用线程专用数据TSD,并设置析构函数ser_destructor当线程退出时调用。通过listen套接字,为每个客户创建一个线程,并通过线程专用数据存储客户名与套接字描述符。
客户端实现:利用I/O复用,设置需要等待的描述符集,标准输入描述符与socket描述符,为每个需要监听的动作设置相应的打开位,当select返回时检测是哪一个描述符可读,于是进行相应的操作。
1、服务端:
2、客户端:
总结:
1、 本作业的服务端相对于作业三,稍有改动。由于在作业三中客户端是利用fets来循环获取标准输入的字符串,并在读入后在最后一个位置加上\0的字符串结束符,所以当写给服务端时,服务端不必给字符串加结束符。但是由于在本次作业中客户端直接从stdin中读数据,然后写给服务端,所以在服务端处理时需要加字符串结束符\0,以免多于的字符返回回来,或者说处理的时候出错。
2、 通过n来记录read, write所操作的字节数,来确定字符串边界。
3、 注意strlen,与sizeof区别,前者为字符串长度,后者为内存大小,数组中即为数组的长度。
要求:
服务器利用I/O复用技术,实现同时向多个客户提供服务。
服务端:
接收客户连接请求,并打印客户IP地址及端口号,然后接收客户发来的字符串,并打印该字符串和其来自与哪个客户。同时向客户返回该字符串。
客户端:
从命令行接收服务器地址,并向服务器发起连接请求,连接成功后,从标准输入接收字符串并发送给服务器,等待服务器响应并打印接收的信息
思路:
针对本次作业,即通过select设置需要等待的描述符。首先将监听套接字描述符在描述符集中打开,当有新的客户请求连接时,select返回,监听套接字可读,于是建立连接,当连接建立以后,产生新的已连接套接字描述符,将其在描述符集中打开,此后每次select返回,检查哪些套接字描述符可用。
针对本题,我没有做题目中的要求功能,而是相对简单类似的,通过用I/O复用解决第三个作业中存在当服务端被kill后,客户端无法得知的问题。
程序实现:
服务器端:my_server4.c
#include <sys/socket.h> #include <sys/types.h>/*The funcion sizeof,socklen_t need*/ #include <netinet/in.h>/*The funcion sockaddr_in need*/ #include <unistd.h> #include <arpa/inet.h>/*The funcion inet_ntoa need*/ #include <string.h>/*The funcion strlen need*/ #include <errno.h>/*errno == EINTR*/ #include <sys/wait.h>/*WNOHANG*/ #include <pthread.h> #define UPORT 8088 /*This is the port number used by me */ #define MAXLINE 255 #define LISTENQ 32 #define NAMELEN 21 typedef struct { char buf[MAXLINE+1]; ssize_t n; int sockfd; char name[NAMELEN+1]; } readline; pthread_key_t ser_key; pthread_once_t ser_once=PTHREAD_ONCE_INIT; void str_echo( readline *tsd); void sig_chld(int signo); void ser_destructor(void *ptr); void service_once(void); static void *doit(void *arg); void echo_name(readline *tsd); int main(int argc, char **argv) { int listenfd ,connfd, reuse=1;//if the value of reuse is not zero, mean open this reuse address selection, or else ban this function int *cfdp; struct sockaddr_in servaddr, cliaddr; socklen_t clilen; pthread_t tid,tid1; 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(UPORT); /* daytime server */ if( setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1){ perror("There is an error occured when the program set REUSEADDR symbol\n"); return -1; } if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))==-1){ perror("%s\r\n","bind error"); exit(-1); } listen(listenfd, LISTENQ); signal(SIGCHLD, sig_chld); for ( ; ; ) { clilen=sizeof(cliaddr); cfdp=(int *)malloc(sizeof(int)); if((*cfdp = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen))==-1){ perror("%s\r\n","An error occured while tring to creat a connfd! "); exit(-1); } printf("the new connection address is:%s:%d\r\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port); if(pthread_create(&tid, NULL, &doit, cfdp )!=0){ perror("pthread_create: error\n"); exit(-1); } } } void echo_name(readline *tsd){ char tmp; int i, j; char name[21];//all strcpy(tsd->buf,"Dear client please input your name: "); if(write(tsd->sockfd, tsd->buf,strlen(tsd->buf)+1)==-1) { perror("write error"); exit(-1); } if ((tsd->n=read(tsd->sockfd,tsd->name, NAMELEN)) > 0) { /*tsd->=*tsd.n*/ tsd->name[tsd->n-1]=0;/*change '\n' to 0*/ printf("the client's name: [ %s ]\n", tsd->name); strcpy(tsd->buf,"Now,you can begin to input the string you need to conver!\n"); if(write(tsd->sockfd, tsd->buf,strlen(tsd->buf)+1)==-1) { perror("write error"); exit(-1); } } if (tsd->n<0 && errno == EINTR) { perror("read:error interrupt"); } else if (tsd->n<0) { perror("str_echo:read error"); exit(-1); } } void str_echo( readline *tsd) { char tmp; int i, j; again: while ( (tsd->n=read(tsd->sockfd,tsd->buf, MAXLINE)) > 0) { /*tsd->=*tsd.n*/ tsd->buf[tsd->n]=0; printf("client [ %s ] input string:%s",tsd->name,tsd->buf); for(i=0, j=tsd->n-2; i<j; i++, j--) { tmp=tsd->buf[i]; tsd->buf[i]=tsd->buf[j]; tsd->buf[j]=tmp; } if(write(tsd->sockfd, tsd->buf, tsd->n)==-1) { perror("write error"); exit(-1); } printf("inverted order %s's string:%s",tsd->name,tsd->buf); } if (tsd->n<0 && errno == EINTR) { goto again; } else if (tsd->n<0) { perror("str_echo:read error"); exit(-1); } } void sig_chld(int signo) { pid_t pid; int stat; while( (pid = waitpid(-1,&stat,WNOHANG))>0) printf("child %d terminated\n", pid); return; } void ser_destructor(void *ptr) { free(ptr); printf("one of the tsd end:%d\n",pthread_self()); } void service_once(void) { pthread_key_create(&ser_key, ser_destructor); } static void *doit(void *arg) { readline *tsd; if(pthread_detach(pthread_self())!=0) { perror("pthread_detach:error\n"); exit(-1); } pthread_once(&ser_once,service_once); if( (tsd=pthread_getspecific(ser_key)) == NULL){ tsd=calloc(1,sizeof(readline)); pthread_setspecific(ser_key,tsd); tsd->sockfd=*( (int*)arg); } //printf("%d\n",tsd->sockfd); echo_name(tsd); str_echo( tsd); if(close(*( (int*)arg))==-1){ perror("close:error\n"); exit(-1); } pthread_exit(0); return; }
客户端:my_client4.c
#include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <sys/select.h> #include <sys/time.h> /*there is the time struct in the select variate*/ #define UPORT 8088 /*This is the port number used by me */ #define MAXLINE 255 void str_cli(FILE *fp, int sockfd) { char recvline[MAXLINE],sendline[MAXLINE],buf[MAXLINE+1];/*recv or send use a buffer array*/ fd_set rset; int maxfdp,n; int stdineof=0; FD_ZERO(&rset); for(;;){ if(stdineof==0){ FD_SET(fileno(fp),&rset); } FD_SET(sockfd,&rset); maxfdp=(fileno(fp)>sockfd)?fileno(fp):sockfd+1; if(select(maxfdp, &rset, NULL, NULL, NULL)==-1) { perror("select error\n"); exit(-1); } if(FD_ISSET(sockfd,&rset)){ if( (n=read(sockfd, recvline, MAXLINE)) <= 0 ) { if(stdineof==1){ printf("you have terminated the conection with server!\n"); return;/*normal termination*/ } else{ printf("server terminated prematurely!\n"); exit(0); } } write(fileno(stdout),recvline,n); } if(FD_ISSET(fileno(fp),&rset)){ memset(buf,0,sizeof(buf)); if( (n=read(fileno(fp), sendline, MAXLINE))<= 0 ) { stdineof=1; shutdown(sockfd,SHUT_WR);/*send FIN*/ FD_CLR(fileno(fp),&rset); continue; } sendline[strlen(sendline)]='\0'; if(write(sockfd, sendline, n)==-1){ perror("write sockfd:error!\n"); exit(-1); } //memset(buf,0,sizeof(buf)); } } } int main(int argc, char **argv) { int sockfd, n; struct sockaddr_in servaddr; if (argc != 2){ perror("usage: a.out <IPaddress>"); exit(-1); } if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ perror("socket error"); exit(-1); } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(UPORT); /* daytime server */ if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){ printf("inet_pton error for %s", argv[1]); exit(-1); } if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){ perror("connect error"); exit(-1); } str_cli(stdin,sockfd); /*do it all*/ exit(0); }
运行截图:
编译:gcc -pthread my_client4.c -o my_client4
Gcc -pthread my_server4.c -o my_server4
服务端运行:./my_server3
客户端1运行:./my_client3 192.168.1.128
客户端2运行:./my_client3 192.168.1.128
本实验是在两台虚拟机上操作
服务端ip:192.168.1.128
客户端1 ip:192.168.1.119
客户端2 ip:192.168.1.128
服务端实现:利用线程专用数据TSD,并设置析构函数ser_destructor当线程退出时调用。通过listen套接字,为每个客户创建一个线程,并通过线程专用数据存储客户名与套接字描述符。
客户端实现:利用I/O复用,设置需要等待的描述符集,标准输入描述符与socket描述符,为每个需要监听的动作设置相应的打开位,当select返回时检测是哪一个描述符可读,于是进行相应的操作。
1、服务端:
2、客户端:
总结:
1、 本作业的服务端相对于作业三,稍有改动。由于在作业三中客户端是利用fets来循环获取标准输入的字符串,并在读入后在最后一个位置加上\0的字符串结束符,所以当写给服务端时,服务端不必给字符串加结束符。但是由于在本次作业中客户端直接从stdin中读数据,然后写给服务端,所以在服务端处理时需要加字符串结束符\0,以免多于的字符返回回来,或者说处理的时候出错。
2、 通过n来记录read, write所操作的字节数,来确定字符串边界。
3、 注意strlen,与sizeof区别,前者为字符串长度,后者为内存大小,数组中即为数组的长度。
相关文章推荐
- 分享:基于神经网络的Visual C++阿拉伯数字训练和识别程序
- HttpClient调用webApi时注意的小问题
- Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)
- 负载均衡之Haproxy配置详解(及httpd配置)
- android studio 首次进入问题 Internal error. Please report to https://code.google.com/p/android/issues
- 在IIS上启用Gzip压缩(HTTP压缩)
- HTTP 协议 -- iOS开发网络开发
- HTTP头部详解
- 计算机网络---基础题目汇总二
- android使用appache httpclient混淆后错误排除总结
- setOnScrollListener实现网络请求上下滑动加载数据
- iOS开源库解析之ASIHTTPRequest
- 《神经网络和深度学习》系列文章十六:反向传播算法代码
- 《神经网络和深度学习》系列文章十五:反向传播算法
- iOS开发网络篇—监测网络状态
- 用Java实现基于SOAP的XML文档网络传输及远程过程调用(RPC)
- kvm配置网络
- 【读书笔记】:哈工大软件学院计算机网络期末复习概要
- 未能从程序集“System.ServiceModel”中加载类型“System.ServiceModel.Activation.HttpModule”。
- 未能从程序集“System.ServiceModel”中加载类型“System.ServiceModel.Activation.HttpModule”。