Linux之套接字UDP实现网络通信

news2024/10/7 10:24:24

Linux之套接字UDP实现网络通信

文章目录

  • Linux之套接字UDP实现网络通信
  • 1.引言
  • 2.具体实现
    • 2.1需要知道的套接字接口
      • 1.socket()
      • 2.bind()
      • 3.recvfrom()
      • 4.sendto()
    • 2.2服务器端server.hpp
    • 2.3服务器端server.cc
    • 2.4客户端Client.cc

1.引言

​ 套接字(Socket)是计算机网络中实现网络通信的一种编程接口。它提供了应用程序与网络通信之间的一座桥梁,因为它允许应用程序通过网络发送和接收相应的数据以实现不同主机之间的通信。

在这里插入图片描述

通常套接字由以下两部分组成:

1.网络IP和端口号:IP用来标识主机,而端口号可以标识到单台主机的唯一进程。

2.通信协议:套接字通过规定通信协议来制定数据传输和发送的规则。常见的有TCP和UDP等协议。

TCP是一种面向连接的协议,提供可靠的、有序的、基于字节流的数据传输。

UDP是一种无连接的协议,提供不可靠的、无序的、基于数据报的数据传输。

​ 我们今天要实现的是通过UDP协议实现网络通信。UDP协议通信虽然无连接不可靠,可是足够简单到我们了解通信的基本原理。

那么话不多讲,我们赶快看看我们学习完今天这一篇能够实现出怎么样的结果吧:

在这里插入图片描述

我们通过实现客户端和服务器端,实现了通过套接字UDP创建了一个服务器,之后通过客户端链接并且通信的一个功能。

事不宜迟,我们马上实现!

2.具体实现

​ 首先我们需要明确具体的大思路: 先服务器端创建socket套接字,并recvfrom接收到。客户端也创建套接字绑定后确定到唯一IP和端口号之后即可进行通信。

​ 在具体实现之前我们首先需要一些必要的套接字接口

2.1需要知道的套接字接口

1.socket()

​ socket函数是用于创建套接字的函数,创建成功返回文件描述符fd,失败返回-1

int socket(int domain, int type, int protocol);

​ 参数说明:

  • domain

    :指定套接字的地址族(Address Family)

    今天我们选择:

    • AF_INET:IPv4 地址族
  • type

    :指定套接字的类型(Socket Type)

    今天我们选择:

    • SOCK_DGRAM:无连接的数据报套接字,用于 UDP 协议
  • protocol

    :可选参数,指定具体的传输协议。常用的有:

    ​ 今天我们选择:

    • 0:自动选择合适的协议

2.bind()

​ 在Linux下,bind() 函数用于将一个套接字(socket)与特定的IP地址和端口号进行绑定

*int bind(int sockfd, const struct sockaddr addr,socklen_t addrlen);

参数说明:

  • sockfd:要进行绑定的套接字的文件描述符。
  • addr:指向一个 struct sockaddr 结构体的指针,其中包含要绑定的IP地址和端口号信息。
  • addrlenaddr 结构体的长度。

在绑定bind的第二个参数中,我们也需要用到库中定义好的sockaddr_in结构体来初始化!

具体结构体struct sockaddr_in说明:

结构体中有三个值也需要初始化指定一下:

  1. sin_family:表示地址族(Address Family),一般为 AF_INET

  2. sin_port:表示端口号。它是一个 16 位的整数,使用网络字节序(大端字节序)表示。在使用时,通常需要使用 htons() 函数将主机字节序转换为网络字节序。

  3. sin_addr:表示 IPv4 地址。它是一个 struct in_addr 类型的结构体,用于存储 32 位的 IPv4 地址。

    一般服务端用INADDR_ANY,让udp_server在启动时候可以绑定任何ip.

    ​ 客户端用inet_addr函数将字符串转化成32位无符号整数

3.recvfrom()

从套接字接收数据,并获取发送方的地址

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

参数说明:

  • sockfd:接收数据的套接字的文件描述符。
  • buf:指向接收数据的缓冲区。
  • len:缓冲区的长度。
  • flags:可选的标志参数,用于影响接收操作的行为,通常设为 0。
  • src_addr:用于存储发送方的地址信息(对于面向数据报的套接字)。它是一个 struct sockaddr 结构体的指针。
  • addrlensrc_addr 结构体的长度,作为输入参数指定 src_addr 缓冲区的大小,作为输出参数返回实际地址的长度。

4.sendto()

通过套接字发送数据到指定目的地

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

参数说明:

  • sockfd:要发送数据的套接字的文件描述符。
  • buf:指向要发送数据的缓冲区。
  • len:要发送数据的长度。
  • flags:可选的标志参数,用于影响发送操作的行为,通常设为 0。
  • dest_addr:指向目标地址(接收方地址)的结构体指针,可以是 struct sockaddr 或其派生类型的指针。
  • addrlendest_addr 结构体的长度。

2.2服务器端server.hpp

​ 在服务器的头文件中,我们首先需要定义一个udpserver的类,服务器类中需要有服务器的初始化与启动命令,当然需要有构造析构等。默认的私有成员是**_sock套接字和port端口**


const static uint16_t default_port = 8080;

class UdpServer
{
   public:
    UdpServer(uint16_t port = default_port)
    :_port(port)
    {
        std::cout<< "server addr: "<<_port <<std::endl;
    }

    ~UdpServer() {}

    void InitServer() //初始化服务器
    {
        _sock = socket(AF_INET,SOCK_DGRAM,0);
        if(_sock < 0)
        {
            std::cerr << " socket create err " << std::endl;
        }
        std::cout << "create socket success: " << _sock << std::endl;

        struct sockaddr_in local; //利用库中创建好的结构体来初始化socket
        memset(&local,0,sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port); // 本地主机序列转网络序列
        local.sin_addr.s_addr = INADDR_ANY; //让udp_server在启动时候可以绑定任何ip

         //绑定
        if(bind(_sock,(struct sockaddr*)&local,sizeof(local)) < 0)
        {
            std::cerr << " bind error" << std::endl;
            exit(-1);
        }
        std::cout << "bind socket success: " << _sock << std::endl;
    }   
        void Start()    //执行逻辑
        {
            char buffer[1024];
            while(true)
            {   //接收数据
                //ssize_t recvfrom(套接字,缓冲区,缓冲区大小,flag = 0,client的IP和port,实际结构体大小);
                struct sockaddr_in far; //远端定义结构体
                socklen_t len = sizeof(far); 
                int n = recvfrom(_sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&far,&len);
                if(n > 0) buffer[n] = '\0';
                else continue;
                
                std::string clientip = inet_ntoa(far.sin_addr); //ipv4的地址从二进制转化为点分十进制的函数
                uint16_t clientport = ntohs(far.sin_port);      //将网络字节序转化为一个本地主机字节序
                std::cout<< clientip << "-" << clientport << "#" << buffer << std::endl;

                //发送数据
                //ssize_t sendto(套接字,发的数据,数据大小,flag = 0,(struct sockaddr*)&far,sizeof(far));
                sendto(_sock,buffer,sizeof(buffer),0,(struct sockaddr*)&far,sizeof(far));

                
            }

        }


    

   private:

    int _sock; //套接字
    uint16_t _port; //端口

};

2.3服务器端server.cc

在服务器端的使用中,我们采用智能指针unique_ptr来帮助资源创建以及销毁,在使用中,我们调用以上server.hpp中类的初始化与启动函数即可.

//输出格式说明:./udp_server port

static void usage(string proc)//使用手册
{
    std::cout << "Usage:\n\t" << proc << "port\n" <<std::endl;


}

int main(int argc,char* argv[]) //获取到参数

{
    if(argc != 2) //若输入参数不是两个的话,就弹出使用手册 
    {
        usage(argv[0]);
        exit(-1);
    }
    uint16_t port = atoi(argv[1]); //获取到端口直接进行构造后面

    std::unique_ptr<UdpServer> ptr(new UdpServer(port));

    ptr->InitServer();
    ptr->Start();
    return 0;
}

2.4客户端Client.cc

在客户端中我们首先需要知道主函数的服务端的ip和端口,也就是我们需要从输入的参数来知道服务端是谁?之后由用户输入消息后发送给服务器端并输出。


// 执行格式:./udp_client ip serverport
static void usage(std::string proc) //使用手册
    {
    std::cout << "Usage:\n\t" << proc << "port\n" <<std::endl;
    }
int main(int argc,char* argv[])
{

    if(argc != 3)		//如果输入参数个数不是3个就弹出使用手册
    {
        usage(argv[0]);
        exit(-1);
    }

    //从主函数获取到了服务端的ip和端口
    std::string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);


    
    int sock = socket(AF_INET,SOCK_DGRAM,0);  //创建套接字
    if(sock < 0)
    {
        std::cerr << "create socket errno" <<std::endl;
        exit(-1);
    }

    //明确server是谁
    struct sockaddr_in server;
    memset(&server,0,sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    server.sin_addr.s_addr = inet_addr(serverip.c_str());
    //这里client一定需要绑定bind 不过由os来帮我们做,因为OS需要随机分配端口,防止冲突
   //用户输入
    while(true)
    {
        std::string message;
        std::cout<< "please Enter#  ";
        std::cin >> message;

        sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));

        //接收消息
        char buffer[1024];
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp); 
        int n = recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "server echo#  " << buffer << std::endl; 
        }
    }
    
    return 0;
}

最后执行后我们便可以看出结果: 说明执行成功!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/904225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java-类与对象(下)

访问限定符 类可以将数据以及封装数据的方法结合在一起&#xff0c;更符合人类对事物的认知&#xff0c;而访问权限用来控制方法或者字段能否直接在类外使用。 这里目前主要掌握private&#xff0c;default和public public&#xff1a;可以理解为一个人的外貌特征&#xff0c;…

金狮金狮鹏保宝点盾云等加密视频翻录录屏教程

这款软件采用独家方法特殊方式打开录屏工具&#xff0c;理论上通杀所有的检测进程的加密视频播放器的检测&#xff0c;不被检测。 使用方法很简单&#xff1a; 点击 打开录屏工具&#xff0c;就可以以特殊方式加载录屏工具。 然后就可以开始录屏了。 下载&#xff1a;spjm.lan…

在线HmacSHA1加密工具--在线获取哈希值又称摘要

具体请前往&#xff1a; 在线计算HmacSha1工具

0102垂直分片-jdbc-shardingsphere

1 前言 垂直分片分为垂直分库于垂直分表&#xff0c;但是实际应用中数据库表会根据业务需要提前做好规划&#xff0c;垂直分表的情况很少使用。但是随着业务增长&#xff0c;某些重点业务数据库压力增大&#xff0c;可能考虑需要考虑垂直分库的情况。 垂直分库&#xff0c;简…

22-组件通信

一. 什么是组件通信 组件通信&#xff0c;就是指 组件与组件 之间的数据传递 1. 组件的数据是独立的&#xff0c;无法直接访问其他组件的数据。 2. 想用其他组件的数据 -> 组件通信 二. 不同的组件关系 和 组件通信方案分类 组件关系分类: 1. 父子关系 2. 非父子关系 组件…

【Unity】制作一个简单的菜单栏页面并实现其功能

这是一个简单的菜单页面制作&#xff0c;接下来我们将制作一个完整的菜单页面&#xff0c;并且通过一定的代码去实现它对应的效果。这个主要的功能就是我们在游戏中如果想暂停一下或者重新开始&#xff0c;那么就要用到我们这个功能。接下来我们将实现在游戏中按ESC退出键可以调…

第6步---MySQL的控制流语句和窗口函数

第6步---MySQL的控制流语句和窗口函数 1.IF关键字 -- 控制流语句 SELECT IF(5>3,大于,小于);-- 会单独生成一列的 SELECT *,IF(score >90 , 优秀, 一般) 等级 FROM stu_score;-- IFNULL(expr1,expr2) SELECT id,name ,IFNULL(salary,0),dept_id FROM emp4;-- ISNULL() …

【C语言学习】数组

定义数组 <类型>变量名称[元素变量] 元素数量必须是整数 int a[10]; double b[20]; scanf("%d",&a[i]);//输入数组数组 数组是一种容器&#xff0c;特点为其中所有元素具有相同的数据类型&#xff1b; 一旦创建&#xff0c;不能改变大小&#xff1b; 数…

【C语言】什么是结构体内存对齐?结构体的大小怎么计算?

目录 1.结构体内存对齐 对偏移量的理解&#xff1a;​ 2.结构体的大小计算 2.1结构体中只有普通的数据类型的大小计算 2.2 结构体中有嵌套的结构体的大小计算 3.修改默认对齐数 4.为什么存在内存对齐? 这篇文章主要介绍结构体内存对齐和如何计算大小。 在学习结构体内存…

个性化定制界面 VS 极简版原装界面:你更喜欢哪一个?为什么?

文章目录 每日一句正能量前言自己的喜好使用这种界面的原因这种界面对你的影响后记 每日一句正能量 不管昨天、今天、明天&#xff0c;能豁然开朗就是最美好的一天。 前言 个性化定制界面和极简版原装界面&#xff0c;哪一个你用起来更加顺手呢&#xff0c;相比之下你更喜欢哪一…

空洞卷积网络实现

代码&#xff1a; import torch.nn as nn import numpy as npfrom matplotlib import pyplot as plt import time #from utils import get_accur,load_data,trainimport torchvision from torchvision import transforms from torch.utils.data import DataLoader import torch…

硬编码基础一(经典定长指令,寄存器相关)

硬编码基础一&#xff08;定长指令&#xff09; push/pop 通用寄存器 50~57是push8个32位通用寄存器 58~5f是pop8个32位通用寄存器 inc/dec 通用寄存器 40~47是inc8个32位通用寄存器 47~4f是dec8个32位通用寄存器 八位通用寄存器的立即数赋值 b0~b3 {立即数} 是低八位(…

蓝蓝设计-专业UI设计公司-界面设计作品

数慧时空&#xff08;全称&#xff1a;北京数慧时空信息技术有限公司&#xff09;是以空间信息技术为核心、国内领先的国土资源行业解决方案提供商&#xff0c;具有业务咨询、自主软件研发、数据加工和信息服务的全业务综合服务能力&#xff0c;是国土资源部最重要的信息化合作…

AD19基础应用技巧(位号的调整)

在进行元件装配时&#xff0c;需要输出相应的装配文件&#xff0c;而元件的位号图可以方便比对元件装配。隐藏其他层&#xff0c;只显示Overlay和Solder层可以更方便地进行位号调整。 一般来说&#xff0c;位号大都放到相应元件旁边&#xff0c;其调整应遵循以下原则&#xff…

LLM低成本微调方法

LLM日益流行&#xff0c;已经渗透到各个领域&#xff0c;比如生物医学&#xff0c;但是模型的规模导致微调LLM对普通用户不够友好&#xff0c;因此&#xff0c;我们需要借助一些低成本方法&#xff0c;通过更新少量参数也达到与LLM全参数更新一样的效果。这里介绍三种主流方法&…

JVM——垃圾回收(垃圾回收算法+分代垃圾回收+垃圾回收器)

1.如何判断对象可以回收 1.1引用计数法 只要一个对象被其他对象所引用&#xff0c;就要让该对象的技术加1&#xff0c;某个对象不再引用其&#xff0c;则让它计数减1。当计数变为0时就可以作为垃圾被回收。 有一个弊端叫做循环引用&#xff0c;两个的引用计数都是1&#xff…

npm和node版本升级教程

cmd中查看本地安装的node版本 node -v //查询node的位置 where node2.官网下载所需要的node版本&#xff0c;安装在刚查出来的文件夹下&#xff0c;即覆盖掉原来的版本 3.查看node版本是否已经更新 4.查看npm版本是否和node版本相匹配 cnpm install -g npm

超实用的两款截图工具(FastStone Capture 和 Snipaste)

文章目录 一、概述1&#xff09;FastStone Capture2&#xff09;Snipaste 二、FastStone Capture 和 Snipaste 截图软件安装 一、概述 "FastStone Capture" 和 "Snipaste" 都是计算机上常用的截图工具&#xff0c;用于捕捉屏幕截图、编辑图像以及进行屏幕…

湘潭大学 湘大 XTU OJ 1217 A+B VII 题解(非常详细)

链接 1217 题目 题目描述 小明非常高兴你能帮他处理那些罗马数字&#xff0c;他想学着自己写罗马数字&#xff0c;但是他不知道自己到底写对了没有。 请你帮他写个程序&#xff0c;能正确地将10进制数字转换成罗马数字&#xff0c;以便他能进行核对。 罗马数字是使用字母组…

Docker运行Nacos容器,过一会就报错`UnsatisfiedDependencyException`

Docker运行Nacos容器&#xff0c;过一会就报错UnsatisfiedDependencyException 问题背景&#xff1a; 最近要上线一个项目&#xff0c;由于要使用Nacos作为服务注册中心&#xff0c;为了方便&#xff0c;我就打算直接使用Docker部署Nacos&#xff0c;没想到Nacos启动没一会就嗝…