【网络】协议的理解

简介: 下面是百度百科对计算机协议意思的解释:

一. 什么是协议


下面是百度百科对计算机协议意思的解释:


协议,网络协议的简称,网络协议是通信计算机双方必须共同遵从的一组约定。如怎么样建立连接、怎么样互相识别等。只有遵守这个约定,计算机之间才能相互通信交流。


通俗的讲协议是一种约定,约定所有参与方的行为。没有协议就会混乱,说不清道不明或不在同一频道,即协议是一种背景知识和行为规范。


二. 为什么要有协议


为了使数据在网络上能够从源到达目的地,网络通信的参与方必须遵循相同的规则,这套规则就称为协议(protocol),它最终体现为在网络上传输的数据包的格式。


三. 利用套接字实现一个简单的网络计算器


1. 代码实现


通信的形式多种多样,只要涉及通信就涉及到协议。要实现网络版计算器,我们把需要计算的数据和操作符保存到一个类对象(Request)中,然后客户端把这个类对象通过网络发给服务端;服务端拿到这个类对象后完成计算并把计算结果存储到另外定义的一个类对象(Respond)中,并返回给客户端,这样一次通信就完成了。


protocol.h

需要计算的数据保存到class Request类对象中,计算结果保存到class Respond类对象中,这两种结构要保证客户端和服务端同时都能看到,所以我们把它俩的声明放到一个头文件中,这个头文件里的内容就相当于客户端、服务端的通信协议。


/* protocol.h */                                                                                                               
#pragma once     
struct Request    
{    
  // 两个整型操作数和一个操作符(+-*/%)    
  int x;    
  int y;    
  char op;    
};    
struct Respond    
{    
  // 0 -> 正确运算    
  // 1 -> 除0错误    
  // 2 -> 模0错误    
  // 3 -> 非法运算符    
  int status = 0;    
  int result = 0;// 运算结果    
};



通信协议制定好了,现在我们要开始写客户端/服务端的代码了,应用层网络开发的话我们可以使用socket编程。


server.cpp


服务端通过TCP协议不断接收客户端发来的连接,然后派生一个子线程去专门处理该客户端的计算任务,子线程计算完成后把结果发还给客户端并继续等待客户端发来计算任务。


/* server.cpp */  
#include "protocol.h"                                                                                                          
#include <unistd.h>    
#include <pthread.h>    
#include <string.h>    
#include <sys/socket.h>    
#include <sys/types.h>    
#include <netinet/in.h>    
#include <arpa/inet.h>    
#include <iostream>    
using namespace std;    
// 子线程执行函数
void* Routine(void* arg)    
{    
  pthread_detach(pthread_self());    
  int linkSock = *(int*)arg;    
  delete (int*)arg;    
  // 针对处理某一个客户端发来的任务,直到客户端关闭或接收数据失败,这个线程才结束     
  while(1)    
  {    
    Request rq;    
    ssize_t size = recv(linkSock, &rq, sizeof(rq), 0);    
    if(size > 0)    
    {    
      Respond rp;    
      switch(rq.op)    
      {
      case '+':
          rp.result = rq.x + rq.y;
          break;
        case '-':
          rp.result = rq.x - rq.y;
          break;
        case '*':
          rp.result = rq.x * rq.y;
          break;
        case '/':
          if(rq.y == 0)
          {
            rp.status = 1;
          }
          else 
          {
            rp.result = rq.x / rq.y;
          }
          break;                                                                                                               
        case '%':
          if(rq.y == 0)
          {
            rp.status = 2;
          }
    else 
          {
            rp.result = rq.x % rq.y;
          }
          break;
        default:
          rp.status = 3;
          break;
      }
      send(linkSock, &rp, sizeof(rp), 0);
    }
    else if(size == 0)
    {
      cout<<"client close!"<<endl;
      break;                                                                                                                   
    }
  else 
    {
      cerr<<"recv error"<<endl;
      break;
    }
  }
  close(linkSock);
  return nullptr;
}
int main(int argc, char* argv[])
{
  // 检查传入的命令行参数是否符合要求
  if(argc != 2)
  {
    cerr<<"Usage:serverName serverPort"<<endl;
    exit(1);                                                                                                                   
  }
  // 解析传入的命令行参数
  int port = atoi(argv[1]);
  // 创建套接字
  int listenSock = socket(AF_INET, SOCK_STREAM, 0);
  if(listenSock < 0)
  {
    cerr<<"socket error"<<endl;
    exit(2);
  }
  // 绑定socket地址
  struct sockaddr_in local;
  memset(&local, 0, sizeof(local));
  local.sin_family = AF_INET;
  local.sin_port = htons(port);
  local.sin_addr.s_addr = htonl(INADDR_ANY);
  if(bind(listenSock, (struct sockaddr*)&local, sizeof(local)) < 0)
  {
    cerr<<"bind error"<<endl;
    exit(3);
  }
  // 设置监听套接字
  if(listen(listenSock, 5) < 0)                                                                                                
  {
    cerr<<"listen error"<<endl;
    exit(4);
  }
  // 不断地建立连接,然后创建子线程去处理连接任务
  struct sockaddr_in peer;
  memset(&peer, 0, sizeof(peer));
  socklen_t len = sizeof(peer);
  while(1)                         
  {                                
    int linkSock = accept(listenSock, (struct sockaddr*)&peer, &len);    
    if(linkSock < 0)               
    {           
      cout<<"accept fail, continue next"<<endl;      
      continue;                                      
    }                                
    else                             
    {    
      cout<<"get a new link"<<endl;                                                                                            
      int* pLinkSock = new int(linkSock);    
      pthread_t pid;             
      pthread_create(&pid, nullptr, Routine, pLinkSock);    
    }                            
  } 
  return 0;
}


client.cpp


启动客户端后,客户端首先要去和服务端建立连接关系,连接完成后从键盘接收需要计算的数据,然后把这些数据通过网络发送给服务端的某一个线程让其处理这个计算任务,处理完成后把计算结果打印到屏幕上,一直循环这个过程。



/* client.cpp */                                                                                                               
#include "protocol.h"    
#include <stdlib.h>                                                   
#include <sys/socket.h>    
#include <sys/types.h>              
#include <netinet/in.h>    
#include <arpa/inet.h>    
#include <string>                                      
#include <string.h>    
#include <iostream>    
using namespace std;    
int main(int argc, char* argv[])    
{                                       
  // 参数检查      
  if(argc != 3)                          
  {               
    cerr<<"Usage:clientName serverIp serverPort"<<endl;    
    exit(1);                               
  }                
  // 参数解析                                                           
  string serverIp = argv[1];    
  int serverPort = atoi(argv[2]);    
  // 创建套接字  
  int linkSock = socket(AF_INET, SOCK_STREAM, 0);
  if(linkSock < 0)
  {
    cerr<<"socket error"<<endl;
    exit(2);
  }
  // 和服务端建立连接
  struct sockaddr_in peer;
  memset(&peer, 0, sizeof(peer));
  peer.sin_family = AF_INET;
  peer.sin_port = htons(serverPort);
  peer.sin_addr.s_addr = inet_addr(serverIp.c_str());// PS:注意用了inet_addr就不要在用htonl了,不然会转换出错
  if(connect(linkSock, (struct sockaddr*)&peer, sizeof(peer)) < 0)
  {
    cerr<<"connect error"<<endl;
    exit(3);
  }
  // 死循环从外界获得算式并交给服务端线程去完成任务
  while(1)
  {
    Request rq;
    cout<<"请输入左操作数(整数):";
    cin>>rq.x;
    cout<<"请输入操作符(+-*/%):";
    cin>>rq.op;
    cout<<"请输入右操作数(整数):";
    cin>>rq.y;
    send(linkSock, &rq, sizeof(rq), 0);
    Respond rp;
    recv(linkSock, &rp, sizeof(rp), 0);
    cout<<"status:"<<rp.status<<' '<<"result="<<rp.result<<endl;
  }
  return 0;
}


2. 结果测试


首先编译连接生成两个可执行程序:server和client

31dbb0a4db584e5dbfa741800a05a2a7.png


启动服务端,端口号我们设为8081


18efd969896d4e07b9450beb4ddfbef9.png

启动客户端,在本地环回(127.0.0.1)进行测试,发现连接成功

ac0b71c97e2b40c7bf1c3a471f9622df.png


输入数据,计算的结果也没有问题


d931e2bc370b4bb09c5d3e9146f83b7e.png

最后关闭客户端,此时服务端不会关闭,依然可以接收其他客户端发来的连接请求,而且可以并发的同时处理多个客户端任务。


3a55483db2a14ed9b1df999d225a7e39.png


四. 知识点补充


理解一下TCP/IP四层模型里的协议


下三层负责的是通信细节,而应用层负责的是如何使用传输过来的数据,两台主机在进行通信的时候,应用层的数据能够成功交给对端应用层,因为网络协议栈的下三层已经负责完成了这样的通信细节,而如何使用传输过来的数据就需要我们去定制协议,这里最典型的就是HTTP协议,这个协议是一些大佬已经写好的,在应用层我们只需要遵守规则去使用即可。

42f32fb53175483dbd0eed97c5cecae0.png


socket套接字处于网络协议的那一层?


socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议簇细节隐藏在socket接口后面,对用户来说,一组简单的接口就是全部,让socket去组织数据,以符合指定的协议。而我们所说的socket编程指的是利用socket接口来实现自己的业务和协议。


综上所述:socke接口属于应用层与传输层中间的软件抽象层,而socket编程却是标准的应用层开发。


相关文章
|
3天前
|
负载均衡 网络协议 算法
|
2月前
|
域名解析 存储 网络协议
深入解析网络通信关键要素:IP 协议、DNS 及相关技术
本文详细介绍了IP协议报头结构及其各字段的功能,包括版本、首部长度、服务类型、总长度、标识、片偏移、标志、生存时间(TTL)、协议、首部检验和等内容。此外,还探讨了IP地址的网段划分、特殊IP地址的应用场景,以及路由选择的大致流程。最后,文章简要介绍了DNS协议的作用及其发展历史,解释了域名解析系统的工作原理。
111 5
深入解析网络通信关键要素:IP 协议、DNS 及相关技术
用MASM32按Time Protocol(RFC868)协议编写网络对时程序中的一些有用的函数代码
用MASM32按Time Protocol(RFC868)协议编写网络对时程序中的一些有用的函数代码
|
2月前
|
缓存 算法 物联网
基于AODV和leach协议的自组网络平台matlab仿真,对比吞吐量,负荷,丢包率,剩余节点个数,节点消耗能量
本系统基于MATLAB 2017b,对AODV与LEACH自组网进行了升级仿真,新增运动节点路由测试,修正丢包率统计。AODV是一种按需路由协议,结合DSDV和DSR,支持动态路由。程序包含参数设置、消息收发等功能模块,通过GUI界面配置节点数量、仿真时间和路由协议等参数,并计算网络性能指标。 该代码实现了节点能量管理、簇头选举、路由发现等功能,并统计了网络性能指标。
160 73
|
6天前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
28 13
|
5天前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
6天前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
1月前
|
安全 网络协议 算法
HTTPS网络通信协议揭秘:WEB网站安全的关键技术
HTTPS网络通信协议揭秘:WEB网站安全的关键技术
139 4
HTTPS网络通信协议揭秘:WEB网站安全的关键技术
|
8天前
|
网络协议 安全 NoSQL
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
|
28天前
|
网络协议 网络虚拟化 网络架构
【网络实验】/主机/路由器/交换机/网关/路由协议/RIP+OSPF/DHCP(上)
【网络实验】/主机/路由器/交换机/网关/路由协议/RIP+OSPF/DHCP(上)
55 1