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

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

 

要求:

客户端:

1、从命令行读入服务器的IP地址;并连接到服务器;

2、循环从命令行读入一行字符串,并传递给服务器,由服务器对字符串反转并将结果返回客户程序;

3、客户程序显示反转后的字符串;

服务器端:

1、接收客户的连接请求,并显示客户的IP地址和端口号;

2、接收客户传来的字符串,反转后传递给客户;

 

程序实现:

服务器端:my_server2.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*/

 

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

#define  MAXLINE 255

#define  LISTENQ 32

 

void str_echo(int sockfd) {

       ssize_t n;

       char buf[MAXLINE+1], tmp;

       int i, j;

again:

       while ( (n=read(sockfd, buf, MAXLINE)) > 0) { /*blocking*/

              printf("client input string:%s",buf);

              for(i=0, j=n-3; i<j; i++, j--) {

                     tmp=buf[i];

                     buf[i]=buf[j];

                     buf[j]=tmp;

                     /*printf("%d\n",i); */

              }

              /*printf("strlen:%d\n",strlen(buf)); */

              /*printf("i:j,%d:%d\n",i,j); */

              if(write(sockfd, buf, n)==-1) {

                     perror("write error");

                     exit(-1);

              }

              printf("inverted order string:%s",buf);

              //printf("line break:%c",'\n');/*the printf is line buffer*/

              //printf("line break:%s","\n");

              //printf("n:%d\n", n);

       }

       if (n<0 && errno == EINTR) {

              goto again;

       }

       else if (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;

}

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

       struct sockaddr_in  servaddr, cliaddr;

       socklen_t  clilen;

       pid_t childpid;

       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\r\n","bind error");

              exit(-1);

       }

       listen(listenfd, LISTENQ);

       signal(SIGCHLD, sig_chld);

       for ( ; ; ) {

              clilen=sizeof(cliaddr);/*this line should in the for,because everytime ciculation the value of the clilen will be changed by the following lines program,so we should reset it */

              if((connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen))==-1){ /*blocking*/

                     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( (childpid=fork())==0) { /* child process*/

                     close(listenfd);/*close listening socket*/

                     str_echo(connfd);/*process the request*/

                     exit(0);

              }

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

       }

}

客户端:my_client2.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];

       while (fgets(sendline, MAXLINE+1, fp) != NULL) { /*read at most MAXLINE character, auto add 0 in the end,blocking until end a time of stdin by clicking the Enter. when the length of input string less than the MAXLINE the fgets can read-in the line break character '\n'*/

              if(write(sockfd, sendline, (strlen(sendline)+1)) == -1) { /*the write  do not stop in the line break and '\0' ,it will stop writing when the number of writing character equal it, note that the strlen did not inlcude the '\0' character,so we make the strlen add one,eg:str="123\n";strlen(str) is 7,actually it contains 8 character, the last char is '\0'. */

                     perror("write error");

                     exit(-1);

              }

              if(read(sockfd, recvline, MAXLINE) <= 0 ) { /*read will end in encounter the EOF or number of character less than MAXLINE or equal, rather than the line break or '\0'*/

                     //perror("server terminated prematurely!");/*when use this function, if no error will output :Success*/

                     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 my_client.c -o my_client

gcc my_server.c -o my_server

服务端运行:./my_server

客户端运行:./my_client 192.168.1.119

本实验是在两台虚拟机上操作,服务端ip为192.168.1.119,客户端ip为192.168.1.120

正常结束

1、 客户端:正常结束EOF(ctrl+D),fges=null

 

2、 服务端:收到EOF,子进程终止,父进程收到终止信号并处理,打印出子进程pid,释放子进程所占用内存,避免僵尸进程。

 

服务器进程先终止

1、通过设置signal,捕获SIGHLD子进程终止的信息,作相应的处理输出。

2、当子进程被kill子后会向客户端发送FIN,客户端收到之后回复ACK,关闭已经连接的套接字。

3、关闭连接之后,当再次向服务端发送字符时,服务端返回reset,于是客户端进程退出。

4、如果客户端接收到reset的之后再发送字符,那么这时候内核就会向该进程发送一个SIGPIPE信号,终止进程。

本程序涉及到前3点,不会出现第4点情况,因为当读到的字符为null时,客户进程将打印出信息,并推出。注意当没有输入任何字符按回车之后,fgets将读到换行符\n,只有客户端收到EOF时,表示文件结束符,此时fgets不会读到任何字符,即为null。

1、 服务端:查找子进程pid并kill

 

 

2、客户端:终止之后再发送字符,返回服务端提前终止信息

 

总结:

1、  fgets若从标准输入stdin读字符串,当所给的字符串长度小于MAXLINE-1时,会读入换行符并在最后加上\0,否则,读入MAXLINE-1个字符加\0;

2、  read,write写文件以EOF为结束,写字符串别忘了写上\0

3、  strlen不包括\0长度,所以在write时要strlen+1

posted @ 2015-11-24 01:05  鎏鑫岁月  阅读(707)  评论(0编辑  收藏  举报