UDP网络套接字编程

news/2024/5/18 13:38:50 标签: 网络, udp, 网络协议

先来说说数据在网络上的传输过程吧,我们知道系统其实终究是根据冯诺依曼来构成的,而网络数据是怎么发的呢?

其实很简单,网络有五层。如下:

如上图,我们知道的是,每层对应的操作系统中的那些地方,有些可能说是网络有七层,其实和这个五层一样的。下面我们说说数据是怎么运输的在网络中,如下图:

如上图,其实数据在网络中是自顶向下,然后在通过以太网的网线传输到另一个主机上,在自底向上,就可以收到了,前提是在同一个局域网中,如果不在一个局域网,肯定会经过路由器的,这里就不详细说了,主要说说我们的udp协议。

我们知道了网络的五层,那么每层其实都与对应的协议等。udp协议对应在传输层(运输层)。那么我们来看看如何用udp协议实现套接字编程吧。先来看看代码:

#include <iostream>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <unordered_set>
#include <unordered_map>
using namespace std;
#define NUM 1024
int main(int argc, char *argv[])
{
    unordered_map<uint16_t,sockaddr_in> usdate;
    // 创建套接字
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        cout << "sock enrro" << endl;
        exit(1);
    }
    // 绑定
    sockaddr_in se;
    memset(&se, 0, sizeof(se));
    se.sin_family = AF_INET;
    se.sin_port = htons(atoi(argv[2]));
    se.sin_addr.s_addr = inet_addr(argv[1]);
    int ret = bind(sock, (sockaddr *)&se, sizeof(se));
    if (ret < 0)
    {
        cout << "bind enrro" << endl;
        exit(2);
    }
    // 服务端    1.0版本
    // 可以开始读取
    // sockaddr_in reader;
    // socklen_t size = sizeof(reader);
    // char buffer[NUM];
    // while (true)
    // {
    //     ssize_t r = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&reader, &size);
    //     buffer[r] = '\0';
    //     if (r > 0)
    //     {
    //         cout << buffer << endl;
    //     }
    //     else
    //         break;
    //     sendto(sock, buffer, sizeof(buffer), 0, (sockaddr *)&reader, size);
    //     memset(buffer, 0, sizeof(buffer));
    // }
    // close(sock);
    // 服务器 2.0版本 实现群聊
    char buffer[NUM];
    memset(buffer, 0, NUM);
    sockaddr_in reader;
    socklen_t size = sizeof(reader);
    while (true)
    {
        ssize_t s = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&reader, &size);
        usdate.insert(make_pair(reader.sin_port, reader));
        cout << "插入成功" << endl;
        cout << ntohs(reader.sin_port) <<" "<< inet_ntoa(reader.sin_addr)<< '#' << " "
             << ":" << buffer << endl;
        if (s > 0)
        {
            for (const auto &e : usdate)
                sendto(sock, buffer, sizeof(buffer), 0, (sockaddr *)&(e.second), sizeof(e.second));
            memset(buffer, 0, NUM);
        }
        else
            break;
    }
    close(sock);
    return 0;
}
#include <iostream>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
using namespace std;
#define NUM 1024
void *reads(void *args)
{
    // 线程分离
    pthread_detach(pthread_self());
    char buffer[NUM];
    // 清空buffer
    memset(buffer, 0, NUM);
    int *sc = static_cast<int *>(args);
    sockaddr_in reader;
    socklen_t len = sizeof(reader);
    while (true)
    {
        ssize_t s = recvfrom(*sc, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&reader, &len);
        if (s > 0)
        {
            cout << buffer << endl;
            memset(buffer, 0, NUM);
        }
        else
            break;
    }
    return nullptr;
}
int main(int argc, char *argv[])
{
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        cout << "sock enrro" << endl;
        exit(1);
    }

    char buffer[NUM];
    memset(buffer, 0, NUM);
    pthread_t tid;
    pthread_create(&tid, nullptr, reads, &sock);

    sockaddr_in clinet;
    memset(&clinet, 0, sizeof(clinet));
    clinet.sin_family = AF_INET;
    clinet.sin_addr.s_addr = inet_addr(argv[1]);
    clinet.sin_port = htons(atoi(argv[2]));
    while (true)
    {
        cout << "你要输入" << endl;
        cin >> buffer;
        ssize_t s = sendto(sock, buffer, sizeof(buffer), 0, (sockaddr *)&(clinet), sizeof(clinet));
        if (s > 0)
            memset(buffer, 0, NUM);
        else
            break;
    }
    close(sock);
    return 0;
}

这是我的服务端和客户端的代码,分了单人聊天和多人聊天。下面就讲解一下吧。

什么是端口号:主机中能表示一个唯一的进程的编号

什么是ip地址:其实ip地址是网络层对应的主机地址。

什么是mac地址:这个网卡的地址,一般出厂的时候就会确定,且不能修改

什么是套接字:IP+端口号

我们首先可以根据套接字找到网络中唯一的一个主机上进程,此处不考虑ip地址重复问题。假设ip地址不重复。所以我们要进行udp套接字编程,首先要创建套接字。也就是我上图代码中的socket这个函数,然后绑定地址和端口,这个就可以用我们main函数中的参数了。创建完套接字和绑定完成以后,我们就可以通信了,我们用的这些函数其实就是系统调用,是udp的一些函数暴露给用户层的系统调用。

大概知道了怎么用udp编程,那么此时有些伙伴可能有些疑问了。我们在系统层面上pid也可以表示进程的唯一性,为啥不用PID表示端口号呢?其实也很简单,原因就是网络层是这么表示的,就好比你的名字一样,在外面大家都叫你名字,回到家家里面的人都叫你小名,一样的道理。

然后就是有些人可能没有理解数据是怎么从下往上,从上往下的。其实很好理解,因我们在写代码的时候,我们是属于用户层的。而我们传输层的系统调用接口,那么说明他这个数据肯定是要进内核的,而我们的另一个主机接收到信息后,我们用的打印函数,又是用户层的,所以就类似于一个轮回。所以这样就可以很好的理解了。

然后就是一些编码的注意事项了,在客户端我用了多线程来实现了读数据和写数据的解耦,这个其实在单人聊天中没什么影响,再多人聊天中就不可以了。假设单人聊天就要先发在读。因为再多人聊天中,我们预期是一个人发,多个人收,如果还是用这个代码的话,那么 如果开启服务端的时候,多个人你同时建立连接,那么此时如果有其中一个人发了信息,并且假设其他人都没有发信息,那么此时就会导致其他人卡在写的界面,因为他们没有写,所以没办法读信息。所以此时我们这里必须要实现成多线程,一个写,一个读,读写解耦。两个互不影响。建议使用线程,不用进程,且不说多进程可不可以实现这个功能,就算是实现了,那么此时它的消耗是很大的。(多进程也可以实现这个功能),如果用多进程,那么此时我们要考虑的是,如何回收这个子进程,肯定不可以阻塞等待,如果用waitpid且不是阻塞等待的话,那么此时我们要写成循环,要不断去检测子进程是否完成任务。或是可以在子进程中在frok,让孙子进程执行任务,子进程退出,此时就会形成孤儿进程,会被1号进程领养,所以不用担心资源泄露,但是这样很明显很麻烦,还不如用线程,且消耗还比进程小。

以上就是这篇文章的内容,希望大家支持,如果对你有用,希望支持一下!!!!


http://www.niftyadmin.cn/n/5198020.html

相关文章

前端食堂技术周刊第 105 期:TS 5.3 RC、Vite 5.0、W3C 新任 CEO、有害的 Pinia 模式、2024 更快的 Web

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;金桂普洱 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看下…

Motion Plan之搜素算法笔记

背景&#xff1a; 16-18年做过一阵子无人驾驶&#xff0c;那时候痴迷于移动规划&#xff1b;然而当时可学习的资料非常少&#xff0c;网上的论文也不算太多。基本就是Darpa的几十篇无人越野几次比赛的文章&#xff0c;基本没有成系统的文章和代码讲解实现。所以对移动规划的认…

震惊!这个网站几分钟就能制作出电子画册

一直以来&#xff0c;制作电子画册都是一项繁琐且耗时的任务&#xff0c;需要专业的设计技能和大量的时间。 但是现在&#xff0c;有了这个神奇的网站&#xff0c;FLBOOK在线制作电子杂志平台。一切都变得如此简单&#xff01;它不仅提供了丰富的模板和素材&#xff0c;还支持在…

AIGC 技术在淘淘秀场景的探索与实践

本文介绍了AIGC相关领域的爆发式增长&#xff0c;并探讨了淘宝秀秀(AI买家秀)的设计思路和技术方案。文章涵盖了图像生成、仿真形象生成和换背景方案&#xff0c;以及模型流程串联等关键技术。 文章还介绍了淘淘秀的使用流程和遇到的问题及处理方法。最后&#xff0c;文章展望…

数据结构与算法编程题5

从有序表中删除重复元素&#xff0c;使表中所有元素值均不相同。 #include <iostream> using namespace std;typedef int ElemType; #define Maxsize 100 #define OK 1 #define ERROR 0 typedef struct SqList {ElemType data[Maxsize];int length; }SqList;void Init_…

『亚马逊云科技产品测评』活动征文|构建生态农场家禽系统

『亚马逊云科技产品测评』活动征文&#xff5c;构建生态农场家禽系统 授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 前…

基于ResNet框架的CNN

数据准备 DATA_URL http://download.tensorflow.org/example_images/flower_photos.tgz 一、训练集和验证集的划分 #spile_data.pyimport os from shutil import copy import randomdef mkfile(file):if not os.path.exists(file):os.makedirs(file)file flower_data/flower…

Vue3 源码解读系列(十一)——插槽 slot

slot 插槽的实现实际上就是一种 延时渲染&#xff0c;把父组件中编写的插槽内容保存到一个对象上&#xff0c;并且把具体渲染 DOM 的代码用函数的方式封装&#xff0c;然后在子组件渲染的时候&#xff0c;根据插槽名在对象中找到对应的函数&#xff0c;然后执行这些函数做真正的…