服务器版的加法器
客户端 :
我们需要把要计算的两个加数发过去
服务器端 :
进行计算
方案 :
方案一:
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;
}