【计算机网络】UDP网络程序

news/2024/11/18 22:13:34/

一、服务端

1.udpServer.hpp

此文件负责实现一个udp服务器

#pragma once#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>// 默认参数
static const std::string defaultIp = "0.0.0.0";
static const int gnum = 1024;// 回调函数类型
typedef std::function<void (int, std::string, uint16_t, std::string)> func_t;// 错误码
enum
{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,OPEN_ERR
};// udp服务器
class udpServer
{
public:// 构造函数udpServer(func_t &callback, uint16_t &port, std::string &ip = defaultIp):_callback(callback), _port(port), _ip(ip), _sockfd(-1){}// 析构函数~udpServer(){}// 初始化服务器void initServer();// 启动服务器void start();private:uint16_t _port;  // 服务器进程的端口号std::string _ip; // 服务器的IP 一款网络服务器不建议指明一个IPint _sockfd;     // 创建socket后返回的文件描述符,利用这个fd进行读写func_t _callback;// 回调函数:利用这个函数处理业务
};

(1)initServer()

void initServer()
{// 1. 创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){cerr << "socket error" << errno << " : " << strerror(errno) << endl;exit(SOCKET_ERR);}std::cout << "socket success" << " : " << _sockfd << std::endl;// 2. 绑定 port ip// 未来服务器要明确的port,不能随意改变struct sockaddr_in local;bzero(&local, sizeof(local)); // 初始化结构体为全0local.sin_family = AF_INET;// 你如果要给别人发消息,你的port和ip要发送给对方// 所以port和ip要从当前主机到网络 h -> nlocal.sin_port = htons(_port);local.sin_addr.s_addr = inet_addr(_ip.c_str()); // local.sin_addr.s_addr = htonl(INADDR_ANY);// 任意地址bind:INADDR_ANY:全0// 把创建的fd和端口号绑定int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n == -1){cerr << "bind error" << errno << " : " << strerror(errno) << endl;exit(BIND_ERR);}// UDP Server 的预备工作完成
}

(2)start()

void start()
{// 服务器的本质其实就是一个死循环char buffer[gnum]; // 读到的数据放这里for (;;){// 读取数据struct sockaddr_in peer;socklen_t len = sizeof(peer);                                                                 // recvfrom读取数据ssize_t s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);// 处理数据if (s > 0){// 1.知道是谁发的(获取客户端ip/port)std::string clientip = inet_ntoa(peer.sin_addr); // IPuint16_t clientport = ntohs(peer.sin_port); // 端口号// 2.知道发的什么(获取客户端发送的内容)buffer[s] = '\0';std::string message = buffer;cout << clientip << "[" << clientport << "]# " << message << endl;// 我们只把数据读上来就完了吗?还需要对数据做处理_callback(_sockfd, clientip, clientport, message);}}
}

2.udpServer.cpp

此文件负责udp服务器的调用逻辑

#include "udpServer.hpp"
#include <cstdio>
#include <memory>
#include <fstream>
#include <signal.h>// 使用手册
void Usage(std::string proc)
{std::cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}// 对收到的数据做处理
void handlerMessage(int sockfd, std::string clientip, uint16_t clientport, std::string message)
{// 填写客户端信息struct sockaddr_in client;bzero(&client, sizeof(client));client.sin_family = AF_INET;client.sin_port = htons(clientport);client.sin_addr.s_addr = inet_addr(clientip.c_str());// 处理并发送消息(服务器 -> 客户端)std::string response_message = message;response_message = "[server echo] " + message;sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr*)&client, sizeof(client));
}// ./udpServer local_port
int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage, port));usvr->initServer();usvr->start();return 0;
}

二、客户端

1.udpClient.hpp

此文件负责实现一个udp客户端

#pragma once#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>class udpClient
{
public:// 构造函数udpClient(std::string &serverIp, const uint16_t &serverPort): _serverIp(serverIp), _serverPort(serverPort), _sockfd(-1), _quit(false){}// 析构函数~udpClient(){}// 初始化客户端void initClient()// 这个线程负责 接受消息static void *readMessage(void *args)// 主线程负责 发送消息void run()private:int _sockfd;           // 自己的fdstd::string _serverIp; // 目的地uint16_t _serverPort;  // 目的地bool _quit;            // 表明是否退出
};

(1)initClient()

void initClient()
{// 1.创建socket_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd == -1){cerr << "socket error" << errno << " : " << strerror(errno) << endl;exit(2);}std::cout << "socket success" << " : " << _sockfd << std::endl;// 2.client要不要bind?要;client要不要显示的bind/程序员自己bind?不需要
}

① 客户访问服务器时需要一个确定的端口号,所以服务器的端口需要我们明确指出

② 客户端的端口号不用我们明确指出,因为写客户端的是许多家公司

假设A公司给Client_A定的端口号是8080,B公司给Client_B定的端口号也是8080

这样这两个程序就无法一起运行,不符合实际,所以客户端由OS自动形成端口进行bind!

(2)run()

// 主线程负责 发送消息
void run()
{// 填写服务器的ip/portstruct 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);// 消息处理std::string message;char line[1024];while (!_quit){// 发送消息(客户端 -> 服务器)std::cout << "Please Enter# ";// 这种输入:重点在于可以输入空格fgets(line, sizeof(line), stdin);line[strlen(line) - 1] = '\0'; // 处理掉输入进来的回车message = line;sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));// 没有显示绑定时,第一次发消息的时候绑定port// 接收消息(服务器 -> 客户端)char buffer[1024]; // 收到的消息放到这里struct sockaddr_in serverSock;socklen_t len = sizeof(serverSock);size_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&serverSock, &len);if (n >= 0) buffer[n] = 0;std::cout << buffer << std::endl;}
}

2.udpClient.cpp

此文件负责udp客户端的调用逻辑

#include "udpClient.hpp"
#include <memory>// 使用手册
void Usage(std::string proc)
{std::cout << "\nUsage:\n\t" << proc << " server_ip server_port\n\n";
}// ./udpClient server_ip server_port -> 输入目的地址(服务器地址)
int main(int argc, char *argv[])
{if(argc != 3){Usage(argv[0]);exit(1);}std::string serverIp = argv[1];uint16_t serverPort = atoi(argv[2]);std::unique_ptr<udpClient> ucli(new udpClient(serverIp, serverPort));ucli->initClient();ucli->run();return 0;
}

三、整体调用逻辑

1.双方调用逻辑

UDP服务器可以多个客户端同时访问,用一幅图展示这个过程

2.展示运行结果


http://www.ppmy.cn/news/1548086.html

相关文章

【Jenkins实战】Windows安装服务启动失败

写此篇短文&#xff0c;望告诫后人。 如果你之前装过Jenkins&#xff0c;出于换域账号/本地帐号的原因想重新安装&#xff0c;你大概率会遇上一次Jenkins服务启动失败提示&#xff1a; Jenkins failed to start - Verify that you have sufficient privileges to start system…

LeetCode题解:18.四数之和【Python题解超详细】,三数之和 vs. 四数之和

题目描述 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&#xff1a; …

【贪心算法】贪心算法三

贪心算法三 1.买卖股票的最佳时机2.买卖股票的最佳时机 II3.K 次取反后最大化的数组和4.按身高排序5.优势洗牌&#xff08;田忌赛马&#xff09; 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#…

android studio new flutter project-运行第一个flutter项目

android studio new flutter project win10系统&#xff0c;由于之前尝试学习RN的时候已经安装了android studio 所以在尝试运行Flutter项目省去了一些步骤 这里说一下如何在android studio创建第一个flutter project 下载flutter sdk 到 https://docs.flutter.cn/release/a…

.NET 9.0 中 System.Text.Json 的全面使用指南

以下是一些 System.Text.Json 在 .NET 9.0 中的使用方式&#xff0c;包括序列化、反序列化、配置选项等&#xff0c;并附上输出结果。 基本序列化和反序列化 using System; using System.Text.Json; public class Program {public class Person{public string Name { get; se…

HTTP常见的请求头有哪些?都有什么作用?在 Web 应用中使用这些请求头?

HTTP 请求头&#xff08;Request Headers&#xff09;用于在 HTTP 请求中携带额外的信息&#xff0c;帮助服务器更好地处理请求。以下是一些常见的 HTTP 请求头及其作用&#xff1a; 常见请求头及其作用 1. Accept 作用&#xff1a;告知服务器客户端可以接受的内容类型。示例…

网络技术-定义配置ACL规则的语法和命令

定义ACL&#xff08;访问控制列表&#xff09;规则时&#xff0c;具体命令会根据所使用的设备和操作系统而有所不同。以下是一些常见的设备和操作系统中定义ACL规则的命令示例&#xff1a; 一&#xff1a;思科&#xff08;Cisco&#xff09;路由器/交换机 在思科设备中&#…

微服务day07

MQ高级 发送者可靠性&#xff0c;MQ的可靠性&#xff0c;消费者可靠性。 发送者可靠性 发送者重连 连接重试的配置文件&#xff1a; spring:rabbitmq:connection-timeout: 1s # 设置MQ的连接超时时间template:retry:enabled: true # 开启超时重试机制initial-interval: 10…