UDP服务器—实现数据通信

news2024/11/27 14:38:44

目录

前言

1.接口介绍

2.编写服务器

3.编写客户端

4.测试

总结


前言

        在这篇文章中为大家介绍如何通过编码实现数据通信,实现思路是根据前面介绍的网络编程函数编写一个服务端和客户端,实现客户端和服务端双方通信

1.接口介绍

创建套接字

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

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

domain:网络通信采用AF_INET

type:提供的服务类型,包含TCP流式服务和UDP数据包服务

实现UDP服务器参数设置为SOCK_DGRAM

protocol:采用的协议,一般设置为0,前面的两个参数决定了第三个参数

创建套接字的本质是告诉操作系统要进行网络通信,然后由操作系统在底层维护一个文件缓冲区,创建成功返回该文件描述符,后续数据通信,上层选择将数据放到该文件缓冲区中或者从文件缓冲区中去读数据

返回值:打开的文件描述符

绑定端口号

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

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

sockfd : 创建套接字的返回值

struct sockaddr* addr:使用时填充该结构体的字段

a.协议字段

b.端口号

端口号在进行填充的时候需要先进行转换为网络字节序


#include <arpa/inet.h>

uint16_t htons(uint16_t hostshort);

获取对方传输过来的端口号时需要将网络字节序转化为本机

#include <arpa/inet.h>

uint16_t ntohs(uint16_t netshort);

c.IP地址

一般在服务器的实现中IP地址是不需要绑定的

在客户端中发送数据需要填充服务端的IP地址,因为在用户层IP地址是字符串类型,在网络上传输时IP地址是整型数据,所以需要进行转换

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

in_addr_t inet_addr(const char *cp);

需要获取对方网络中传输过来的IP地址需要将整型转换为字符串类型

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

char *inet_ntoa(struct in_addr in);

注:因为是网络通信,所以在填充好字段之后需要在传参的时候强转为sockaddr_in

addrlen:结构体的大小

接受数据

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

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地址和port

addrlen:结构体的大小

发送数据

 #include <sys/types.h>
 #include <sys/socket.h>
 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地址和port端口号

addrlen:结构体的大小

有了上面这些的这些接口,下面我们就可以正式编写一个客户端和一个服务端了

2.编写服务器

形成makefile

.PHONY:all
all:udpServer udpClient

udpServer:udpServer.cc
	g++ -o $@ $^ -std=c++11
udpClient:udpClient.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -rf udpServer udpClient

编写服务器:udpServer.hpp

#pragma once
#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

namespace Server
{
    using namespace std;
    const static string defaultIP = "0.0.0.0";
    enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR};
    class udpServer
    {
    public:
        udpServer(uint16_t port, const string &ip = defaultIP) 
        :_port(port),_ip(ip),_sockfd(-1)
        {}
        void initServer()
        {
            //1.创建socket
            _sockfd = socket(AF_INET,SOCK_DGRAM,0);
            if(_sockfd == -1)
            {
                cerr<<"socket error:" << errno << strerror(errno) << endl;
                exit(SOCKET_ERR);
            }
            //2.绑定port和ip
            struct sockaddr_in local;
            bzero(&local,sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = htonl(INADDR_ANY);
            int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
            if(n == -1)
            {
                cerr<<"bind error:" << errno << strerror(errno) << endl;
                exit(BIND_ERR);
            }
        }
        void startServer()
        {
            char buffer[1024];
            for(;;)
            {
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                //peer len:输入,输出型参数:用来保存客户端的ip和port
                ssize_t s = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
                //打印发送来的数据
                if(s)
                {
                    buffer[s] = { 0 };
                    //inet_ntoa:网络字节序->int  int->点分十进制
                    string clientIp = inet_ntoa(peer.sin_addr);
                    //ntohl:网络字节序->int
                    uint16_t clientPort = ntohs(peer.sin_port);
                    string message = buffer;
                    cout << clientIp << "[" << clientPort << "]" << message << endl;
                }
            }
        }
        ~udpServer()
        {}
    private:
        uint16_t _port;
        string _ip;
        int _sockfd;
    };
}

启动服务器:udpServer.cc

#include "udpServer.hpp"
#include <memory>
using namespace Server;
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    unique_ptr<udpServer> usvr(new udpServer(port));
    usvr->initServer();
    usvr->startServer();
    return 0;
}

3.编写客户端

编写客户端:udpClient.hpp

#pragma once
#include <iostream>
#include <string>
#include <strings.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

namespace Client
{
    using namespace std;   
    class udpClient
    {
    public:
        udpClient(const string& serverIp,const uint16_t serverPort)
        :_serverIp(serverIp),_serverPort(serverPort),_sockfd(-1) {}
        void initClient()
        {
            //1.创建socket
            _sockfd = socket(AF_INET,SOCK_DGRAM,0);
            if(_sockfd == -1)
            {
                cerr<<"socket error:" << errno << strerror(errno) << endl;
                exit(2);
            }
            //client一定需要bind,服务器只有一个,但是客户端有许多个,所以服务端需要显示的绑定端口号
            //客户端一般不需要自己显示的绑定,而是由os自动形成端口号进行绑定
        }
        void run()
        {
            struct sockaddr_in server;
            memset(&server,0,sizeof(server));
            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(_serverIp.c_str());
            server.sin_port = htons(_serverPort);
            string message;
            while(1)
            {
                cout << "请输入:";
                cin >> message;
                //发送数据到客户端
                sendto(_sockfd,message.c_str(),message.size(),0,(const struct sockaddr*)&server,
                sizeof(server));
            }
        }
    private:
        string _serverIp;
        int _sockfd;
        uint16_t _serverPort;
    };
}

启动客户端:udpClient.cc

#include"udpClient.hpp"
#include<memory>
using namespace Client;
static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";
}
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    string serverip = argv[1];
    uint16_t serverport = atoi(argv[2]);
    unique_ptr<udpClient> uct(new udpClient(serverip,serverport));
    uct->initClient();
    uct->run();
    return 0;
}

4.测试

说明:因为本次是在一台主机上,所以为了做测试将IP地址绑定为"127.0.0.1",该IP地址的特点是专门用来在本主机上做测试,称为本地环回,后续在不同主机上通信的时候只需要将IP地址改为对方主机的IP地址即可。

 此时我们就可以看到当客户端向服务器发送数据时,服务器收到了数据,并且将数据回显出来

总结

通过上面的编码,使用UDP协议简单实现了一个服务器,可以用来进行数据通信了,是不是感觉很神奇呀,看到这里你就会发现,原来网络通信也并不复杂,和系统内部进程间通信有异曲同工之妙关于网络通信的更多细节,后面再一一为大家介绍,我们下次再见!

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

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

相关文章

前端:Vue.js学习

前端:Vue.js学习 1. 第一个Vue程序2. Vue指令2.1 v-if、v-else-if、v-else2.2 v-for2.3 事件绑定 v-on:2.4 v-model 数据双向绑定2.5 v-bind 绑定属性 3. Vue组件4. Vue axios异步通信5. 计算属性6. 插槽 slots7. 自定义事件内容分发 1. 第一个Vue程序 首先把vue.js拷贝到本地…

腾讯云服务器CVM实例族标准型/内存/高IO/大数据/GPU有什么区别?

腾讯云服务器CVM有多种实例族&#xff0c;如标准型S6、标准型S5、SA3实例、高IO型、内存、计算型及GPU型实例等&#xff0c;如何选择云服务器CVM实例规格呢&#xff1f;腾讯云服务器网建议根据实际使用场景选择云服务器CVM规格&#xff0c;例如Web网站应用可以选择标准型S5或S6…

关于fastjson序列化失败的问题

com.alibaba.fastjson2.JSONArray cannot be cast to com.alibaba.fastjson2.JSONArray 问题 我们使用若依的架子把common抽离出来一个jar包&#xff0c;导致从redis中序列化出错 这是由于springboot-devtools 使用restartClassLoader加载了JSONArray 而上层controller中的JSO…

Dataloader数据集的制作

数据集Dataloader制作 如何自定义数据集&#xff1a; 1.数据和标签的目录结构先搞定(得知道到哪读数据)2.写好读取数据和标签路径的函数(根据自己数据集情况来写)3.完成单个数据与标签读取函数(给dataloader举一个例子) 咱们以花朵数据集为例&#xff1a; 原来数据集都是以…

Flink CDC系列之:TiDB CDC 导入 Elasticsearch

Flink CDC系列之&#xff1a;TiDB CDC 导入 Elasticsearch 一、通过docker 来启动 TiDB 集群二、下载 Flink 和所需要的依赖包三、在TiDB数据库中创建表和准备数据四、启动Flink 集群&#xff0c;再启动 SQL CLI五、在 Flink SQL CLI 中使用 Flink DDL 创建表六、Kibana查看Ela…

页面静态化(模板引擎Freemarker)

1、浏览器请求web服务器 2、服务器渲染页面&#xff0c;渲染的过程就是向jsp页面(模板)内填充数据(模型)。 3、服务器将渲染生成的页面返回给浏览器。 所以模板引擎就是&#xff1a;模板数据输出&#xff0c;Jsp页面就是模板&#xff0c;页面中嵌入的jsp标签就是数据&#x…

【Linux】Reactor模式

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录 &#x1f449;Reactor …

生成式人工智能模型:提升营销分析用户体验

使用生成式人工智能来改善分析体验&#xff0c;使业务用户能够询问有关我们数据平台中可用数据的任何信息。 在本文中&#xff0c;我们将解释如何使用新的生成式人工智能模型 ( LLM ) 来改善业务用户在我们的分析平台上的体验。假设我们为零售销售经理提供 Web 应用程序或移动应…

【雕爷学编程】Arduino动手做(24)---水位传感器模块3

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

斯坦福「小镇」开源AI智能体;小米应用商店将要求AI应用符合资质标准

&#x1f989; AI新闻 &#x1f680; 斯坦福「小镇」开源AI智能体 摘要:斯坦福研究人员开源了一个类似《西部世界》的数字化「小镇」,里面有25个AI智能体可以生活、工作、社交。这项研究被视为AGI的重要开端,可能会改变游戏、企业应用领域。网友期待这项技术改善游戏NPC的交互…

【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(上)

前提介绍 Feign是SpringCloud中服务消费端的调用框架&#xff0c;通常与ribbon&#xff0c;hystrix等组合使用。由于遗留原因&#xff0c;某些项目中&#xff0c;整个系统并不是SpringCloud项目&#xff0c;甚至不是Spring项目&#xff0c;而使用者关注的重点仅仅是简化http调…

软工导论知识框架(七)面向对象设计

一.设计准则 分析&#xff1a;提取、整理用户需求&#xff0c;建立问题域精确模型。设计&#xff1a;转变需求为系统实现方案&#xff0c;建立求解域模型。 在实际的软件开发过程中分析和设计的界限是模糊的&#xff0c;分析和设计活动是一个多次反复迭代的过程。分析的结果可…

无涯教程-Perl - msgsnd函数

描述 此功能使用可选的FLAGS将消息MSG发送到消息队列ID。 语法 以下是此函数的简单语法- msgsnd ID, MSG, FLAGS返回值 该函数在错误时返回0,在成功时返回1。 Perl 中的 msgsnd函数 - 无涯教程网无涯教程网提供描述此功能使用可选的FLAGS将消息MSG发送到消息队列ID。 语法…

接地电阻测试仪的原理和使用事项

接地电阻测试仪&#xff08;Ground Resistance Tester&#xff09;是用来测量接地电阻的一种仪器。接地系统是指用于保护人员和设备的设施&#xff0c;它将电流引导到地下&#xff0c;将任何潜在危险的电流导向地面。 接地电阻测试仪的作用是通过测量接地系统的电阻值来评估其…

C++ STL string类模拟实现

目录 string类成员变量 一.构造函数 二.析构函数 三.拷贝构造 四.size(),capacity() 五.operator [ ] 六. operator 七.字符串比较 八.reserve&#xff08;&#xff09; 九.push_back&#xff08;&#xff09;&#xff0c;append&#xff08;&#xff09; 十.operato…

【雕爷学编程】Arduino动手做(12)---霍尔磁场传感器模块5

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

数据归一化:优化数据处理的必备技巧

文章目录 &#x1f340;引言&#x1f340;数据归一化的概念&#x1f340;数据归一化的应用&#x1f340;数据归一化的注意事项与实践建议&#x1f340;代码演示&#x1f340;在sklearn中使用归一化&#x1f340;结语 &#x1f340;引言 在当今数据驱动的时代&#xff0c;数据的…

Vue在页面输出JSON对象,测试接口可复制使用

效果图&#xff1a; 数据处理前&#xff1a; 数据处理后&#xff1a; 代码实现&#xff1a; HTML: <el-table height"600" :data"tableData" border style"width: 100%" tooltip-effect"dark" size"mini"><el-…

Django笔记之数据库函数之日期函数

日期函数主要介绍两个大类&#xff0c;Extract() 和 Trunc() Extract() 函数作用是提取日期&#xff0c;比如我们可以提取一个日期字段的年份&#xff0c;月份&#xff0c;日等数据 Trunc() 的作用则是截取&#xff0c;比如 2022-06-18 12:12:12&#xff0c;我们可以根据需求…

深度学习基础知识笔记

深度学习要解决的问题 1 深度学习要解决的问题2 应用领域3 计算机视觉任务4 视觉任务中遇到的问题5 得分函数6 损失函数7 前向传播整体流程8 返向传播计算方法1 梯度下降9 神经网络整体架构 11 神经元个数对结果的影响12 正则化和激活函数1 正则化2 激活函数 13 神经网络过拟合…