【网络】协议的理解

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

一. 什么是协议


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


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


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


二. 为什么要有协议


为了使数据在网络上能够从源到达目的地,网络通信的参与方必须遵循相同的规则,这套规则就称为协议(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月前
|
数据采集 算法 数据挖掘
模块化控制协议(MCP)在网络中增强智能体执行效率的研究
随着Web3技术的迅速发展,去中心化应用和智能体在各种领域的应用逐渐增多。MCP(Modularized Control Protocol,模块化控制协议)作为一种增强智能体执行能力的关键技术,为Web3场景中的智能体提供了更强的灵活性和可扩展性。本文将探讨如何利用MCP技术提升智能体在Web3场景中的执行能力,并通过实例代码展示其实现路径。
270 22
|
14天前
|
监控 负载均衡 安全
WebSocket网络编程深度实践:从协议原理到生产级应用
蒋星熠Jaxonic,技术宇宙中的星际旅人,以代码为舟、算法为帆,探索实时通信的无限可能。本文深入解析WebSocket协议原理、工程实践与架构设计,涵盖握手机制、心跳保活、集群部署、安全防护等核心内容,结合代码示例与架构图,助你构建稳定高效的实时应用,在二进制星河中谱写极客诗篇。
WebSocket网络编程深度实践:从协议原理到生产级应用
|
1月前
|
运维 架构师 安全
二层协议透明传输:让跨域二层协议“无感穿越”多服务商网络
简介:本文详解二层协议透明传输技术,适用于企业网工、运营商及架构师,解决LLDP/LACP/BPDU跨运营商传输难题,实现端到端协议透传,提升网络韧性与运维效率。
|
5月前
|
安全 网络协议 Linux
Linux网络应用层协议展示:HTTP与HTTPS
此外,必须注意,从HTTP迁移到HTTPS是一项重要且必要的任务,因为这不仅关乎用户信息的安全,也有利于你的网站评级和粉丝的信心。在网络世界中,信息的安全就是一切,选择HTTPS,让您的网站更加安全,使您的用户满意,也使您感到满意。
156 18
|
11月前
|
负载均衡 网络协议 算法
|
6月前
|
安全 网络安全 定位技术
网络通讯技术:HTTP POST协议用于发送本地压缩数据到服务器的方案。
总的来说,无论你是一名网络开发者,还是普通的IT工作人员,理解并掌握POST方法的运用是非常有价值的。它就像一艘快速,稳定,安全的大船,始终为我们在网络海洋中的冒险提供了可靠的支持。
199 22
|
6月前
|
网络协议 数据安全/隐私保护 网络架构
|
7月前
|
缓存 网络协议 API
掌握网络通信协议和技术:开发者指南
本文探讨了常见的网络通信协议和技术,如HTTP、SSE、GraphQL、TCP、WebSocket和Socket.IO,分析了它们的功能、优劣势及适用场景。开发者需根据应用需求选择合适的协议,以构建高效、可扩展的应用程序。同时,测试与调试工具(如Apipost)能助力开发者在不同网络环境下优化性能,提升用户体验。掌握这些协议是现代软件开发者的必备技能,对项目成功至关重要。
|
8月前
|
人工智能 自然语言处理 决策智能
智能体竟能自行组建通信网络,还能自创协议提升通信效率
《一种适用于大型语言模型网络的可扩展通信协议》提出创新协议Agora,解决多智能体系统中的“通信三难困境”,即异构性、通用性和成本问题。Agora通过标准协议、结构化数据和自然语言三种通信格式,实现高效协作,支持复杂任务自动化。演示场景显示其在预订服务和天气预报等应用中的优越性能。论文地址:https://arxiv.org/pdf/2410.11905。
245 6
|
10月前
|
前端开发 网络协议 安全
【网络原理】——HTTP协议、fiddler抓包
HTTP超文本传输,HTML,fiddler抓包,URL,urlencode,HTTP首行方法,GET方法,POST方法