Linux_实现UDP网络通信

news2024/11/24 5:28:45

目录

1、实现服务器的逻辑

1.1 socket

1.2 bind

1.3 recvfrom

1.4 sendto

1.5 服务器代码

2、实现客户端的逻辑 

2.1 客户端代码

3、实现通信

结语 


前言:

        在Linux下,实现传输层协议为UDP的套接字进行网络通信,网络层协议为IPv4,需要用到的接口有以下4个:socket、bind、recvfrom、sendto。具体实现方法:在云服务器上创建一个服务器进程和一个客户端进程,让客户端向服务器发送消息,并且服务器收到消息后可以反馈给对方。

        示意图如下:

1、实现服务器的逻辑

        按照以下函数的调用顺序,即可实现服务器方的UDP通信。

1.1 socket

        首先明确使用IPv4协议和UDP协议后,先调用接口socket,让其返回一个网络文件描述符给到我们,socket函数介绍如下:

#include <sys/types.h>          
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
//domain表示网络协议族,AF_INET为IPv4,AF_INET6为IPv6
//type表示传输层协议,SOCK_STREAM为TCP,SOCK_DGRAM为UDP
//protocol表示指定特定的协议,一般前两个参数的协议足矣,这里填0即可

//调用成功返回一个类型文件描述符的网络描述符,失败返回-1

1.2 bind

        定义一个struct sockaddr_in类型的变量,该变量的作用是为调用bind接口做准备,该变量里面有3个信息需要填写,分别是:1、传输层协议,2、为该进程设置的端口号,3、该主机的IP地址。其中,端口号和IP地址需要对其转换成大端字节序,因为网络规定传输的数据采用大端字节序传输,这里介绍两个接口可以帮助我们直接将端口号和IP转换成大端字节序,接口介绍如下:

#include <arpa/inet.h>

uint16_t htons(uint16_t hostshort);//常用来转换端口号
//将主机字节序转换成网络字节序并返回

#include <arpa/inet.h>  
  
unsigned long inet_addr(const char *cp);//常用来转换IP地址
//如果cp指向的是IP地址的字符串形式,那么会将其转换为网络字节序的IP地址
//并且以无符号的长整型返回

        待struct sockaddr_in类型的变量的字段填写完毕后,下一步就是进行绑定操作,绑定的目的是将socket返回的网络描述符与struct sockaddr_in类型的变量进行绑定,即可以通过网络描述符来找到对应的ip地址以及端口号,简单来说,通过网络描述符就能找到对应主机的对应进程


        接着就是调用bind接口进行绑定了,bind接口介绍如下:

int bind(int socket, const struct sockaddr *address,
              socklen_t address_len);
//socket表示要绑定的网络描述符
//address表示指向struct sockaddr类型的变量
//address_len表示address指向变量的大小

//调用成功返回0,失败返回-1

1.3 recvfrom

         recvfrom接口有点类似文件操作中的read接口,都带有接收的意思。recvfrom接口用于从网络描述符中读取对方主机发送的数据,并且还可以将对方主机的地址信息(IP和端口号)给记录下来,该接口的介绍如下:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,  
                 struct sockaddr *src_addr, socklen_t *addrlen);
//sockfd表示读取的网络描述符
//buf表示存放读取数据的目标缓冲区
//len表示期望读取内容的大小
//flag表示设置该函数的模式,比如阻塞或非阻塞,通常设为0表阻塞
//src_addr是个输出型参数,用于保存发送方的地址信息
//addrlen表示src_addr指向变量的大小

//成功返回接收的字节数,若sockfd关闭返回0,失败返回-1

1.4 sendto

        如果说recvfrom接口类似read接口,那么sendto就好比write接口,他能够往网络文件描述符内写入数据,即发送方就是调用sendto接口向接收方发送数据,sendto和recvfrom相互搭配实现网络通信。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表示要发送数据的长度
//flag表示设置该函数的模式,比如阻塞或非阻塞,通常设为0表阻塞
//dest_addr指向的结构体里包含接收方的IP和端口号,依靠他们才能找到接收方
//addrlen表示dest_addr指向结构体的大小

1.5 服务器代码

        将服务器封装成一个类,并把服务器的端口号、ip地址、网络描述符作为该类的成员变量,这样就可以对上述功能逻辑进行分层了,服务器类代码如下:

#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdio>
#include <unistd.h>
using namespace std;

uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";
const int size = 1024;

class UdpServer{
public:
    UdpServer(const uint16_t &port = defaultport, 
    const std::string &ip = defaultip):sockfd_(0), port_(port), ip_(ip)
    {}
    void Init()
    {
        // 1. 创建udp socket
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); 
        printf("socket create success, sockfd: %d\n", sockfd_);
        // 2. bind socket
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_); 
        local.sin_addr.s_addr = inet_addr(ip_.c_str()); 

        bind(sockfd_, (const struct sockaddr *)&local, sizeof(local));
        printf("bind success, errno: %d, \
        err string: %s\n", errno, strerror(errno));
    }
    void Run() // 对代码进行分层
    {
        char inbuffer[size];
        while(true)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            //服务器先接收消息
            ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1,
             0, (struct sockaddr*)&client, &len);
            cout<<"客户端说:"<<inbuffer<<endl;
            inbuffer[n] = 0;
            std::string info = inbuffer;
            std::string echo_string = "服务器的回答:"+info;
            //再反馈消息
            sendto(sockfd_, echo_string.c_str(), echo_string.size()
            , 0, (const sockaddr*)&client, len);
        }
    }
    ~UdpServer()
    {
        if(sockfd_>0) close(sockfd_);//关闭描述符
    }
private:
    int sockfd_;     // 网路文件描述符
    std::string ip_; // ip地址
    uint16_t port_;  // 服务器端口号
};

         该服务器的逻辑是先接收客户端发送的消息,然后利用recvfrom函数保存客户端的地址信息,再使用sendto函数对客户端进行信息的反馈。

2、实现客户端的逻辑 

        客户端逻辑和服务器逻辑几乎一样,第一步必须调用socket创建网络描述符,但是第二步客户端不需要进行bind绑定,因为服务器之所以需要绑定是因为服务器必须手动自定义一个端口号,目的就是要让该端口号可见,以便让客户端知道该端口号,这样客户端才能通过该端口号定位服务器。而客户端不需要自定义端口号,因为客户端的主要任务是给服务器发送信息,这个过程服务器是不需要知道客户端的端口号也可以接收客户端的信息,因此客户端的端口号只需要保证其唯一性即可,即交给操作系统来生成,当首次发送数据的时候操作系统就会为客户端生成端口号。

2.1 客户端代码

        客户端代码如下:

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

void Usage(std::string proc)
{
    std::cout << "\n\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

// ./udpclient serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(0);
    }
    //从命令行参数拿到ip地址和端口号
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    struct sockaddr_in server;
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport); //转换网络字节序
    server.sin_addr.s_addr = inet_addr(serverip.c_str());//转换网络字节序
    // 1. socket拿到网络描述符
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);


    socklen_t len = sizeof(server);
    string message;
    char buffer[1024];
    while (true)
    {
        cout << "Please Enter@ ";
        getline(cin, message);
        //2. 向服务器发送信息
        sendto(sockfd, message.c_str(), message.size(), 
        0, (struct sockaddr *)&server, len);
        
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        //3. 打印来自服务器的信息
        recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);
        cout << buffer << endl;
        
    }

    close(sockfd);//关闭文件描述符
    return 0;
}

3、实现通信

        实现通信的前提是让服务器以进程的形式跑起来,然后再让客户端也以进程的形式跑起来,因为网络通信的本质就是进程间通信,而上述代码中客户端本身就是在main函数中执行的,所以此时客户端可以直接运行,但是服务器还只是个类,因此现在只需要用服务器类实现一个main函数,即可完成两个进程的运行。

        服务器进程代码如下: 

#include "UDPser.hpp"
#include <memory>
#include <cstdio>


void Usage(std::string proc)
{
    std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}

// ./udpserver port
int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    //从命令行参数拿到端口号
    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<UdpServer> svr(new UdpServer(port));

    svr->Init();
    svr->Run();

    return 0;
}

        运行结果:

        从结果可以看到目前可以正常的进行客户端与服务器之间的通信。

结语 

        以上就是关于实现UDP网络通信的讲解,实现UDP的核心在于对套接字的理解以及相关接口的逻辑使用,其实只需要记住只要涉及到网络通信,那么socket和bind函数是必须在最开始就调用的。

        最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!! 

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

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

相关文章

Spring Boot集成Spire.doc实现对word的操作

1.什么是spire.doc? Spire.Doc for Java 是一款专业的 Java Word 组件&#xff0c;开发人员使用它可以轻松地将 Word 文档创建、读取、编辑、转换和打印等功能集成到自己的 Java 应用程序中。作为一款完全独立的组件&#xff0c;Spire.Doc for Java 的运行环境无需安装 Micro…

JMeter:BeanShell到JSR223迁移中的注意事项

前言 在之前的文章JMeter&#xff1a;BeanShell向JSR223迁移过程遭遇的java标准库不可用问题-如何切换JDK版本中引用了一段使用BeanShell对入参进行加密的脚本&#xff0c;迁移到JSR223&#xff0c;虽然更换JDK后编译通过&#xff0c;看似也可以执行了&#xff0c;但是其实那段…

AI绘画入门实践 | Midjourney:画面权重控制

在 Midjourney 中&#xff0c;使用两个连续的英文冒号::来进行分割与权重控制。 作为分隔符使用 在提示词中添加双冒号::表示让 MJ 将部分提示词单独考虑 2d illustration, french fries, hot dog --v 6 2d illustration, french fries, hot:: dog --v 6 作为权重控制使用 在双…

google 浏览器插件开发简单学习案例:TodoList;打包成crx离线包

参考&#xff1a; google插件支持&#xff1a; https://blog.csdn.net/weixin_42357472/article/details/140412993 这里是把前面做的TodoList做成google插件&#xff0c;具体网页可以参考下面链接 TodoList网页&#xff1a; https://blog.csdn.net/weixin_42357472/article/de…

docker基础镜像

一、配置 docker 本地源 [docker-ce-stable] nameDocker CE Stable baseurlhttp://10.35.186.181/docker-ce-stable/ enabled1 gpgcheck0 配置阿里云Docker Yum源 yum install -y yum-utils device-mapper-persistent-data lvm2 git yum-config-manager --add-repo http://mirr…

简单修改,让UE4/5着色器编译速度变快

简单修改&#xff0c;让UE4/5着色器编译速度变快 目录 简单修改&#xff0c;让UE4/5着色器编译速度变快 一、问题描述 二、解决方法 &#xff08;一&#xff09;硬件升级 &#xff08;二&#xff09;调整相关设置和提升优先级 1.调整相关设置 &#xff08;1&#xff09…

【Android】碎片的初识

之前我们学习的是一个活动作为一个页面&#xff0c;有了平板之后&#xff0c;页面如果像手机一样设计就会浪费很多的空间&#xff0c;会有很多的空白区域&#xff0c;为了使屏幕充分利用&#xff0c;引入了碎片这样一个概念。 碎片&#xff08;Fragment&#xff09;&#xff1…

pikachu之sql lnjet 字符型注入

先测试一下闭合 注释符号&#xff1a;-- 注释符号可以忽略其后的内容&#xff0c;使得后续的原始查询内容不会影响我们注入的SQL代码。 条件测试&#xff1a;通过and 11和and 12分别测试真假条件&#xff0c;可以判断输入是否成功闭合&#xff0c;并且可以检测注入是否成功。 …

构造+位运算,CF 1901C - Add, Divide and Floor

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1901C - Add, Divide and Floor 二、解题报告 1、思路分析 我们假设将原数组排序&#xff0c;那么每次操作不会改变数组单调性 当 最大值 调整等于 最小值时 所有数都相等&#xff0c;因为单调性不变&…

VS2022下安装和配置OpenCV环境参数+QT开发环境搭建(1)

1.工具准备 VS2022,OpenCV4.5.5版本&#xff0c;QT5.12.12 VisualStudio最新版直接官网下载&#xff0c;根据需要进行下载&#xff0c;我下载的免费社区版本。日常开发完全够用。 qt官网下载5.12版本。 OpenCVReleases - OpenCV 选择Windows版本下载并解压到本地磁盘&#xff0…

操作系统——笔记(1)

操作系统是管理计算机硬件资源&#xff0c;控制其他程序运行并为用户提供交互操作界面的系统软件的集合&#xff0c;控制和管理着整个计算机系统的硬件和软件资源&#xff0c;是最基本的系统软件。 常见的操作系统&#xff1a;ios、windows、Linux。 计算机系统的结构层次&am…

【SpringCloud】微服务远程调用OpenFeign

工作原理流程图 上代码 common中添加依赖&#xff1a; <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency><groupId>org.spri…

Android APP 音视频(01)MediaCodec解码H264码流

说明&#xff1a; 此MediaCodec解码H264实操主要针对Android12.0系统。通过读取sd卡上的H264码流Me获取视频数据&#xff0c;将数据通过mediacodec解码输出到surfaceview上。 1 H264码流和MediaCodec解码简介 1.1 H264码流简介 H.264&#xff0c;也被称为MPEG-4 AVC&#xff…

用51单片机或者stm32能否开发机器人呢?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;能的。但是由于单片机和st…

记录安装android studio踩的坑 win7系统

最近在一台新电脑上安装android studio,报了很多错误&#xff0c;也是费了大劲才解决&#xff0c;发出来大家一起避免一些问题&#xff0c;找到解决方法。 安装时一定要先安装jdk&#xff0c;cmd命令行用java -version查当前的版本&#xff0c;没有的话&#xff0c;先安装jdk,g…

ARP欺骗——华为ensp

首先&#xff0c;搭建好网络拓扑。网络设备包含客户端Client1和服务端Server1&#xff0c;交换机 以及 云。 图中的 Client和Server 配置IP地址&#xff0c;要和 vm8 在相同的网段。故设置客户端ip为192.168.11.10 &#xff0c;服务端ip为&#xff1a;192.168.11.20&#xff0…

MySQL补充性文件

数据库专属单词 authentication #身份验证 delimiter #分隔符 character #字符集 collate #整理。 指定字符集的排序规则 unicode #统一码 flush #刷新 privileges #特权 string #串 set #设置 use #使用 zerofill #修饰符。0可以填补输出的值 unsigned #修饰符。无符…

STM32--HAL库--定时器篇

一&#xff1a;如何配置定时器 打开对应工程串口配置好的工程&#xff08;上一篇博客&#xff09;做如下配置&#xff1a; 定时器的中断溢出时间计算公式是&#xff1a; 由图得T100*1000/100MHz 注&#xff1a;100MHz100000000 所以溢出时间等于1ms 关于上图4的自动重装…

Robot Operating System——初探动态配置Parameters

大纲 同步模式Node内使用declare_parameter方法声明Parameters创建Parameter同步访问客户端跨Node修改Parameters跨Node查询Parameters完整代码运行结果 异步模式创建Node&#xff0c;设置Parameters创建Parameter异步访问客户端异步设置&#xff0c;同步等待异步查询&#xff…

VMware三种网络模式---巨细

文章目录 目录 ‘一.网络模式概述 二.桥接模式 二.NAT模式 三.仅主机模式 四.案例演示 防火墙配置&#xff1a; 虚拟电脑配置 前言 本文主要介绍VMware的三种网络模式 ‘一.网络模式概述 VMware中分为三种网络模式&#xff1a; 桥接模式&#xff1a;默认与宿主机VMnet0绑…