网络套接字编程(UDP协议)

news2024/10/7 12:26:15

文章目录

    • 预备知识
    • socket(网络套接字)编程接口
    • 简单的UDP网络程序
    • 增加多用户可以互相通信

预备知识

网络字节序
大端存储:数据的高字节内容保存在内存的低地址处,数据的低字节内容保存在内存的高地址处
小端存储:数据的高字节内容保存在内存的高地址处,数据的低字节内容保存在内存的低地址处

内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏
移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.

不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;

如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可

网络字节序和主机字节序的转换

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;

如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

socket(网络套接字)编程接口

// 创建 socket 文件描述符 
int socket(int domain, int type, int protocol);
 
// 绑定端口号 
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

sockaddr结构
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,然而, 各种网络协议的地址格式并不相同.

在这里插入图片描述

简单的UDP网络程序

socket函数

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

参数说明:

domain:创建套接字的域或者叫做协议家族,也就是创建套接字的类型。该参数就相当于struct sockaddr结构的前16个位。如果是本地通信就设置为AF_UNIX,如果是网络通信就设置为AF_INET(IPv4)或AF_INET6(IPv6)。

type:创建套接字时所需的服务类型。其中最常见的服务类型是SOCK_STREAM和SOCK_DGRAM,如果是基于UDP的网络通信,我们采用的就是SOCK_DGRAM,叫做用户数据报服务,如果是基于TCP的网络通信,我们采用的就是SOCK_STREAM,叫做流式套接字,提供的是流式服务。

protocol:创建套接字的协议类别。你可以指明为TCP或UDP,但该字段一般直接设置为0就可以了,设置为0表示的就是默认,此时会根据传入的前两个参数自动推导出你最终需要使用的是哪种协议。

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

参数说明:

sockfd:创建套接字返回的文件描述符
addr:网络相关的属性
传入的addr的长度

struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;			/* Port number.  */
    struct in_addr sin_addr;		/* Internet address.  */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
			   __SOCKADDR_COMMON_SIZE -
			   sizeof (in_port_t) -
			   sizeof (struct in_addr)];
  };

sin_port:表示端口号
sin_family:表示协议家族
sin_addr:表示IP地址

字符串转in_addr

in_addr_t inet_addr(const char *cp);

服务器绑定

class udpServer
{
	udpServer(uint16_t port, string ip = "") : _port(port), _ip(ip)
    {
    }
    void init()
    {
        //1. 创建套接字
        _sockfd = socket(AF_INET,SOCK_DGRAM,0);

        if(_sockfd < 0)
        {
            logmessage("error");
            exit(1);
        }
        logmessage("socket create sucessfully");


        // 2. 创建ip与端口号
        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port=htons(_port);
        // INADDR_ANY表示让服务器端计算机上的全部网卡的IP地址均可以做为服务器IP地址
        local.sin_addr.s_addr =  _ip.empty() ?  htonl(INADDR_ANY)  :
          inet_addr(_ip.c_str());

        socklen_t len = sizeof(local);
        //3.连接
        if(bind(_sockfd,(struct sockaddr*)&local,len) == -1) 
        {
            logmessage("bind error");
			exit(2);
        }
        logmessage("bind sucessfully");
    }
private:
    int _sockfd; //套接字

    uint16_t _port;  // 端口号
    string _ip; // ip地址
};

服务器读取用户发来的数据

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:对端网络相关的属性信息,包括协议家族、IP地址、端口号等。
addrlen:调用时传入期望读取的src_addr结构体的长度,返回时代表实际读取到的src_addr结构体的长度,这是一个输入输出型参数。

服务器接收用户信息

message.hpp

struct message
{
    /* data */
    message()
    {
        _time=(uint64_t)time(nullptr);
    }

    friend ostream& operator<<(ostream& out,const message& mess);


    char _name[10];
    char _message[1024];
    uint64_t _time;
};


ostream& operator<<(ostream& out,const message& mess)
{
    out<<"时间:"<<mess._time<<"用户名:"<<mess._name<<endl;
    out<<mess._message<<endl;
    return out;
}
void start()
{
    message mess;

    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    memset(&client,0,sizeof(client));
    while(1)
    {
        ssize_t s = recvfrom(_sockfd,&mess,sizeof(mess),0,(struct sockaddr*)&client,&len);

        if(s < 0)
        {
            logmessage("recvfrom error");
            continue;
        }
        logmessage("recvfrom sucess");

        cout<<mess;
    }
}

客户端创建套接字

class udpClient
{

public:
    udpClient(const string &serip, uint16_t serport) : _serip(serip), _serport(serport)
    {
        //创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    }
    
private:
    int _sockfd; // 套接字

    string _serip;     // 服务器ip
    uint16_t _serport; // 服务器port
};

为什么客户端不需要绑定
所谓的"不需要",指的是: 不需要用户自己bind端口信息!因为OS会自动给你绑定,推荐这么做

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:对端网络相关的属性信息,包括协议家族、IP地址、端口号等。
addrlen:传入dest_addr结构体的长度。

客户端发送消息

void communicate()
{
    message mess;
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(_serport);
    server.sin_addr.s_addr = inet_addr(_serip.c_str());
    cout<<"请输入你的名字"<<endl;
    cin>>mess._name;
    while (1)
    {
        cout<<"请输入你要发的消息"<<endl;
        cin>>mess._message;
        
        sendto(_sockfd, &mess, sizeof(mess), 0, 
        (struct sockaddr *)&server, sizeof(server));
    }
}

效果:在这里插入图片描述

增加多用户可以互相通信

class udpServer
{
public:
	void start()
    {
        message mess;

        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        memset(&client,0,sizeof(client));
        while(1)
        {
            ssize_t s = recvfrom(_sockfd,&mess,sizeof(mess),0,
            (struct sockaddr*)&client,&len);

            if(s < 0)
            {
                logmessage("recvfrom error");
                continue;
            }
            logmessage("recvfrom sucess");

            cout<<mess;

            // 给其他用户聊天发送信息
            messageRoutine(mess);

            string ip_port;
            ip_port+=to_string(ntohl(client.sin_addr.s_addr));
            ip_port+=to_string(ntohs(client.sin_port));

            users.insert({ip_port,client});
        }
    }
private:
void messageRoutine(const message& mess)
    {
        for(auto user:users)
        {
            sendto(_sockfd,&mess,sizeof(mess),0,
            (struct sockaddr*)&(user.second),sizeof(user.second));
        }
    }


private:
    int _sockfd; //套接字

    uint16_t _port;  // 端口号
    string _ip; // ip地址
    unordered_map<string,struct sockaddr_in> users; // 所有的用户
};
class udpClient
{

public:
    udpClient(const string &serip, uint16_t serport) : _serip(serip), _serport(serport)
    {
        //创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    }

    void communicate()
    {

        pthread_t t;
        pthread_create(&t,nullptr,ReadMessage,(void*)&_sockfd);
        pthread_detach(t);



        message mess;
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(_serport);
        server.sin_addr.s_addr = inet_addr(_serip.c_str());
        cout<<"请输入你的名字"<<endl;
        cin>>mess._name;
        cout<<"开始通信"<<endl;

        while (1)
        {
            cin>>mess._message;
        
            sendto(_sockfd, &mess, sizeof(mess), 0, (struct sockaddr *)&server, sizeof(server));
        }
    }

    ~udpClient()
    {
    }
private:
static void* ReadMessage(void* args)
{
    int socket = *(int*)args;

    message mess;
    struct sockaddr_in tmp;
    socklen_t len=sizeof(tmp);
    while(1)
    {
        recvfrom(socket,&mess,sizeof(mess),0,(sockaddr*)&tmp,&len);
        cout<<mess<<endl;
    }
}

private:
    int _sockfd; // 套接字

    string _serip;     // 服务器ip
    uint16_t _serport; // 服务器port
};

效果:
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

婚纱预订小程序开发,商家线上展示平台

婚纱代表着纯洁与忠贞&#xff0c;也是爱情永恒的见证者&#xff0c;穿上洁白的婚纱嫁给自己心爱的人是每个女生的梦想&#xff0c;婚纱对于每一个女生来说都有着重要的意义&#xff0c;所以选择一件美丽且适合的婚纱非常重要&#xff0c;因此人们在选择婚纱时会花费很多的时间…

MySQL数据库之索引

目录 一、MySQL索引简介 二、索引的作用 1、优点 2、缺点 三、创建索引的原则依据 四、索引的分类和创建 1、普通索引 2、唯一索引&#xff08;创建唯一键即创建唯一索引&#xff09; 3、主键索引&#xff08;和创建主键的方式一样&#xff09; 4、组合索引&#xff…

Python标准库之copy

1. copy标准库简介 Python 中赋值语句不复制对象&#xff0c;而是在目标和对象之间创建绑定 (bindings) 关系。对于自身可变或者包含可变项的集合对象&#xff0c;我们有时会需要生成其副本用于改变操作&#xff0c;进而避免改变原对象。 2. copy常用函数 2.1 copy.copy(x) …

R语言用ARIMA模型滑动时间窗口识别网络流量时间序列异常值

全文链接&#xff1a;http://tecdat.cn/?p30597最近我们被要求解决时间序列异常检验的问题。有客户在使用大量的时间序列。这些时间序列基本上是每10分钟进行一次的网络测量&#xff0c;其中一些是周期性的&#xff08;即带宽&#xff09;&#xff0c;而另一些则不是&#xff…

Android Jetpack Compose——一个简单的聊天界面

Jetpack Compose——聊天界面前言效果视频引入RowColumnTextImage聊天界面效果左边布局右边布局插入数据总结前言 目前声明式UI已经成为前端开发趋势&#xff0c;除了一开始的跨端开发React,Flutter等以及Web支持外&#xff0c;后续Android和IOS平台也相继推出声明式开发&…

零基础快速开发Vue图书管理系统—登录注册篇(一)

零基础快速开发Vue图书管理系统—登录注册篇&#xff08;一&#xff09; 一、图书管理系统项目功能 二、项目技术选型 前端主要采用&#xff1a;Vue3.x (vuex/vue-router)、Ant Design Vue、Axios等服务端主要采用&#xff1a;Node.js、Koa、Mongoose等数据库主要采用&#x…

Docker安装Redis集群失败经历汇总

在程序员的开发过程中&#xff0c;Redis可以说基本上是必不可少的缓存中间件。不管是二进制包还是docker安装Redis的文章在网上都是数不胜数。我之前自己玩Redis的时候基本不是二进制包安装就是docker安装&#xff0c;也没有尝试过集群方式。每次需要的时候&#xff0c;网上百度…

DataFrame转化为json的方法教程

网络上有好多的教程&#xff0c;讲得不太清楚和明白&#xff0c;我用实际的例子说明了一下内容&#xff0c;附档代码&#xff0c;方便理解和使用 DataFrame.to_json(path_or_bufNone, orientNone, date_formatNone, double_precision10, force_asciiTrue, date_unitms, defau…

考研数据结构大题整合_组一(ZYL组)

考研数据结构大题整合 目录考研数据结构大题整合一、ZYL组ZYL组一ZYL组二ZYL组三ZYL组四ZYL组五ZYL组六ZYL组七ZYL组八一、ZYL组 ZYL组一 1.一棵树有度为i的结点ni 个(i1,2,3,…m), 求叶结点的个数.&#xff08;10分&#xff09; 2、已知带权连通图G(V,E)的邻接表如图所示&am…

rubbitmq 图形界面使用 常用六种通信模式 Simple-Work-fanout-direct-topic-headers

阿里云服务器添加rubbitmq需要开启端口:rabbitmq阿里云服务器开放端口号 Rubbitmq地址: 服务器地址:15672 1.简单模式Simple 一个生产者、一个消费者&#xff0c;不需要设置交换机&#xff08;使用默认的交换机&#xff09; 2.工作队列模式Work Queue 一个生产者、多个消费者&a…

windows操作系统双网卡问题处理办法

windows操作系统双网卡问题处理办法&#xff08;详解&#xff09;一、命令说明二、处理办法1.设置外网网关为默认网关2.查看当前路由表3.删除缺省路由4.添加访问外网的缺省路由5.添加访问内网的路由信息一、命令说明 显示 IP 路由表的信息 route print显示 IP 路由表中以 192…

3.7.3、ARP协议(网际层)

地址解析协议 ARP 1、工作原理 在共享总线型的以太网中 为了简单起见&#xff1a;只有各主机所配置的 IP 地址和其网卡上固化的 MAC 地址 假设主机 B 要给主机 C 发送数据包 主机 B 知道主机 C 的 IP 地址&#xff0c;但是不知道 C 的 MAC 地址 因此&#xff0c;主机 B 的…

使用 Bytebase 管理 Rainbond 上的应用数据库

在应用的发布过程中数据库的结构变更一直是最复杂也是风险最大的环节&#xff0c;而 Bytebase 可以对这一过程进行全生命周期的管理。在 Rainbond 中安装 Bytebase&#xff0c;轻松管理部署在 Rainbond 上的所有数据库。 Bytebase 是什么&#xff1f; Bytebase 是一个开源的数…

linux驱动 usb转串口ch344 改变读取缓冲区大小

开发环境 核心板&#xff1a;IMX6 内核版本&#xff1a;linux4.1.5 问题 通过USB扩展出来的串口接收数据会出现截断现象&#xff0c;而且每次截断的大小都一样。而核心板提供的串口UART就没有这个现象。 核心板自带串口正常的现象&#xff1a; 扩展串口异常现象&#xff1a…

【软件测试】8年资深测试说出来我们的心声......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 执着于手动的功能测…

VLAN划分-----计算机网络

拓扑图 LSW1与LSW2都是三层交换机&#xff0c;现在两台交换机分别连着两台pc。 其中&#xff1a; LSW1的0/0/1端口连着pc1&#xff0c;0/0/2连着pc2&#xff0c;0/0/3端口连着lsw2。 LSW2的0/0/1端口连着pc3&#xff0c;0/0/2连着pc4&#xff0c;0/0/3端口连着lsw1。 不同vlan…

pycharm opencv无法自动补全

我的环境 python 3.6.9opencv-python 4.4.0.42opencv-contrib-python 4.6.66ubuntu 18.04 LTSpycharm 2020.3.2 解决方案 首先找到cv2的site-packagespip3 show opencv-python进入到该目录, 复制so结尾文件至上级目录: cp cv2.cpython-36m-x86_64-linux-gnu.so ../等待pych…

基于粒子群算法和遗传算法优化的高速列车横向悬挂

目录 前言 1.高速列车模型 2.优化算法优化模糊PID流程 3.普通PID、优化算法模糊PID仿真对比 3.1 模糊控制器设计 3.2 仿真结果 3.2.1粒子群优化PID 3.2.2粒子群优化模糊PID 3.2.3遗传算法优化模糊PID 4.总结 前言 高速列车&#xff0c;是指最高行驶速度在200km/h 及以…

二、【React-Router5】路由的基本使用

文章目录1、写在前面的总结2、效果图3、项目结构4、CODE4.1、index.js4.2、App.js4.3、About.jsx4.4、Home.jsx5、Result6、路由组件与一般组件7、Link 升级 NavLink8、封装NavLink8.1、MyNavLink.jsx8.2、修改上面4.2部分代码1、写在前面的总结 明确好界面中的导航区、展示区…

火山引擎数智平台:CDP产品要能与多方联动

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 CDP&#xff08;Customer Data Platform&#xff0c;客户数据平台&#xff09;市场将迎来新一轮的高速增长。 国际数据公司&#xff08;以下简称“IDC”&#xff0…