计网 网络编程 TCP单进程循环服务器与单进程客户端 (五则运算)

avatar
作者
猴君
阅读量:0

Linux 网络应用编程
写在前面:写了好几天的代码,写前甚至根本不会运行这种代码啊啊啊!网上也搜不到太多的参考,所以超痛苦,一定要总结下这艰难的日子

一、首先是基本框架

包含套接字相关以及客户端和服务器的基本架构,这里主要是参考的老师给出的大框架以及这几篇文章的框架。

TCP 简单回声
这篇超有用,因为感觉要求和代码都很适合我们的实验,(甚至怀疑真的是我们之前的学长学姐的实验?雾)基本框架和语句都是在这学的。
网络编程基本框架
这篇是刚开始就搜到的,感觉框架也很全,但是实在是不知道是完成啥的也就没仔细研究。

这部分(最开始写的时候)我的重点是相关语句,刚拿到手根本不知道有些句子是干啥的,这时候的目的就是写个能运行的玩意跑一下,但是,但是连个能运行的都不知道咋写,甚至不知道咋运行。

唯一运行的线索是第一篇的编译运行给出的代码(第五条这里)
在这里插入图片描述
但是,不知道咋在一个终端里面初始化完服务器直接链接客户端,没错,后来划水去小b搜了一下网络编程,发现人家,打开了两个终端,没错,非常合理,一个服务器,另一个客户端,所以终于搞出了一个能运行的代码。

这么运行!:
两个终端!

此时我的全部目的就是能运行,所以五则运算这里的逻辑是直接传了字符串(简单回声实验里面传的是字符串,所以只会传字符串了)
但还有一个问题就是简单回声的代码我没有办法正确connect,此时的搜索包括不限于:
在这里插入图片描述

发现问题后,
就诞生了第一版抽象,但是勉强正确运行的代码:

服务器:

 #include<stdio.h> #include<unistd.h> #include<stdint.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<signal.h> #include<stdlib.h> #include<sys/types.h>  int sigint_flag = 0; void handle_sigint(int sig){     printf("[srv] SIGINT is coming!\n");     sigint_flag = 1; } void usage(const char *str) {     printf("%s  usage:<IP><PORT><veri_code>\n", str); } void srv_biz(int connfd);  int main(int argc,char **argv){      if(argc != 3){         usage(argv[0]);         return 1;     }          struct sigaction sa;     sa.sa_flags = 0;     sa.sa_handler = handle_sigint;     sigemptyset(&sa.sa_mask);     sigaction(SIGINT,&sa,NULL);          char ip_adress[20];     strcpy(ip_adress,argv[1]);          int port = atoi(argv[2]);      int listenfd = socket(AF_INET,SOCK_STREAM,0);     struct sockaddr_in serv_addr,cli_addr;      memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充     memset(&cli_addr, 0, sizeof(cli_addr));          serv_addr.sin_family = AF_INET;  //使用IPv4地址     serv_addr.sin_addr.s_addr = inet_addr(argv[1]);  //具体的IP地址     serv_addr.sin_port = htons(port);      socklen_t clilen;     char cli_ip[1024];      printf("[srv] server [%s:%d] is initializing!\n",ip_adress,port);          int chec=0;     chec=bind(listenfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));     if(chec<0){         perror("bind error");         return 1;     }     chec=listen(listenfd,10);     if(chec<0){         perror("listen error");         return 1;     }      while(!sigint_flag){         int clilen = sizeof(cli_addr);         int connfd = accept(listenfd,(struct sockaddr*) &cli_addr,&clilen);                  if(connfd<0){             if(errno == EINTR)                 continue;             else                 perror("accept error");             return 1;                 //print error exit;         }         inet_ntop(AF_INET,&(cli_addr.sin_addr),cli_ip,sizeof(cli_ip));         int cli_port=ntohs(cli_addr.sin_port);         printf("[srv] client[%s:%d] is accepted!\n",cli_ip,cli_port);                      srv_biz(connfd);         printf("[srv] client[%s:%d] is closed!\n",cli_ip,cli_port);          close(connfd);      }      close(listenfd);     printf("[srv] listenfd is closed!");     printf("[srv] server is going to exit!");     return 0; }  void srv_biz(int connfd){     char message[1024]={0};     char ret_message[1024]={0};     while(1){         memset(message,0,140);         ssize_t size_read = read(connfd,message,sizeof(message)-1);         if(size_read <= 0||strncmp(message,"EXIT",4)==0){             return ;         }              int i=4,a=0,b=0,sum=0,j=0;             while(message[i]<='9'&&message[i]>='1'){                 ret_message[j]=message[i];                 a=a*10+message[i]-'0';                 i++;                 j++;             }             i++;             ret_message[j]=32;             j++;             if(strncmp(message,"ADD",3)==0){ ret_message[j]='+';}             if(strncmp(message,"MUL",3)==0){ ret_message[j]='*';}             if(strncmp(message,"MOD",3)==0){ ret_message[j]='%';}             if(strncmp(message,"DIV",3)==0){ ret_message[j]='/';}             if(strncmp(message,"SUB",3)==0){ ret_message[j]='-';}             j++;             ret_message[j]=32;             j++;             while(message[i]<='9'&&message[i]>='1'){                 ret_message[j]=message[i];                 b=b*10+message[i]-'0';                 i++;                 j++;             }             //printf("%d,%d\n",a,b);             if(strncmp(message,"ADD",3)==0){ sum=a+b;}             if(strncmp(message,"MUL",3)==0){ sum=a*b;}             if(strncmp(message,"MOD",3)==0){ sum=a%b;}             if(strncmp(message,"DIV",3)==0){ sum=a/b;}             if(strncmp(message,"SUB",3)==0){ sum=a-b;}             ret_message[j]=32;             j++;             char string[32]={0};            // while(sum!=0){             //itoa(sum, string, 10);                          sprintf(string,"%d",sum);             strcat(ret_message,string);                          ret_message[strlen(ret_message)] = '\0';     //puts(ret_message);         ssize_t size_write = write(connfd,ret_message,strlen(ret_message)+2);     } 

客户端:

#include<stdio.h> #include<unistd.h> #include<stdint.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<signal.h> #include<stdlib.h> #include<sys/types.h>  void cli_biz(int connfd); int main(int argc,char *argv[]){      char ip_str[20] = {0};     int port = atoi(argv[2]);     int connfd = socket(AF_INET,SOCK_STREAM,0);      struct sockaddr_in serv_addr;          serv_addr.sin_family = AF_INET;  //使用IPv4地址     serv_addr.sin_addr.s_addr = inet_addr(argv[1]);  //具体的IP地址     serv_addr.sin_port = htons(port);      if(connect(connfd,(struct sockaddr*) &serv_addr,sizeof(serv_addr))<0){     perror("connect failed");     return 1;         }     else {         inet_ntop(AF_INET, &serv_addr.sin_addr, ip_str, sizeof(ip_str));         printf("[cli] server[%s:%d] is connected\n", ip_str , ntohs(serv_addr.sin_port)) ;     }          cli_biz(connfd);     close(connfd);     return 0; }  void cli_biz(int connfd){     char message[1024];     while(1){         //获取用户输入         fgets(message,sizeof(message),stdin);         //如果输入EXIT/SIGINT,退出         if(strcmp(message,"EXIT\n")==0)             return ;         if(strcmp(message,"SIGINT\n")==0)             return ;         //解析输入数据,执行业务逻辑                  //发送+接收+解析自定义应用层协议PDU         ssize_t size_write = write(connfd,message,strlen(message));         ssize_t size_read = read(connfd,message,sizeof(message)-1);         printf("[rep_rcv] %s\n",message);      }      } 

二、正确完成报文建立和传输

这里要求是共20字节报文传输,4字节数字代表操作符,两个8字节的操作数字。

所以,本人又经过了使用数组传递,很成功的运行出了结果,但是,并不符合要求!伟大的舍友聪明的舍友已经满分,并提示我要用结构体传输!(永远的神)

所以这时目的就变成了使用read和write传一个20字节的结构体,这里需要用到的搜索包括但不限于:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
这里共有几个问题:
1.结构体怎么传,read函数原型里面中间的缓冲区是void * buf,直接写read(connf,buf,sizeof(buf))(buf为结构体,分别含int32_t op,int64_t a,b)会报错,这里要在buf前加个“&”,不能直接传
2.结构体对齐问题,结构体存储会对齐,导致这里其实sizeof(buf)是24,(这里如果不懂可以搜索下原因),但是这样子不合题意,所以要取消结构体自动对齐(这个也可以自行搜索取消对齐的办法)
3.字节序转换,注意正确使用字节序转换函数(多少位用多少的转换函数!!!!建议搜索16、32、64位字节转换函数并选择合适函数)

所以到此会有更为成功的一版代码:
核心部分:(更符合要求)

void cli_biz(int connfd){     while(1){         char message[1024]="";         struct mess buf={0,0,0};         struct mess buff={0,0,0};         //获取用户输入         scanf("%s",message);         //如果输入EXIT/SIGINT,退出         if(strcmp(message,"EXIT")==0){             printf("[cli] command EIXT received\n");             return ;         }         if(strcmp(message,"SIGINT")==0)             return ;         //解析输入数据,执行业务逻辑         int64_t rebuf=0;         scanf("%ld%ld",&buf.a,&buf.b);     //int64_t buf[4]={0},rebuf[4]={0};          //if(strncmp(message,"EXIT",4) == 0){ buf[0]=0; }          //else{          if(strncmp(message,"ADD",3) == 0){ fu='+';buf.op=0x1; }          if(strncmp(message,"MUL",3) == 0){ fu='*';buf.op=0x4; }          if(strncmp(message,"MOD",3) == 0){ fu='%';buf.op=0x10;}           if(strncmp(message,"DIV",3) == 0){ fu='/';buf.op=0x8; }          if(strncmp(message,"SUB",3) == 0){ fu='-';buf.op=0x2; }               //  while(message[i]<='9'&&message[i]>='0'){            //     buf[1]=buf[1]*10+message[i]-'0';            //     i++;           //  }            // i++;            // while(message[i]<='9'&&message[i]>='0'){           //      buf[2]=buf[2]*10+message[i]-'0';           //      i++;           //  }            //}         //发送+接收+解析自定义应用层协议PDU         //printf("[rep_rcv] %ld %ld %ld\n",buf[1],buf[2],buf[0]);         buff.op=htonl(buf.op);         buff.a=htobe64(buf.a);         buff.b=htobe64(buf.b);         ssize_t size_write = write(connfd,&buff,sizeof(buff));         //printf("%ld",sizeof(buf));         ssize_t size_read = read(connfd,&rebuf,sizeof(rebuf));         rebuf=be64toh(rebuf);         printf("[rep_rcv] %ld %c %ld = %ld\n",buf.a,fu,buf.b,rebuf);      }      } 
  void srv_biz(int connfd){     while(1){         struct mess buf={0,0,0};         int64_t rebuf=0;         ssize_t size_read = read(connfd,&buf,sizeof(buf));                  if(size_read <= 0){             return ;         }         //printf("%ld\n",sizeof(buf));         buf.op=ntohl(buf.op);         buf.a=be64toh(buf.a);         buf.b=be64toh(buf.b);                  if(buf.op==0x1){ fu='+';rebuf=buf.a+buf.b;}         if(buf.op==0x2){ fu='-';rebuf=buf.a-buf.b;}         if(buf.op==0x4){ fu='*';rebuf=buf.a*buf.b;}         if(buf.op==0x8){ fu='/';rebuf=buf.a/buf.b;}         if(buf.op==0x10){ fu='%';rebuf=buf.a%buf.b;}                  printf("[rqt_res] %ld %c %ld = %ld\n",buf.a,fu,buf.b,rebuf);         rebuf=htobe64(rebuf);         //printf("%ld\n",sizeof(rebuf));         ssize_t size_write = write(connfd,&rebuf,sizeof(rebuf));     } } 

由于现在(2024/6/27)作业还没截止,所以这个总结先存个草稿,等下次想起来再发。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!