实现一个服务器版的计算器

服务器版的加法器


客户端 :

我们需要把要计算的两个加数发过去

服务器端 :

进行计算

方案 :


方案一:

1. 客户端发送一个例如“1+1”的字符串
2. 这个字符串中有两个操作数,都是整型
3. 两个数字之间有一个字符是运算符, 运算符只能是 + 
4. 数字和运算符之间没有空格
........

方案二:

1. 定义结构体来表示我们需要发送的信息 
2. 发送数据时将这个结构体按照一个规则转换成字符串 //客户端:写的时候用request 读的时候用response(因为客户端输入的是两个操作数,比如10 20)
3. 接收到数据的时候再按照相同的规则把字符串转化为结构体 //服务器端:读的时候用request 写的时候用response(服务器端,回应的时候是计算后的结果30,系统会将其看成是一个字符串,所以在客户端接受的时候需要转换)
4. 这个过程叫做 “序列化” 和 “反序列化” 
序列化:将两个数字和运算符转为字符串(客户端)—— 多变一
反序列化:将字符串再转为两个数字(服务器端)—— 一变多

无论我们采用方案一, 还是方案二, 还是其他的方案, 只要保证 一端发送时构造的数据, 在另一端能够正确的进行解析, 就是OK的 。这种约定, 就是 应用层协议。

代码实现:


1. 以下的两个结构体就是我们此处自定制的应用层协议 :

comm.h:
typedef struct Request{
       int x;
       int y;
       int op;// 操作符
}Request;
typedef struct Response{
       int sum;
       int status;//状态信息
}Response;

2. 服务器端:

#include <stdio.h>                                                                                                                                                
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>   
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/wait.h>

#include "comm.h"
typedef struct Arg
{
    int fd;
}Arg;
void ProcessRequest (int sock)
{
    while(1){
    Response rp = {0,0};
    Request r;
    read(sock,&r,sizeof(r));

    switch(r.op)
    {
        case 1:
            rp.sum = r.x+r.y;
            break;
        case 2:
            rp.sum = r.x-r.y;
             break;
        case 3:
             rp.sum = r.x*r.y;
             break;
        case 4:
           {
                 if(r.y == 0  )//非法的
                 {
                     rp.status = -1;
                 }
                 else
                 {
                     rp.sum = r.x/r.y;
                 }
             }
             break;
        case 5:
              {   
                  if(r.y == 0  )//非法的
                  {   
                      rp.status = -1; 
                  }   
                  else
                  {   
                      rp.sum = r.x%r.y;
                  } 
              }   
              break;
        default:
              rp.status = -3;
              break;
    }
    write(sock,&rp,sizeof(rp));
    }
}

void* service(void* ptr)
{
    Arg* arg = (Arg *)ptr;
    ProcessRequest(arg->fd);
    free(arg);
    return NULL;
}
int main(int argc,char* argv[])
{
    if(argc != 3)
    {   
        printf("Usage:%s ip port\n",argv[0]);
        return 1; 
    }   

     //1. 创建套接字

     int listen_sock = socket(AF_INET, SOCK_STREAM , 0 );// SOCK_DGRAM 表示UDP
     if(listen_sock < 0)
     {   
         perror("socket");
         return 2;
     }   
     printf("Socker:%d\n",listen_sock);

     int opt = 1;
     setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    //2. 命名套接字

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(argv[2]));//端口号是2位的数
    local.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY可以绑定任意ip
     // 3. 绑定端口号
     if( bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0  )
     {
         perror("bind");
         return 3;
     }

     // 4. 开始监听listen_sock
     if( listen(listen_sock,5) < 0 )//这里的5是允许有5个客户端连接等待,如果收到更多的请求则忽略
     {
         perror("listen");
         return 4;
     }


     // 5. 接受请求
     printf("bind and listen success,wait accept...\n");

     for(;;)
     {
         struct sockaddr_in client;
         socklen_t  len = sizeof(client);
         int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);
         if( new_sock < 0 )
         {
             perror("accept");
             continue;//如果出现错误,则继续接受请求
         }
         printf("get new link![%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
         pthread_t id = 0;
         Arg* arg = (Arg*)malloc(sizeof(Arg));
         arg->fd = new_sock;                               
         pthread_create(&id,NULL,service,(void *)arg);
         pthread_detach(id);
          }
         return 0;
}

3. 客户端:

#include <stdio.h>                                                                                                                                                
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>   
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#include "comm.h"
int main(int argc,char* argv[])
{
        if(argc != 3)
        {
            printf("Usage:%s ip port\n",argv[0]);
            return 1; 
        }   
        //1. 创建套接字

        int sock = socket(AF_INET, SOCK_STREAM , 0 );// SOCK_DGRAM 表示UDP
        if( sock < 0 )
        {   
            perror("socket");
            return 2;
        }   
        printf("Socker:%d\n",sock);

        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(atoi(argv[2]));
        server.sin_addr.s_addr = inet_addr(argv[1]);

        // 2. 建立连接
        if( connect(sock,(struct sockaddr*)&server,sizeof(server)) < 0  )
         {
            perror(" connnet ");
            return 3;
        }

        char buf[128];
        Request r;
        Response rp;
        while(1)
        {
            printf("Please Enter<x,y>:");
            scanf("%d%d",&r.x,&r.y);
            fflush(stdout);
            printf("Please Enter op[1(+),2(-),3(*),4(/),5(%)]:");
            scanf("%d",&r.op);

            write(sock,(void *)&r,sizeof(r));
            read(sock,&rp,sizeof(rp));
            printf("status: %d,result: %d\n",rp.status,rp.sum);
         }
        return 0;
 }

测试:


服务器端:

这里写图片描述

客户端一:

这里写图片描述

客户端二:

这里写图片描述


猜你喜欢

转载自blog.csdn.net/qq_37941471/article/details/80788174