在网络基础------TCP/IP五层模型一文中笼统的介绍了有关TCP/IP五层模型的相关知识点,本文中将详细的说明TCP/IP五层网络模型中的应用层,及相关的应用层协议。
应用层概述
应用层的任务是通过应用程序间的交互来完成特定的网络应用。根据网络应用的不同,应用程序之间就需要根据不同的方式来实现交互。如:如果两个应用程序之间要传递整型数据,那么二者之间必须遵循这样的方式:二者之间必须以整型来接收和发送数据。我们把这样的方式也可以成为双方约定好的“协议”。
我们可以将上述的网络应用理解为一个个实际需要解决的问题。由于需要解决的实际问题不同,应用程序与应用程序之间就需要制定不同的协议。这些为解决某一类应用问题所制定的协议称为应用层协议。而问题的解决是通过不同主机上的多个应用进程之间的交互来实现的,因此,应用层协议定义为应用进程间通信和交互的规则。
下面我们自己来指定一个应用层协议来完成特定的网络应用。
例如:实现一个网络版的整数加法计算器。通过在客户端读取两个加数,然后发送给服务器端,经服务器端计算后将结果反馈给客户端并显示出结果。
这里,我们首先要明确一个概念。网络中的数据是以字节流(二进制流)的方式进行传送的。所以在对客户端的两个整数加数进行传输时,要将其转化为二进制流的形式。从网络中接收的数据也是一串字节流(二进制流)。所以,客户端接收到二进制流之后要将其转化为对应的整数进行输出。
我们可以将两个加数封装为一个结构体,而结构体中的数据都是以“0”“1”这样的数据进行存储的。所以可以将该结构体直接通过网络进行发送。待客户端接收到结果对应的二进制流之后,再将其存放在整型变量中。
上述中,将结构化的数据转化为二进制流称为序列化。将二进制流转化为结构化的数据称为反序列化。
因此,根据前面所说过的TCP协议以及相关的接口来编写代码如下:
结构体封装:
//将两个加数和被加数封装为结构体
typedef struct
{
int x;
int y;
}Request;
//加法计算的结果
typedef struct
{
int sum;
}Response;
服务器端部分代码:
void server_work(int client_sock,char* ip,int port)
{
Request req;//定义结构体变量用于接受客户端发来的两个加数
Response res;//定义结构体变量用于存储计算的结果
while(1)
{
//从客户端读取两个加数
ssize_t s = read(client_sock,&req,sizeof(req));
if(s < 0)//读取失败
{
//错误处理
printf("read error\n");
break;
}
else if(s == 0)
{
///客户端已断开连接
printf("client:[%s][%d] quit\n",ip,port);
break;
}
//计算结果
res.sum = req.x + req.y;
//将结果发送给客户端
write(client_sock,&res,sizeof(res));
}
}
客户端部分代码:
while(1)
{
Request req;//定义结构体变量用于存储两个加数
Response res;//定义结构体变量用于接受来自服务器端计算的结果
printf("please enter:");
fflush(stdout);
scanf("%d%d",&req.x,&req.y);//从键盘读取两个加数
write(sock,&req,sizeof(req));//将两个加数发送给服务器端
read(sock,&res,sizeof(res));//接收来自服务器端的结果
printf("%d + %d = %d\n",req.x,req.y,res.sum);//输出结果
}
以上关于服务器端和客户端的其余代码见:这篇博客,其中多进程多线程版本都可以实现计算器的功能。