TCP/IP基本实现

本文深入介绍了TCP/IP协议族的四层模型,包括应用层、传输层、网络层和网络接口层,讲解了各层的主要协议和功能。此外,探讨了MAC地址、IP地址的作用以及如何查看。通过示例展示了如何使用tcpdump工具抓包,并解释了流量控制和MTU概念。最后,给出了基于TCP协议的客户端和服务端的C++实现,演示了网络通信的基本流程。
  • 应用层:应用程序通过这一层访问网络,常见 FTP、HTTP、DNS 和 TELNET 协议;
  • 传输层:TCP 协议和 UDP 协议;
  • 网络层:IP 协议,ARP、RARP 协议,ICMP 协议等;
  • 网络接口层:是 TCP/IP 协议的基层,负责数据帧的发送和接收。
ifconfig -a

MAC(Media Access Control)地址,或称为物理地址、硬件地址,用来定义互联网中设备的位置。

在 TCP/IP 层次模型中,网络层管理 IP 地址,链路层则负责 MAC 地址。因此每个网络位置会有一个专属于它的 IP 地址,而每个主机会有一个专属于它 MAC 地址。

img

  1. 查看自己电脑的 IP 地址,并截图。
  2. ping github.com,查看 IP 地址,并截图

了解了上面的理论知识过后,我们可以使用 tcpdump 这个抓包工具来实际看一下。

sudo tcpdump -ntx -c 1
  • -n :显示 IP 地址而非域名地址
  • -t :不显示时间戳
  • -x :以十六进制显示包内内容
  • -c :tcpdump 将在接受到几个数据包后退出

由此可见,流量控制实际上是对发送方数据流量的控制,使其发送速率不超过接收方的速率。所以需要一些规则使得发送方知道在什么情况下可以接着发送下一帧,而在什么情况下必须暂停发送,以等待收到某种反馈信息后再继续发送。这就是流量控制。

为了提供足够快的响应时间,以太网和 IEEE802.3 对数据帧长度都有限制,其最大值分别为 1500 字节和 1492 字节,链路层的这个特性称作 MTU,即最大传输单元

用命令 netstat -in 可以查看网络接口的 MTU:

基于TCP协议的客户端和服务端实现

服务端口代码

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <sys/socket.h>
#include "base.h"

//出错调用函数
static void error_handle(std::string opt, std::string message)
{
    //根据errno值获取失败原因并打印到终端
    perror(opt.c_str());
    std::cout << message << std::endl;
    exit(1);
}

int main(int argc, char *argv[])
{
    int serv_sock, client_sock;
    char message[BUFFSIZE];
    int str_len, ret;

    struct sockaddr_in serv_adr, client_adr;
    socklen_t client_adr_size;

    //判断命令行参数合法性
    if(argc < 2)
    {
        std::cout << "Usage : " << argv[0] << " <port>" << std::endl;
        exit(1);
    }

    //创建socket套接字
    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    if(serv_sock < 0)
    {
        error_handle("socket", "socket() error.");
    }

    //初始化服务器套接字参数,设置网卡IP 和端口号
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_adr.sin_port = htons(atoi(argv[1]));

    //绑定端口
    ret = bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
    if(ret < 0)
    {
        error_handle("bind", "bind() error.");
    }

    //监听TCP端口号
    ret = listen(serv_sock, 5);
    if(ret < 0)
    {
        error_handle("listen", "listen() error.");
    }

    client_adr_size = sizeof(client_adr);

    while(1)
    {
        //使用accept接收客户端连接请求
        client_sock = accept(serv_sock, (struct sockaddr*)&client_adr, &client_adr_size);
        if(client_sock < 0)
        {
            //接收客户端连接请求失败,
            continue;
        }
        //接收新的客户端请求,进行客户端数据处理
        std::cout << "Accept New Client : " << inet_ntoa(client_adr.sin_addr) << " , port : " << ntohs(client_adr.sin_port) << std::endl;
        std::cout << "Start Recv Client Data..." << std::endl;

        //清空缓存区
        memset((void *)&message, 0, BUFFSIZE);
        while((str_len = read(client_sock, message, BUFFSIZE)) != 0)
        {
            //成功读取客户端发送来的数据消息
            //打印输出
            std::cout << "Recv Message : " << message << std::endl;
            //将消息回传给客户端,作为回声服务器,类似 echo 命令
            write(client_sock, message, str_len);
            //清空缓存区,等待再次读取数据
            memset(message, 0, BUFFSIZE);
        }

        //客户端断开连接,关闭套接字
        close(client_sock);
    }

    //服务器关闭,关闭服务器套接字
    close(serv_sock);
    return 0;
}

客户端

//
// Created by wpf on 2022/8/7.
//

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <sys/socket.h>
#include "base.h"

//出错调用函数
static void error_handle(std::string opt, std::string message)
{
    //根据errno值获取失败原因并打印到终端
    perror(opt.c_str());
    std::cout << message << std::endl;
    exit(1);
}

int main(int argc, char *argv[])
{
    int sock, ret;
    char message[BUFFSIZE];
    int str_len;
    struct sockaddr_in serv_adr;

    if(argc < 3)
    {
        std::cout << "Usage : " << argv[0] << " <IP> <port>" << std::endl;
        exit(1);
    }

    sock = socket(PF_INET, SOCK_STREAM, 0);
    if(sock < 0)
    {
        error_handle("socket", "socket() error.");
    }

    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
    serv_adr.sin_port = htons(atoi(argv[2]));

    ret = connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
    if(ret < 0)
    {
        error_handle("connect", "connect() error.");
    }

    std::cout << "Connect Success..." << std::endl;

    //进入数据处理
    while(1)
    {
        std::cout << "Please Input Message(Q to quit) : " << std::endl;
        std::cin >> message;
        if((!strcmp(message, "q")) || (!strcmp(message, "Q")))
        {
            //退出客户端
            break;
        }

        //将数据发送给服务端
        write(sock, message, strlen(message));
        //读取服务端回传的数据
        str_len = read(sock, message, BUFFSIZE-1);
        message[str_len] = 0;
        //打印输出
        std::cout << "Echo Message : " << message << std::endl;
    }

    //关闭套接字
    close(sock);
    return 0;
}
v2.3正式版本正式更新说明一、版本概述本次更新主要围绕 MER 多端商户体系进行功能完善,重点覆盖平台端、商户端、移动端、PC 端及后端服务。核心方向包括:商户与店铺业务拆分、商户入驻流程完善、店铺分组/商圈管理增强、平台业务角色与权限边界优化,以及商品、订单、营销、统计等模块适配多端权限体系。二、新增功能新增商户端及商户主体相关功能模块平台端新增商户端、商户主体相关路由、菜单、权限及页面入口。后端补全商户端、商户主体相关 Controller、Service、权限数据及菜单 SQL。支持平台、商户、商户主体等不同角色进入对应业务管理范围。新增平台业务管理角色选择能力平台端 /platform/business/list 支持创建业务账号时选择角色。后端新增或复用业务角色选项接口,创建业务账号时按所选角色写入。修正业务账号默认角色写死的问题。新增商户/店铺入驻流程能力平台端完善商户申请入驻、审核、店铺创建、店铺列表等页面。移动端完善用户入驻申请页面、入驻记录、入驻背景图与申请数据提交。PC 端新增或完善商户入驻、商户列表、商户主页、推荐商户等页面。新增店铺分组/商圈管理能力平台端新增店铺分组管理、店铺分组维护、商圈关联店铺等页面能力。后端补全店铺分组列表、详情、新增、编辑、启停、显示开关、关联店铺等接口。移动端支持基于商圈/店铺分组进行选择、展示及相关数据联动。新增店铺分组/商圈“店铺数量”展示店铺分组列表和店铺分组维护页新增“店铺数量”字段展示。后端列表与详情接口返回 merchantCount,前端统一展示当前店铺分组/商圈绑定店铺数量。新增统计类能力新增商品、订单、会员、用户等统计相关路由和接口支持。商户端/业务端补充首页统计、商品统计、订单统计、会员统计等数据能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值