Unix網絡編程---第三次做業

Unix網絡編程---第三次做業編程

 

要求:服務器

利用多線程技術實現以下併發網絡程序,要求對上課時的實現進行完善,利用線程專用數據TSD實現。網絡

服務端:數據結構

服務器等待客戶鏈接,鏈接成功後顯示客戶地址,接着接收該客戶的名字並顯示,而後接收來自客戶的信息(字符串),將該字符串反轉,並將結果送回客戶。要求服務器具備同時處理多個客戶的能力。多線程

客戶端:併發

客戶首先與服務器鏈接,接着接收用戶輸入客戶的名字,將該名字發送給服務器,而後接收用戶輸入的字符串,併發送給服務器,而後接收服務器返回的經處理後的字符串,並顯示之。當用戶輸入Ctrl+D,終止鏈接並退出。socket

程序實現:函數

服務器端:my_server3.cthis

#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);

              }

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

              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\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_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!\n");

              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!\n");

                     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:

 

總結:

一、 每一個線程都有本身的數據棧,對於執行線程的函數其數據都是在線程單獨的數據棧中,那麼TSD主要是對全局變量的使用,來減小程序編寫複雜性(我的理解),好比說每一個線程都要用的數據,而每一個線程所執行的功能又不同,可是又要用到相同的數據或數據結構,若是定義全局變量,則均可能會修改,如果在每一個功能函數都定義一下,這樣又太麻煩,因此對於這種狀況就利用線程專用數據TSD。

二、 對於服務端進程被kill後客戶端還沒能反應。當服務端被kill後內核發送FIN給客戶端,客戶端回ACK,但客戶端阻塞在fgets中,只能當它發送數據後,服務端返回RST,因而客戶端讀到字節數小於等於0,才得知服務器關閉而後退出。

三、 對於2的問題解決方法:I/O複用、非阻塞、SO_KEEPALIVE套接字選項。還有沒其它方法呢?

相關文章
相關標籤/搜索