【Linux后端服务器开发】UDP协议

目录

一、端口号

二、UDP报头格式

三、UDP的特点

四、UDP协议实现网络聊天群


一、端口号

端口号port标识了一个主机上进行通信的不同的应用程序。

  • 0 ~ 1023:系统端口号,HTTP、FTP、SSH等这些广为使用的应用层协议,它们的端口号都是固定的系统端口号(知名端口号)
  • 1024 ~ 65535:操作系统动态分配的端口号,客户端程序的端口号,可有操作系统分配或程序员分配(普通端口号)

知名端口号(Well-Know Port Number):

  • ftp服务器:21
  • ssh服务器:22
  • telnet服务器:23
  • http服务器:80
  • https服务器:443

查看知名端口号:cat /etc/services

netstat:一个用来查看网络状态的重要工具

语法:netstat -[选项]

  • n:拒绝显示别名,能显示数字的全部显示数字
  • l:仅列出有在 listen 状态的服务状态
  • p:显示建立相关连接的程序名
  • t:仅显示tcp相关选项
  • u:仅显示udp相关选项
  • a:显示所有选项,默认不显示 listen 相关

pidof:查看服务器的进程id(通过进程名查看进程id)

语法:pidof [进程名]

端口号   ----->   进程(唯一关系)

  • 一个端口号是否可以被多个进程bind?不可以,端口号指向进程必须唯一
  • 一个进程是否可以绑定多个端口号?可以,进程指向端口号不一定唯一

二、UDP报头格式

  • 16位UDP长度,表示整个数据报(UDP首部+UDP数据)的最大长度(64K)
  • 如果检验和出错,数据直接丢弃 
  • 定长报头,报头的本质是结构化数据

 

三、UDP的特点

UDP传输的过程类似于寄信:

  • 无连接:知道对端的IP和端口号就直接进行传输,不需要建立连接
  • 不可靠:没有确认机制,没有重传机制,如果因为网络故障导致数据段无法发送,UDP协议层也不会给应用层返回任何错误信息
  • 面向数据报:应用层交给UDP多长的报文,UDP原样发送,既不会拆分,也不会合并,不能够灵活的控制读写数据的次数和数量

        

UDP的缓冲区:

  • UDP没有真正意义上的发送缓冲区,调用sendto会直接交给内核,由内核将数据报传给网络层协议进行后续的传输处理
  • UDP具有接收缓冲区,但是这个接收缓冲区不能保证收到的UDP报文的顺序和发送UDP报文的顺序一致;如果缓冲区满了,再到达的UDP数据就会被丢弃

UDP的socket既能读,也能写,这就是全双工概念。

基于UDP的常用应用层协议:

  • NFS:网络文件系统
  • TFTP:简单的文件传输协议
  • DHCP:动态主机配置协议
  • BOOTP:启动协议(用于无盘设备启动)
  • DNS:域名解析协议

UDP协议的应用场景多为视频网站、直播平台等不担心数据包丢失的情况。

四、UDP协议实现网络聊天群

设计思路:

  • 服务器与客户端通过udp协议通信,即server和client都需要创建自己的socket套接字
  • server创建完socket套接字之后,需要将server自身的ip和port和套接字bind绑定client的套接字不需要显式bind绑定(由OS自动绑定)
  • server先运行,通过recvfrom()向数据的接收缓冲区循环读取数据
  • client在运行的时候指定server的ip和port,通过sendto()向服务器的接收缓冲区发送数据
  • User.h头文件,进行在线用户管理,通过ip和port形成用户信息结构体,可以进行在线用户的添加、删除、判断,并且在server接收到数据的时候,进行广播(对所有在线用户发送刚刚接收到的数据)
  • 所有在线用户信息通过一个静态全局变量的哈希表管理,key值是ip-port组成的字符串
  • client在运行的时候就会进行一次线程分离,分离出去的线程循环读取并打印client自身的接收缓冲区内的数据(来自广播的数据)

设计效果:当多个client连接server之后,只要是online登录了的用户,都可以向server发送数据的同时看到自己和其他登录用户发送的数据,这就是网络聊天群

User.h

#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <unordered_map>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

class User
{
public:
    User(const std::string& ip, const uint16_t& port)
        : _ip(ip), _port(port)
    {}

    std::string ip()
    {
        return _ip;
    }

    uint16_t port()
    {
        return _port;
    }

private:
    std::string _ip;
    uint16_t _port;
};

class OnlineUser
{
public:
    OnlineUser()
    {}

    void AddUser(const std::string& ip, const uint16_t& port)
    {
        std::string id = ip + "-" + std::to_string(port);
        _users.insert(std::make_pair(id, User(ip, port)));
    }

    void DelUser(const std::string& ip, const uint16_t& port)
    {
        std::string id = ip + "-" + std::to_string(port);
        _users.erase(id);
    }

    bool IsOnline(const std::string& ip, const uint16_t& port)
    {
        std::string id = ip + "-" + std::to_string(port);
        return _users.find(id) != _users.end();
    }

    void BroadcastMessage(int sockfd, const std::string& message, const std::string& ip, const uint16_t& port)
    {
        for (auto& user : _users)
        {
            struct sockaddr_in client;
            client.sin_family = AF_INET;
            client.sin_addr.s_addr = inet_addr(user.second.ip().c_str());
            client.sin_port = htons(user.second.port());

            std::string s = "[" + ip + "-" + std::to_string(port) + "]" + "# " + message;
            sendto(sockfd, s.c_str(), s.size(), 0, (struct sockaddr*)&client, sizeof(client));
        }
    }

private:
    std::unordered_map<std::string, User> _users;
};

server.cpp

#include <iostream>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <functional>
#include <memory>

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

#include "User.h"

using namespace std;

typedef function<void(int, string, uint16_t, string)> func_t;
static string defaultIP = "0.0.0.0";

class UdpServer
{
public:
    UdpServer(const func_t& func, const uint16_t& port, const string& ip = defaultIP)
        : _cb(func), _port(port), _ip(ip)
    {}

    void Init()
    {
        // 1. 创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            cerr << "socket error: " << errno << strerror(errno) << endl;
            exit(errno);
        }

        // 2. 绑定 ip:port
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = htonl(INADDR_ANY);    // Address to accept any incomit message -------> 任意地址绑定
        // local.sin_addr.s_addr = inet_addr(_ip.c_str());     // string ---> uint32_t ---> htonl
        local.sin_port = htons(_port);
        
        int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
        if (n < 0)
        {
            cerr << "bind error: " << errno << strerror(errno) << endl;
            exit(errno);
        }
    }

    void Start()
    {
        char buf[1024];
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t peer_len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&peer, &peer_len);
            if (n > 0)
            {
                buf[n] = 0;
                string client_ip = inet_ntoa(peer.sin_addr);     // 网络序列 ---> 整型 ---> 点分十进制
                uint16_t client_port = ntohs(peer.sin_port);   
                string message = buf;
                cout << client_ip << "[" << client_port << "]# " << message << endl;

                _cb(_sockfd, client_ip, client_port, message);
            }
        }
    }

private:
    string _ip;
    uint16_t _port;
    int _sockfd;
    func_t _cb;
};

// 聊天群在线用户,静态全局变量
static OnlineUser g_online_users;

// 处理message,server与业务逻辑解耦
void RouteMessage(int sockfd, string client_ip, uint16_t client_port, string message)
{
    if (message == "online")
        g_online_users.AddUser(client_ip, client_port);
    if (message == "offline")
        g_online_users.DelUser(client_ip, client_port);

    if (g_online_users.IsOnline(client_ip, client_port))
    {
        g_online_users.BroadcastMessage(sockfd, message, client_ip, client_port);
    }
    else
    {
        struct sockaddr_in client;
        bzero(&client, sizeof(client));
        client.sin_family = AF_INET;
        client.sin_addr.s_addr = inet_addr(client_ip.c_str());
        client.sin_port = htons(client_port);

        string response = "你还没有登录,请输入online登录加入聊天群";
        sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr*)&client, sizeof(client));
    }
}

void Usage()
{
    cout << "Usage: \n\t" << "server port" << endl;
    exit(1);
}

int main(int args, char* argv[])
{
    if (args != 2)
        Usage();

    uint16_t port = atoi(argv[1]);

    unique_ptr<UdpServer> udp_server(new UdpServer(RouteMessage, port));

    udp_server->Init();
    udp_server->Start();
    
    return 0;
}

client.cpp

#include <iostream>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <functional>
#include <pthread.h>
#include <memory>

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

#include "User.h"

using namespace std;

class UdpClient
{
public:
    UdpClient(const string& server_ip, const uint16_t& server_port)
        : _server_ip(server_ip), _server_port(server_port), _sockfd(-1), _quit(false)
    {}

    void Init()
    {
        // 1. 创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        assert(_sockfd != -1);
        cout << "socket seccess: " << _sockfd << endl;

        // 2. 绑定bind,不需要显示绑定,由os分配
    }

    static void* ReadMessage(void* args)
    {
        int sockfd = *(static_cast<int*>(args));
        pthread_detach(pthread_self());     // 线程分离

        while (true)
        {
            char buf[1024];
            struct sockaddr_in tmp;
            socklen_t tmp_len = sizeof(tmp);
            ssize_t n = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&tmp, &tmp_len);
            if (n >= 0)
                buf[n] = 0;
            cout << "\r" << buf << "\n";
        }

        return nullptr;
    }

    void Run()
    {
        pthread_create(&_reader, nullptr, ReadMessage, (void*)&_sockfd);

        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = inet_addr(_server_ip.c_str());
        server.sin_port = htons(_server_port);

        string message;
        while (!_quit)
        {
            fprintf(stderr, "Enter:# ");
            fflush(stderr);
            getline(cin, message);
            message[message.size()] = 0;
            
            sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));
            usleep(100);
        }
    }

private:
    string _server_ip;
    uint16_t _server_port;
    int _sockfd;
    bool _quit;

    pthread_t _reader;
};

void Usage()
{
    cout << "Usage: \n\t" << "client ip port" << endl;
}

int main(int args, char* argv[])
{
    if (args != 3)
        Usage();

    string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);

    unique_ptr<UdpClient> udp_client(new UdpClient(server_ip, server_port));

    udp_client->Init();
    udp_client->Run();

    return 0;
}

运行效果:

 

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

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

相关文章

TypeScript 1 - 小记

文章目录 关于 TypeScript 关于 TypeScript TypeScript is a superset of JavaScript that compiles to clean JavaScript output. 官网&#xff1a;https://www.typescriptlang.orggithub : https://github.com/microsoft/TypeScriptplayground : https://www.typescriptlan…

AWS MSK集群认证和加密传输的属性与配置

通常&#xff0c;身份认证和加密传输是两项不相关的安全配置&#xff0c;在Kafka/MSK上&#xff0c;身份认证和加密传输是有一些耦合关系的&#xff0c;重点是&#xff1a;对于MSK来说&#xff0c;当启用IAM, SASL/SCRAM以及TLS三种认证方式时&#xff0c;TLS加密传输是必须的&…

RabbitMq(一)

一、基本概念、常见工作模式以及简单使用 MQ全称Message Queue (消息队列)&#xff0c;是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。 小结 MQ消息队列&#xff0c;存储消息的中间件分布式系统通信两种方式:直接远程调用和借助第三方完成间接通信发…

Layui基本功能(增删改查)

话不多说&#xff0c;根据我前面的博客我们直接进行操作。记住以下的文件放置&#xff0c;防止操作出不来. 这是我们要完成的界面及功能 后台功能实现 数据查看 我们在userDao方法里面进行增删改查的方法我们在userAction进行方法的编写R工具类的介绍 查询 userDao方法 因为我…

51单片机学习--矩阵键盘、电子密码锁

矩阵键盘的模块原理图&#xff1a; 功能&#xff1a;按下矩阵按键&#xff0c;在LCD上显示对应的数值。 采用模块化编程&#xff0c;在此只给出MatrixKey.c 和 main.c 两部分代码 #include <REGX52.H> #include "Delay.h"/*矩阵键盘读取键码按下不放&#xf…

typescript manual

这里写目录标题 throw new Error在浏览器中调试Json定义类型定义数组 functionNamed functionanonymous function Axios经典片段 错误及解决ref valuebecause it is a constantAPI 和 客户端定义的数据结构不一样ServerClient throw new Error throw new Error(“Get data err…

存css实现动态时钟背景

代码实现 <!DOCTYPE html> <html lang"en"> <head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><title>Title</title><meta name"referrer" content"no-referrer…

掌握无人机遥感数据预处理的全链条理论与实践流程、典型农林植被性状的估算理论与实践方法、利用MATLAB进行编程实践(脚本与GUI开发)以及期刊论文插图制作等

目录 专题一 认识主被动无人机遥感数据 专题二 预处理无人机遥感数据 专题三 定量估算农林植被关键性状 专题四 期刊论文插图精细制作与Appdesigner应用开发 近地面无人机植被定量遥感与生理参数反演 更多推荐 遥感技术作为一种空间大数据手段&#xff0c;能够从多时、多…

Linux —— 进程状态

目录 一&#xff0c;进程状态分类 二&#xff0c;僵尸进程 三&#xff0c;孤儿进程 一&#xff0c;进程状态分类 进程状态反应进程执行过程中的变化&#xff0c;状态会随外界条件的变化而转换&#xff1b; 三态模型&#xff0c;运行态、就绪态、阻塞态&#xff1b;五态模型…

PageObject+Python+Appium

目录 前言&#xff1a; 简介 功能 常用目录 配置 实例 - 第一次启动 app 实例 - 登录 代码入口实例 结果展示 前言&#xff1a; Page Object模式是一种常用的设计模式&#xff0c;用于组织和管理自动化测试脚本中的页面对象。它将页面的元素和操作封装在一个独立的类…

skbuff.h在哪

今天看来下底层socket的实现&#xff0c;发现很多人都说有个skbuff结构&#xff0c;我就想着去找下这个&#xff0c; cat /include/linux/skbuff.h 结果找不到&#xff0c;查了下&#xff0c;需要下载内核源码 //Ubuntusudo apt install linux-headers-genericdpkg -S skbuff.…

浏览器显示ERR_NETWORK_ACCESS_DENIED,安全设置或防火墙可能正在阻止连接,无法上网

环境: Win10 专业版 HP台式机 问题描述: 浏览器显示ERR_NETWORK_ACCESS_DENIED,安全设置或防火墙可能正在阻止连接,无法上网 1.无线连接状态正常 打不开网站 2.可以ping通百度DNS解析正常 3.防火墙已关闭 这样的错误可能由于多种原因而发生 原因分析 1.防火墙/防…

Unity游戏源码分享-Third Person Controller - Shooter Template v1.3.1

Unity游戏源码分享-Third Person Controller - Shooter Template v1.3.1 功能非常齐全 AI格斗 2.5D 完整工程地址&#xff1a;https://download.csdn.net/download/Highning0007/88057824

SpringCloud学习路线(3)—— Eureka注册中心

一、导引 服务调用出现的问题 服务调用采取的请求地址是静态的&#xff0c;当我们使用服务集群时&#xff0c;很容易造成只能调用固定的微服务上的接口。多个提供者&#xff0c;消费者的使用对象无法确定消费者无法得知提供者的状态 二、Eureka注册中心 &#xff08;一&…

一个小技巧,分分钟搞定新零售!

新零售模式的兴起带来了线上线下销售渠道的整合&#xff0c;而自动售货机作为新零售模式的一种重要形式&#xff0c;提供了便捷的自助购物体验。 自动售货机作为新零售模式的一种典型应用&#xff0c;以其便利性、快捷性和24小时无人值守的特点&#xff0c;深受消费者和商家的青…

【C++】C++入门必备知识详细讲解

C入门必备知识 一、命名空间1. namespace2. namespace 的使用场景 二、了解 C 中的输入和输出三、缺省参数四、函数重载1. 函数重载的概念2. C支持函数重载的原理 五、引用1. 引用的概念2. 引用特性3. 常引用4. 引用的使用场景&#xff08;1&#xff09;做参数&#xff08;传引…

ES系列--打分机制

一、文档打分机制 当你通过关键字搜索相关文档时&#xff0c;可能会出现多个文档&#xff0c;这些文档的顺序是通过一个max_score属性的大小从高到低顺序展现出来的&#xff0c;max_score属性就是我们所说的评分。而这个评分是通过一个文档打分机制计算出来的。 二、打分原理 …

第二章 Android 基础--开发基础

文章目录 1.使用真机调试运行2.Android开发涉及的编程语言3.工程目录结构4.编译配置文件 build.gradle5.清单文件6.界面显示与逻辑处理7.Activity创建与跳转8.练习题 本专栏主要在B站学习视频&#xff1a; B站Android视频链接 本视频范围&#xff1a;P9—P16 工程结构、设计规…

DDOS百科:什么是 DDoS 攻击及如何防护DDOS攻击

一、什么是 DDoS 攻击&#xff1f; 当多台机器一起攻击一个目标&#xff0c;通过大量互联网流量淹没目标或其周围基础设施&#xff0c;从而破坏目标服务器、服务或网络的正常流量时&#xff0c;就会发生分布式拒绝服务(DDoS)攻击。 DDoS允许向目标发送指数级更多的请求&#…

什么是 XSS 攻击,攻击原理是什么

什么是 XSS 攻击&#xff1f; XSS&#xff08;Cross-Site Scripting&#xff09;攻击是一种常见的 Web 安全漏洞&#xff0c;其攻击目标是 Web 应用程序中的用户&#xff0c;攻击者通过在 Web 页面中植入恶意脚本&#xff0c;从而实现窃取用户敏感信息、篡改用户数据等目的。 …