当前位置: 首页 > article >正文

UDP_SOCKET编程实现

文章目录

  • socket编程接口
    • 认识`struct sockaddr`类
  • 编写一个server服务
  • Client代码
  • 查看启动结果
  • 代码修正
    • 1.获取内核分配给客户端的信息
    • 2.修正不匹配ip不能访问的问题
  • 不同机器之间的通信
    • 利用xftp将udp_client传给wsl的ubuntu机器进行演示
    • 现在模拟在windows下的udp_client代码:
  • 对方发的命令也进行执行或者是打印
  • udp tcp是支持全双工的, 所以我们可以实现一个聊天室

socket编程接口

socket常见接口API:
在这里插入图片描述

认识struct sockaddr

网络套接字编程时,socket也分很多类:
1.unix socket:域间socket->用的是同一套接口, 不过用的是同一台机器上的文件路径,类似命名管道,负责本主机内部进行通信
2.网络socket:ip+port 网络通信(也可实现本地socket)(重点)
3.原始socket(应用层直接访问数据链路层,一般用于编写一些网络工具)

设计者想实现在不同应用场景使用一套接口,struct sockaddr{};是一个通用的地址类型

在这里插入图片描述
在这里插入图片描述
由于历史原因, 当初设计的时候不支持void*的通用接口
所以使用这样的方式

编写一个server服务

实现一个服务:
封装一个udpserver
构建服务基本框架:
UdpServer.hpp

#pragma once
#include "nocopy.hpp"
#include <iostream>
//父类内不存在拷贝, 所以基类也不存在拷贝
class UdpServer : public nocopy
{
public:
	UdpServer(){}
	void Init(){}
	void Start(){}
	~UdpServer(){}
private:
};

Main.cc

	#include "UdpServer.hpp"
	#include <memory>
	int main()
	{   
	    //使用std::make_unique函数初始化它
	    std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>();
	    usvr->Init();
	    usvr->Start();
	
	    return 0;
	}

为了防止这个类服务被拷贝, 所以禁用一系列的拷贝函数
nocopy.hpp

	#pragma once
	#include <iostream>
	class nocopy
	{
	public:
	    nocopy() = default;
	    nocopy(const nocopy&) = delete;
	    const nocopy& operator=(const nocopy&) = delete;
	    //推荐禁用上面那个
	    //nocopy& operator=(const nocopy&) = delete;
	    ~nocopy() = default;
};

socket编写:
在这里插入图片描述

返回值:
创建成功返回一个文件描述符,出错返回-1

在这里插入图片描述
在这里插入图片描述
参数3:
确认是udp还是TCP
udp这里是固定写法
创建套接字
在这里插入图片描述绑定网络信息

在这里插入图片描述

返回值: 成功为0 , 错误返回-1, 错误码被设置
参数2虽然是struct sockaddr但是我们要使用网络套接字中的struct sockaddr_in:

在这里插入图片描述

首先这个填充字段指的是:
在这里插入图片描述
in_addr类是指
在这里插入图片描述
在这里插入图片描述
是指
在这里插入图片描述
##表示拼接
将传入的参数与##后面的符号进行拼接
所以这个结构体内的宏代表的参数是sin_family

struct sockaddr_in local;
bzero(&local, sizeof(local)); // 指定的内存大小清零, 头文件是<strings.h>
                              // 也可以使用memset
// 告诉系统绑定网络通信的信息
local.sin_family = AF_INET; // 协议家族,s表示socket的意思
// in表示inet, 使用ipconfig查看会发现inet表示IP地址,
// 这的in也表示ip地址, 与上文的创建socket的参数1不同, 这边只是绑定信息
local.sin_port = htons(_port);
local.sin_addr.s_addr = inet_addr(_ip.c_str());

//我们自己写的是一个主机序列, 要转化为网络序列htons();
//主机转网络短整型, 同样在结构体内的是类型匹配的

在这里插入图片描述
对于我们的网络IP, 同样, 我们想要1. 4字节IP 2. 转成网络序列
在这里插入图片描述

将输入的字符串IP转化为点分十进制IP,并返回
在这里插入图片描述
结构体填完, 只是在当前的用户地址空间, 但是还没有设置到内核, 需要调用bind函数在这里插入图片描述
udp服务器接发消息, 它不面向连接:
收消息:

在这里插入图片描述
在这里插入图片描述发消息
在这里插入图片描述
至此已完成, 运行查看:
在这里插入图片描述

怎么验证:

在这里插入图片描述

n:number数字
a:all 所有的
u:udp
p:process进程

在这里插入图片描述

Client代码

创建套接字
在这里插入图片描述

客户端信息输入:
在这里插入图片描述

发送信息

在这里插入图片描述

接受消息
在这里插入图片描述

查看启动结果

信息查看
在这里插入图片描述

带上-n选项
在这里插入图片描述
127.0.0.1: 本地环回地址, 通常用于进行网路cs(client server)的测试
在这里插入图片描述

cs都在一台机器, 用于测试将信息自己发给自己是否正确

带上-p选项
在这里插入图片描述

输出的信息
在这里插入图片描述
客户端的端口号和ip自动变成了
在这里插入图片描述
客户端的首次运行, 在没有发消息时, 看不到client的相关网络信息:
只有在client发送一条信息之后, 才会看到client的相关信息
说明, 客户端bind是在首次发送数据的时候, 进行由内核随机bind

代码修正

1.获取内核分配给客户端的信息

在server模块中, client首次发送的消息会被recvfrom函数接受, 那么这个函数的后两个参数就是表示这个客户端的信息
所以, 打印需要的信息
在这里插入图片描述
再次运行查看:
在这里插入图片描述

127.0.0.1: 本地环回地址, 通常用于进行网路cs(client server)的测试

cs都在一台机器, 用于测试将信息自己发给自己是否正确

为了封装性, 将客户端信息进行封装:

在这里插入图片描述

在这里插入图片描述
本地测试完成
但是我们的服务器ip绑定只能是127.0.0.1本机, 如果是实际的本地机器, 本地ip也可以, 云服务器的ip不行, 因为云服务器的IP是提供商虚拟出来的公网Ip, 不能直接进行bind
当然, 也不推荐给服务器绑定固定的IP, 使用随机ip更合适, 这样就能实现IP的动态绑定, 不然, 只能绑定的固定的ip机器进行服务器的访问

2.修正不匹配ip不能访问的问题

在这里插入图片描述

固定ip才能访问
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

对应的main.cc文件内以及udpserver.hpp文件的所有的server的ip都要去除, 这边就不再演示

不同机器之间的通信

利用xftp将udp_client传给wsl的ubuntu机器进行演示

传过去默认没有可执行 chmod +x udp_client即可
这边演示两台机器上的相互通信, 原机是虚拟机ubuntu机器

现在在windows下使用wsl创建一台ubuntu机器, 两台机器ip不同
都是虚拟出来的ip

在这里插入图片描述

现在模拟在windows下的udp_client代码:

可以看到win和linux进行了通信
只不过win的汉字编码和Linux汉字的编码方式不同, 输入过去的汉字不能正确的回显, 这边只需要修改win的终端编码方式即可,这边就不再演示

对方发的命令也进行执行或者是打印

可以使用exec*系列进行使用, 不过这边利用popen函数
做两件事:
1.创建管道
2.识别字符指令, fork创建子进程并执行
在这里插入图片描述

可以利用FILE的文件指针, 返回command的结果
type可以利用r w r+等方式来进行处理我们的这个FILE

最后使用pclose进行关闭
打开失败就返回nullptr

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

udp tcp是支持全双工的, 所以我们可以实现一个聊天室

udp+线程池实现聊天室:
在线程池中, 任务现在是一个函数方法
在这里插入图片描述

在server启动的时候
首先进行, 将远端的信息接收进buf中,同时被填充的peer结构体中包含对应机器的port和ip
然后判断就收消息成功之后, 添加用户和用户输入的信息获取, 然后进行message的处理方法Route与当前对象, 当前客户端的信息, 当前的message绑定为task, 然后将这个task添加到线程池中

在这里插入图片描述

其中:
添加用户操作为看他是否在当前的用户列表中,不在,就添加
在这里插入图片描述

收到的message消息的处理方式是
在这里插入图片描述

将给定的消息message通过套接字sock发送给所有在线用户

然后在线程池中, 进行阻塞队列式的执行访问

然后对client进行多线程修改, 一个线程专门用于收消息, 一个线程专用于发消息, 主线程用于操作这个整体流程
在这里插入图片描述
整体没变, 实现模块化的划分:
在这里插入图片描述
聊天室实现成功, 但是输出和输入会混在一起. 再次修改
在linux中, 每个 终端是在/dev/pts 目录下有存在, 直接在这边输出信息也行, 方法有很多, 这边可以使用向/dev 输出的操作, 我们使用另一种方式
在输出消息的时候, 代码部分是向cerr打印, 那么我们可以利用管道, 在终端使用重定向实现输入输出的分别实现
1.创建管道
在这里插入图片描述
2.重定向输出
server端不变
在这里插入图片描述

演示
在这里插入图片描述

现在在回回显处进行修改代码, 以便确认身份
主要是在message进行修改,
在这里插入图片描述

然后输出的时候, 直接输出就行
在这里插入图片描述

结果演示:
在这里插入图片描述

就此, udp_socket完成


http://www.kler.cn/a/315345.html

相关文章:

  • 重构开发之道,Blackbox.AI为技术注入智能新动力
  • 卷积神经网络之Yolo详解
  • C++STL容器——map和set
  • 爬虫补环境案例---问财网(rpc,jsdom,代理,selenium)
  • 机器学习在医疗健康领域的应用
  • 学法减分交管12123模拟练习小程序源码前端和后端和搭建教程
  • 行阶梯形矩阵的定义,通过正例和反例说明如何判断一个矩阵是不是行阶梯形矩阵
  • 9月22日,每日信息差
  • 基于python+django+mysql+Nanodet检测模型的水稻虫害检测系统
  • 基于Python+SQLite的课程管理系统
  • Spring boot中常用注解解释
  • 汽车焊机数据通信:Profinet转Canopen网关的神奇连接
  • 新160个crackme - 062-syllogism-crackme1
  • GlusterFS 分布式文件系统
  • 初识 performance_schema:轻松掌握MySQL性能监控
  • 基于深度学习的因果关系建模
  • [论文笔记]MRRNET
  • 树和二叉树的概念以及结构
  • 关于IT行业
  • 智慧火灾应急救援航拍检测数据集(无人机视角)
  • 【编程底层原理】Java对象头的详细结构、锁机制及其优化技术,以及逃逸分析和JIT技术在性能优化中的作用
  • 无损转换:严选4个视频mkv转mp4格式的方法
  • Python青少年简明教程目录
  • MySQL 数据库备份与恢复详解
  • 1042 Shuffling Machine,1050 String Subtractio
  • uniapp自定义Tabbar教程