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


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

     

    要求:

    利用多线程技术实现如下并发网络程序,要求对上课时的实现进行完善,利用线程专用数据TSD实现。

    服务端:

    服务器等待客户连接,连接成功后显示客户地址,接着接收该客户的名字并显示,然后接收来自客户的信息(字符串),将该字符串反转,并将结果送回客户。要求服务器具有同时处理多个客户的能力。

    客户端:

    客户首先与服务器连接,接着接收用户输入客户的名字,将该名字发送给服务器,然后接收用户输入的字符串,并发送给服务器,然后接收服务器返回的经处理后的字符串,并显示之。当用户输入Ctrl+D,终止连接并退出。

    程序实现:

    服务器端:my_server3.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 ");

                  return -1;

           }

           if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))==-1){

                  perror("%s ","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 ","An error occured while tring to creat a connfd! ");

                         exit(-1);

                  }

                  printf("the new connection address is:%s:%d ",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);

                  if(pthread_create(&tid, NULL, &doit, cfdp )!=0){

                         perror("pthread_create: error ");

                         exit(-1);

                  }

                  //pthread_create(&tid, NULL, &doit,NULL );

                  /*if( (childpid=fork())==0) {

                         close(listenfd);

                         str_echo(connfd);

                         exit(0);

                  }*/

                  //pthread_join(tid,NULL);

                  //close(*cfdp); /*parent closes connected socket*/

           }

    }

    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-2]=0;

                  printf("the client's name: [ %s ] ", tsd->name);

                  strcpy(tsd->buf,"Now,you can begin to input the string you need to conver! ");

                  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*/

                  printf("client [ %s ] input string:%s",tsd->name,tsd->buf);

                  for(i=0, j=tsd->n-3; 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 ", pid);

           return;

    }

    void ser_destructor(void *ptr) {

           free(ptr);

           printf("one of the tsd end:%d ",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 ");

                  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 ",tsd->sockfd);

        echo_name(tsd);

           str_echo( tsd);

           if(close(*( (int*)arg))==-1){

                  perror("close:error ");

                  exit(-1);

           }

           pthread_exit(0);

           return;

    }

    客户端:my_client3.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>

    #define  UPORT 8088 /*This is the port number used by me */

    #define  MAXLINE 255

    void str_cli(FILE *fp, int sockfd) {

           char sendline[MAXLINE+1], recvline[MAXLINE+1];

           if(read(sockfd, recvline, MAXLINE) <= 0 ) {

                  printf("server terminated prematurely! ");

                  exit(0);

           }

           else{

                  fputs(recvline,stdout);

           }

           while (fgets(sendline, MAXLINE+1, fp) != NULL) {

                  if(write(sockfd, sendline, (strlen(sendline)+1)) == -1) {

                         perror("write error");

                         exit(-1);

                  }

                  if(read(sockfd, recvline, MAXLINE) <= 0 ) {

                         printf("server terminated prematurely! ");

                         exit(0);

                  }

                  //recvline(MAXLINE)=0;/*auto set 0 by initializing*/

                  fputs(recvline,stdout);

           }

    }

    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_client3.c -o my_client3

    Gcc -pthread my_server3.c -o my_server3

    服务端运行:./my_server3

    客户端1运行:./my_client3 192.168.1.119

    客户端2运行:./my_client3 192.168.1.119

    本实验是在两台虚拟机上操作

    服务端ip:192.168.1.119

    客户端1 ip:192.168.1.119

    客户端2 ip:192.168.1.120。

    服务端实现:利用线程专用数据TSD,并设置析构函数ser_destructor当线程退出时调用。通过listen套接字,为每个客户创建一个线程,并通过线程专用数据存储客户名与套接字描述符。

    1、服务端:

     

    2、客户端1:

     

    3、客户端2:

     

    总结:

    1、 每个线程都有自己的数据栈,对于执行线程的函数其数据都是在线程单独的数据栈中,那么TSD主要是对全局变量的使用,来减少程序编写复杂性(个人理解),比如说每个线程都要用的数据,而每个线程所执行的功能又不一样,但是又要用到相同的数据或数据结构,如果定义全局变量,则都可能会修改,若是在每个功能函数都定义一下,这样又太麻烦,所以对于这种情况就利用线程专用数据TSD。

    2、 对于服务端进程被kill后客户端还没能反应。当服务端被kill后内核发送FIN给客户端,客户端回ACK,但客户端阻塞在fgets中,只能当它发送数据后,服务端返回RST,于是客户端读到字节数小于等于0,才得知服务器关闭然后退出。

    3、 对于2的问题解决方法:I/O复用、非阻塞、SO_KEEPALIVE套接字选项。还有没其它方法呢?

  • 相关阅读:
    关于时间的字词
    Postgresql 存储过程调试 1
    Delphi 调试日子
    Delphi 调试日子
    TList,TObjectList 使用——资源释放
    Lazarus开发环境编译选项配置
    Delphi 递归搜索.SVN文件夹并“处理”
    Delphi 路径相关函数
    如何掌握程序语言(王垠)
    struct/class等内存字节对齐问题详解
  • 原文地址:https://www.cnblogs.com/lwhp/p/4996182.html
Copyright © 2020-2023  润新知