• Unix网络编程---第四次作业


    Unix网络编程---第四次作业

     

    要求:

    服务器利用I/O复用技术,实现同时向多个客户提供服务。

    服务端:

    接收客户连接请求,并打印客户IP地址及端口号,然后接收客户发来的字符串,并打印该字符串和其来自与哪个客户。同时向客户返回该字符串。

    客户端:

    从命令行接收服务器地址,并向服务器发起连接请求,连接成功后,从标准输入接收字符串并发送给服务器,等待服务器响应并打印接收的信息

    思路:

    针对本次作业,即通过select设置需要等待的描述符。首先将监听套接字描述符在描述符集中打开,当有新的客户请求连接时,select返回,监听套接字可读,于是建立连接,当连接建立以后,产生新的已连接套接字描述符,将其在描述符集中打开,此后每次select返回,检查哪些套接字描述符可用。

    针对本题,我没有做题目中的要求功能,而是相对简单类似的,通过用I/O复用解决第三个作业中存在当服务端被kill后,客户端无法得知的问题。

    程序实现:

    服务器端:my_server4.c

      1 #include <sys/socket.h>
      2 #include <sys/types.h>/*The funcion sizeof,socklen_t need*/
      3 #include <netinet/in.h>/*The funcion sockaddr_in need*/
      4 #include <unistd.h>
      5 #include <arpa/inet.h>/*The funcion inet_ntoa need*/
      6 #include <string.h>/*The funcion strlen need*/
      7 #include <errno.h>/*errno == EINTR*/
      8 #include <sys/wait.h>/*WNOHANG*/
      9 #include <pthread.h>
     10 
     11 #define  UPORT 8088 /*This is the port number used by me */
     12 #define  MAXLINE 255
     13 #define  LISTENQ 32
     14 #define  NAMELEN 21
     15 typedef struct {
     16     char buf[MAXLINE+1];
     17     ssize_t n;
     18     int sockfd;
     19     char name[NAMELEN+1];
     20 } readline;
     21 
     22 pthread_key_t ser_key;
     23 pthread_once_t ser_once=PTHREAD_ONCE_INIT;
     24 
     25 void str_echo( readline *tsd);
     26 void sig_chld(int signo);
     27 void ser_destructor(void *ptr);
     28 void service_once(void);
     29 static void *doit(void *arg);
     30 void echo_name(readline *tsd);
     31 
     32 int main(int argc, char **argv)
     33 {
     34     int    listenfd ,connfd, reuse=1;//if the value of reuse is not zero, mean open this reuse address selection, or else ban this function
     35     int *cfdp;
     36     struct sockaddr_in    servaddr, cliaddr;
     37     socklen_t  clilen;
     38     pthread_t tid,tid1;
     39     listenfd = socket(AF_INET, SOCK_STREAM, 0);
     40     bzero(&servaddr, sizeof(servaddr));
     41     servaddr.sin_family      = AF_INET;
     42     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
     43     servaddr.sin_port        = htons(UPORT);    /* daytime server */
     44     if( setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1){
     45         perror("There is an error occured when the program set REUSEADDR symbol
    ");
     46         return -1;
     47     }
     48     if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))==-1){
     49         perror("%s
    ","bind error");
     50         exit(-1);
     51     }
     52     listen(listenfd, LISTENQ);
     53     signal(SIGCHLD, sig_chld);
     54     for ( ; ; ) {
     55         clilen=sizeof(cliaddr);
     56         cfdp=(int *)malloc(sizeof(int));
     57         if((*cfdp = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen))==-1){ 
     58             perror("%s
    ","An error occured while tring to creat a connfd! ");
     59             exit(-1);
     60         }
     61         printf("the new connection address is:%s:%d
    ",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
     62         if(pthread_create(&tid, NULL, &doit, cfdp )!=0){
     63             perror("pthread_create: error
    ");
     64             exit(-1);
     65         }
     66     }
     67 }
     68 void echo_name(readline *tsd){
     69     char  tmp;
     70     int i, j;
     71     char name[21];//all
     72     strcpy(tsd->buf,"Dear client please input your name: ");
     73     if(write(tsd->sockfd, tsd->buf,strlen(tsd->buf)+1)==-1) {
     74         perror("write error");
     75         exit(-1);
     76     }
     77 
     78     if ((tsd->n=read(tsd->sockfd,tsd->name, NAMELEN)) > 0) { /*tsd->=*tsd.n*/
     79         tsd->name[tsd->n-1]=0;/*change '
    ' to 0*/
     80         printf("the client's name: [ %s ]
    ", tsd->name);
     81         strcpy(tsd->buf,"Now,you can begin to input the string you need to conver!
    ");
     82         if(write(tsd->sockfd, tsd->buf,strlen(tsd->buf)+1)==-1) {
     83             perror("write error");
     84             exit(-1);
     85         }
     86     }
     87     if (tsd->n<0 && errno == EINTR) {
     88         perror("read:error interrupt");
     89     }
     90     else if (tsd->n<0) {
     91         perror("str_echo:read error");
     92         exit(-1);
     93     }
     94 }
     95 void str_echo( readline *tsd) {
     96     char  tmp;
     97     int i, j;
     98 again:
     99     while ( (tsd->n=read(tsd->sockfd,tsd->buf, MAXLINE)) > 0) { /*tsd->=*tsd.n*/
    100         tsd->buf[tsd->n]=0;
    101         printf("client [ %s ] input string:%s",tsd->name,tsd->buf);
    102         for(i=0, j=tsd->n-2; i<j; i++, j--) {
    103             tmp=tsd->buf[i];
    104             tsd->buf[i]=tsd->buf[j];
    105             tsd->buf[j]=tmp;
    106         }
    107         if(write(tsd->sockfd, tsd->buf, tsd->n)==-1) {
    108             perror("write error");
    109             exit(-1);
    110         }
    111         printf("inverted order %s's string:%s",tsd->name,tsd->buf);
    112     }
    113     if (tsd->n<0 && errno == EINTR) {
    114         goto again;
    115     }
    116     else if (tsd->n<0) {
    117         perror("str_echo:read error");
    118         exit(-1);
    119     }
    120 }
    121 void sig_chld(int signo)
    122 {
    123     pid_t pid;
    124     int stat;
    125     while( (pid = waitpid(-1,&stat,WNOHANG))>0)
    126         printf("child %d terminated
    ", pid);
    127     return;
    128 }
    129 void ser_destructor(void *ptr) {
    130     free(ptr);
    131     printf("one of the tsd end:%d
    ",pthread_self());
    132 }
    133 void service_once(void) {
    134     pthread_key_create(&ser_key, ser_destructor);
    135 }
    136 
    137 static void *doit(void *arg) {
    138     readline *tsd;
    139     if(pthread_detach(pthread_self())!=0) {
    140         perror("pthread_detach:error
    ");
    141         exit(-1);
    142     }
    143     pthread_once(&ser_once,service_once);
    144     if( (tsd=pthread_getspecific(ser_key)) == NULL){
    145         tsd=calloc(1,sizeof(readline));
    146         pthread_setspecific(ser_key,tsd);
    147         tsd->sockfd=*( (int*)arg);
    148     }
    149     //printf("%d
    ",tsd->sockfd);
    150     echo_name(tsd);
    151     str_echo( tsd);
    152     if(close(*( (int*)arg))==-1){
    153         perror("close:error
    ");
    154         exit(-1);
    155     }
    156     pthread_exit(0);
    157     return;
    158 }

    客户端:my_client4.c

     1 #include <sys/socket.h>
     2 #include <sys/types.h>
     3 #include <netinet/in.h>
     4 #include <unistd.h>
     5 #include <stdio.h>
     6 #include <string.h>
     7 #include <errno.h>
     8 #include <sys/select.h>
     9 #include <sys/time.h> /*there is the time struct in the select variate*/
    10  
    11 #define  UPORT 8088 /*This is the port number used by me */
    12 #define  MAXLINE 255
    13 
    14 void str_cli(FILE *fp, int sockfd) {
    15     char recvline[MAXLINE],sendline[MAXLINE],buf[MAXLINE+1];/*recv or send use a buffer array*/
    16     fd_set rset;
    17     int maxfdp,n;
    18     int    stdineof=0;
    19     FD_ZERO(&rset);
    20     for(;;){
    21         if(stdineof==0){
    22             FD_SET(fileno(fp),&rset);    
    23         }
    24         FD_SET(sockfd,&rset);
    25         maxfdp=(fileno(fp)>sockfd)?fileno(fp):sockfd+1;
    26         if(select(maxfdp, &rset, NULL, NULL, NULL)==-1) {
    27             perror("select error
    ");
    28             exit(-1);
    29         }
    30         if(FD_ISSET(sockfd,&rset)){
    31             if( (n=read(sockfd, recvline, MAXLINE)) <= 0 ) { 
    32                 if(stdineof==1){
    33                     printf("you have terminated the conection with server!
    ");
    34                     return;/*normal termination*/
    35                 }
    36                 else{
    37                     printf("server terminated prematurely!
    ");
    38                     exit(0);
    39                 }    
    40             }
    41             write(fileno(stdout),recvline,n);
    42         }
    43         if(FD_ISSET(fileno(fp),&rset)){
    44             memset(buf,0,sizeof(buf));
    45             if( (n=read(fileno(fp), sendline, MAXLINE))<= 0 ) { 
    46                 stdineof=1;
    47                 shutdown(sockfd,SHUT_WR);/*send FIN*/
    48                 FD_CLR(fileno(fp),&rset);
    49                 continue;    
    50             }
    51             sendline[strlen(sendline)]='';
    52             if(write(sockfd, sendline, n)==-1){
    53                 perror("write sockfd:error!
    ");
    54                 exit(-1);
    55             }
    56             //memset(buf,0,sizeof(buf));
    57         }
    58     }
    59 }
    60 
    61 
    62 int main(int argc, char **argv)
    63 {
    64     int                    sockfd, n;
    65     struct sockaddr_in    servaddr;
    66 
    67     if (argc != 2){
    68         perror("usage: a.out <IPaddress>");
    69         exit(-1);
    70     }
    71 
    72     if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    73         perror("socket error");
    74         exit(-1);
    75     }
    76         
    77     bzero(&servaddr, sizeof(servaddr));
    78     servaddr.sin_family = AF_INET;
    79     servaddr.sin_port   = htons(UPORT);    /* daytime server */
    80     if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
    81         printf("inet_pton error for %s", argv[1]);
    82         exit(-1);
    83     }
    84 
    85     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){
    86         perror("connect error");
    87         exit(-1);
    88     }
    89     str_cli(stdin,sockfd); /*do it all*/
    90 
    91     exit(0);
    92 }

    运行截图:

    编译: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来循环获取标准输入的字符串,并在读入后在最后一个位置加上的字符串结束符,所以当写给服务端时,服务端不必给字符串加结束符。但是由于在本次作业中客户端直接从stdin中读数据,然后写给服务端,所以在服务端处理时需要加字符串结束符,以免多于的字符返回回来,或者说处理的时候出错。

    2、 通过n来记录read, write所操作的字节数,来确定字符串边界。

    3、 注意strlen,与sizeof区别,前者为字符串长度,后者为内存大小,数组中即为数组的长度。

  • 相关阅读:
    分布式系统(Distributed System)资料
    (转)hive基本操作
    c# 如何通过反射 获取设置属性值、
    安卓webview下使用zepto的swipe失效
    js获取本月、三个月、今年的日期插件dateHelp
    jquery获取form表单内容以及绑定数据到form表单
    JQuery阻止事件冒泡
    javascript-Cookie的应用
    chrome浏览器调试
    JavaScript设计模式之命令模式
  • 原文地址:https://www.cnblogs.com/lwhp/p/4997913.html
Copyright © 2020-2023  润新知