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号进程领养,所以不用担心资源泄露,但是这样很明显很麻烦,还不如用线程,且消耗还比进程小。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud Stream实践

概述 不同中间件&#xff0c;有各自的使用方法&#xff0c;代码也不一样。 可以使用Spring Cloud Stream解耦&#xff0c;切换中间件时&#xff0c;不需要修改代码。实现方式为使用绑定层&#xff0c;绑定层对生产者和消费者提供统一的编码方式&#xff0c;需要连接不同的中间…

拼图游游戏代码

一.创建新项目 二.插入图片 三.游戏的主界面 1.代码 package com.itheima.ui;import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.Random;import javax.swing…

贪吃蛇代码

一.准备 1.新建项目 2.放进照片 3.创建两个包放置图片类和入口类 二&#xff0c;游戏界面 package com.snake.view;import java.awt.Color; import java.awt.EventQueue; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; i…

【Java程序员面试专栏 算法训练篇】二叉树高频面试算法题

一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是二叉树相关汇总的高频题目 遍历二叉树 遍历二叉树,分为递归和迭代两种方式,递归类似于DFS,迭代类似于BFS,【算法训练-二叉树 一】【遍历二叉树】前序遍历、中序遍历、后续遍…

【高级程序设计】Week2-4Week3-1 JavaScript

一、Javascript 1. What is JS 定义A scripting language used for client-side web development.作用 an implementation of the ECMAScript standard defines the syntax/characteristics of the language and a basic set of commonly used objects such as Number, Date …

【Java 进阶篇】Ajax 实现——原生JS方式

大家好&#xff0c;欢迎来到这篇关于原生 JavaScript 中使用 Ajax 实现的博客&#xff01;在前端开发中&#xff0c;我们经常需要与服务器进行数据交互&#xff0c;而 Ajax&#xff08;Asynchronous JavaScript and XML&#xff09;是一种用于创建异步请求的技术&#xff0c;它…

多态语法详解

多态语法详解 一&#xff1a;概念1&#xff1a;多态实现条件 二:重写&#xff1a;三&#xff1a;向上转型和向下转型1:向上转型&#xff1a;1&#xff1a;直接赋值&#xff1a;2&#xff1a;方法传参3&#xff1a;返回值 2:向下转型 一&#xff1a;概念 1&#xff1a;同一个引…

Java值传递和引用传递

在Java中&#xff0c;有值传递&#xff08;Pass-by-Value&#xff09;和引用传递&#xff08;Pass-by-Reference&#xff09;两种参数传递方式。 值传递&#xff08;Pass-by-Value&#xff09;&#xff1a;当使用值传递方式时&#xff0c;方法将参数的副本传递给调用方法。这意…

Go 语言中的map和内存泄漏

map在内存中总是会增长&#xff1b;它不会收缩。因此&#xff0c;如果map导致了一些内存问题&#xff0c;你可以尝试不同的选项&#xff0c;比如强制 Go 重新创建map或使用指针。 在 Go 中使用map时&#xff0c;我们需要了解map增长和收缩的一些重要特性。让我们深入探讨这一点…

大型 APP 的性能优化思路

做客户端开发都基本都做过性能优化&#xff0c;比如提升自己所负责的业务的速度或流畅性&#xff0c;优化内存占用等等。但是大部分开发者所做的性能优化可能都是针对中小型 APP 的&#xff0c;大型 APP 的性能优化经验并不会太多&#xff0c;毕竟大型 APP 就只有那么几个&…

ESP32-BLE基础知识

一、存储模式 两种存储模式&#xff1a; 大端存储&#xff1a;低地址存高字节&#xff0c;如将0x1234存成[0x12,0x34]。小端存储&#xff1a;低地址存低字节&#xff0c;如将0x1234存成[0x34,0x12]。 一般来说&#xff0c;我们看到的一些字符串形式的数字都是大端存储形式&a…

【VRTK】【VR开发】【Unity】7-配置交互能力和向量追踪

【前情提要】 目前为止,我们虽然设定了手模型和动画,还能够正确根据输入触发动作,不过还未能与任何物体互动。要互动,需要给手部设定相应的Interactor能力。 【配置Interactor的抓取功能】 在Hierarchy中选中[VRTK_CAMERA_RIGS_SETUP] ➤ Camera Rigs, Tracked Alias ➤ …

MobaXterm配置ssh端口转发(tensorboard使用)

背景&#xff1a; 我有一台本地Windows电脑&#xff0c;上面安装了MobaXterm软件。 MobaXterm通过ssh连接了一台服务器&#xff08;默认是通过22端口连&#xff0c;我这里配了一下&#xff0c;要填别的&#xff09; 现在服务器在跑模型&#xff0c;其6006端口是tensorboard端口…

8、创建第一个鸿蒙页面并实现页面跳转

一、创建页面 1、新建页面 在项目的"pages"目录上右键&#xff0c;选择”新建“——”page" 2、录入页面的名称 在“Page name”中输入页面的名称&#xff0c;并点击“Finish”完成创建 3、以下为创建的新页面 2、注册页面 新建的页面会自动在“resources”…

ArkTS - HarmonyOS服务卡片(创建)

可以参考官网文档 其中我们在已有的文件中File > New > Service Widget创建你想要的小卡片 本文章发布时目前可使用的模板就三种 有卡片后的new 最终效果

pnpm : 无法加载文件 E:\Soft\PromSoft\nodejs\node_global\pnpm.ps1,

pnpm : 无法加载文件 E:\Soft\PromSoft\nodejs\node_global\pnpm.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中 的 about_Execution_Policies。 所在位置 行:1 字符: 1pnpm -v~~~~ CategoryI…

Kotlin学习之函数

原文链接 Understanding Kotlin Functions 函数对于编程语言来说是极其重要的一个组成部分&#xff0c;函数可以视为是程序的执行&#xff0c;是真正活的代码&#xff0c;为啥呢&#xff1f;因为运行的时候你必须要执行一个函数&#xff0c;一般从主函数入口&#xff0c;开始一…

《微信小程序开发从入门到实战》学习二十二

3.3 开发创建投票页面 3.3.10 使用switch开关组件 用switch开关组件增加一个设置是否匿名投票的功能。 switch常用属性如下&#xff1a; checked 开还是关&#xff0c;默认false关 disabled 是否禁用&#xff0c;默认false不禁用&#xff0…

应试教育导致学生迷信标准答案惯性导致思维僵化-移动机器人

移动机器人课程群实践创新的困境与突围 一、引言 随着科技的快速发展&#xff0c;工程教育变得越来越重要。然而&#xff0c;传统的应试教育模式往往侧重于理论知识的传授&#xff0c;忽视了学生的实践能力和创新精神的培养。这在移动机器人课程群的教学中表现得尤为明显。本文…

win10手机投屏到电脑的操作方法

工具/原料&#xff1a; 系统版本&#xff1a;iOS 15.3,HarmonyOS 2.0.0&#xff0c;windows10系统 品牌型号&#xff1a;iPhone 13,HUAWEI Mate 40 Pro&#xff0c;联想小新air14 方法/步骤&#xff1a;方法一&#xff1a;安卓手机使用无线投屏功能投屏到win10电脑 1、保持手…