Socket套接字类编译测试

目录

类设计

类实现

测试

测试服务器

测试客户端

测试结果


这一节相当于整合了之前的一些东西,重新过了一遍,这个就显得相对之前的版本更加完善一点

类设计

// 套接字类
#define MAX_LISTEN 1024
class Socket
{private:int _sockfd;public:Socket();Socket(int fd);~Socket();// 创建套接字bool Create();// 绑定地址信息bool Bind(const std::string &ip, uint64_t port);// 开始监听bool Listen(int backlog = MAX_LISTEN);// 向服务器发起连接bool Connect(const std::string &ip, uint64_t port);// 获取新连接int Accept();// 接收数据ssize_t Recv(void* buf, size_t len, int flag = 0);  // 0 阻塞// 发送数据ssize_t Send(void* buf, size_t len, int flag = 0);// 关闭套接字void Close();// 创建一个服务器连接bool CreateServer(uint64_t port, const std::string &ip = "0.0.0.0"); // 接收全部// 创建一个客户端连接bool CreateClient(uint64_t port, const std::string &ip);// 设置套接字选项 -- 开启地址端口重用void ReuseAddress();// 设置套接字阻塞属性 -- 设置为非阻塞void NonBlock();
};  

类实现

#include <iostream>
#include <vector>
#include <cstdint>
#include <cassert>
#include <string>
#include <cstring>
#include <ctime>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>#define INF 0
#define DBG 1
#define ERR 2
#define LOG_LEVEL DBG#define LOG(level, format, ...)                                                             \do                                                                                      \{                                                                                       \if (level < LOG_LEVEL)                                                              \break;                                                                          \time_t t = time(nullptr);                                                           \struct tm *ltm = localtime(&t);                                                     \char tmp[32] = {0};                                                                 \strftime(tmp, 31, "%H:%M:%S", ltm);                                                 \fprintf(stdout, "[%s %s:%d] " format "\n", tmp, __FILE__, __LINE__, ##__VA_ARGS__); \} while (0)#define INF_LOG(format, ...) LOG(INF, format, ##__VA_ARGS__)
#define DBG_LOG(format, ...) LOG(DBG, format, ##__VA_ARGS__)
#define ERR_LOG(format, ...) LOG(ERR, format, ##__VA_ARGS__)// 缓冲区类
#define BUFFER_DEFAULT_SIZE 1024 // Buffer 默认起始大小
class Buffer
{
private:std::vector<char> _buffer; // 使用vector进行内存空间管理uint64_t _reader_idx;      // 读偏移uint64_t _writer_idx;      // 写偏移
public:Buffer() : _reader_idx(0), _writer_idx(0), _buffer(BUFFER_DEFAULT_SIZE) {}char *Begin() { return &*_buffer.begin(); }// 获取当前写入起始地址char *WirtePosition() { return Begin() + _writer_idx; }// 获取当前读取起始地址char *ReadPosition() { return Begin() + _reader_idx; }// 获取缓冲区末尾空闲空间大小--写偏移之后的空闲空间, 总体空间大小减去写偏移uint64_t TailIdleSize() { return _buffer.size() - _writer_idx; }// 获取缓冲区起始空闲空间大小--读偏移之前的空闲空间uint64_t HeadIdleSize() { return _reader_idx; }// 获取可读数据大小 = 写偏移 - 读偏移uint16_t ReadAbleSize() { return _writer_idx - _reader_idx; };// 将读偏移向后移动void MoveReadOffset(uint64_t len){// 向后移动的大小, 必须小于可读数据大小assert(len <= ReadAbleSize());_reader_idx += len;}// 将写偏移向后移动void MoveWriteOffset(uint64_t len){// 向后移动的大小,必须小于当前后边的空闲空间大小assert(len <= TailIdleSize());_writer_idx += len;}// 确保可写空间足够(整体空闲空间够了就移动数据,否则就扩容)void EnsureWriteSpace(uint64_t len){// 如果末尾空闲空间大小足够,直接返回if (len <= TailIdleSize()){return;}// 末尾空闲空间不够,则判断加上起始位置的空闲空间大小是否足够,够了就将数据移动到起始位置if (len <= TailIdleSize() + HeadIdleSize()){// 将数据移动到起始位置uint64_t rsz = ReadAbleSize();                            // 把当前数据大小先保存起来std::copy(ReadPosition(), ReadPosition() + rsz, Begin()); // 把可读数据拷贝到起始位置_reader_idx = 0;                                          // 将读偏移归0_writer_idx = rsz;                                        // 将写位置置为可读数据大小, 因为当前的可读数据大小就是写偏移量}else{// 总体空间不够,则需要扩容,不移动数据,直接给写偏移之后扩容足够空间即可_buffer.resize(_writer_idx + len);}}// 写入数据void Write(const void *data, uint64_t len){// 1.保证有足够空间, 2.拷贝数据进去EnsureWriteSpace(len);const char *d = (const char *)data;std::copy(d, d + len, WirtePosition());}void WirteAndPush(const void *data, uint64_t len){Write(data, len);MoveWriteOffset(len);}void WriteString(const std::string &data){return Write(data.c_str(), data.size());}void WriteStringAndPush(const std::string &data){WriteString(data);MoveWriteOffset(data.size());}void WriteBuffer(Buffer &data){return Write(data.ReadPosition(), data.ReadAbleSize());}void WirteBufferAndPush(Buffer &data){WriteBuffer(data);MoveWriteOffset(data.ReadAbleSize());}// 读取数据void Read(void *buf, uint64_t len){// 要求获取的数据大小必须小于可读数据大小assert(len <= ReadAbleSize());std::copy(ReadPosition(), ReadPosition() + len, (char *)buf);}void ReadAndPop(void *buf, uint64_t len){Read(buf, len);MoveReadOffset(len);}std::string ReadAsString(uint64_t len){// 要求获取的数据大小必须小于可读数据大小assert(len <= ReadAbleSize());std::string str;str.resize(len);Read(&str[0], len); // 这里不直接用str.c_str()的原因是,这个的返回值是const类型return str;}std::string ReadAsStringAndPop(uint64_t len){assert(len <= ReadAbleSize());std::string str = ReadAsString(len);MoveReadOffset(len);return str;}char *FindCRLF(){char *res = (char *)memchr(ReadPosition(), '\n', ReadAbleSize());return res;}// 这种情况针对的是,通常获取一行数据std::string GetLine(){char *pos = FindCRLF();if (pos == nullptr)return "";// +1 是为了把换行字符也取出来return ReadAsString(pos - ReadPosition() + 1);}std::string GetLineAndPop(){std::string str = GetLine();MoveReadOffset(str.size());return str;}// 清空缓冲区void Clear(){// 只需要将偏移量归0即可_reader_idx = 0;_writer_idx = 0;}
};// 套接字类
#define MAX_LISTEN 1024
class Socket
{
private:int _sockfd;public:Socket() : _sockfd(-1) {}Socket(int fd) : _sockfd(fd) {}~Socket() { Close(); };// 创建套接字bool Create(){// int socket(int domain, int type, int protocol)_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (_sockfd < 0){ERR_LOG("CREATE SOCKET FAILED!");return false;}return true;}// 绑定地址信息bool Bind(const std::string &ip, uint64_t port){struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip.c_str());socklen_t len = sizeof(struct sockaddr_in);// int bind(int sockfd, struct sockaddr* addr, socklen_t len)int ret = bind(_sockfd, (struct sockaddr *)&addr, len);if (ret < 0){ERR_LOG("BIND ADDRESS FAILED!");return false;}return true;}// 开始监听bool Listen(int backlog = MAX_LISTEN){// int listen(int backlog)int ret = listen(_sockfd, backlog);if (ret < 0){ERR_LOG("SOCKET LISTEN FAILED!");return false;}return true;}// 向服务器发起连接bool Connect(const std::string &ip, uint64_t port){struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip.c_str());socklen_t len = sizeof(struct sockaddr_in);// int connect(int sockfd, struct sockaddr* addr, socklen_t len)int ret = connect(_sockfd, (struct sockaddr *)&addr, len);if (ret < 0){ERR_LOG("CONNECT SERVER FAILED!");return false;}return true;}// 获取新连接int Accept(){// int accept(int sockfd, struct sockaddr *addr, socklen_t *len);int newfd = accept(_sockfd, nullptr, nullptr);if (newfd < 0){ERR_LOG("SOCKET ACCEPT FAILED!");return -1;}return newfd;}// 接收数据ssize_t Recv(void *buf, size_t len, int flag = 0) // 0 阻塞{// ssize_t recv(int sockfd, void *buf, size_t len, int flag)ssize_t ret = recv(_sockfd, buf, len, flag);if (ret <= 0){// EAGAIN 当前的接收缓冲区中没用数据了,在非阻塞的情况下才有这个错误// EINTR 表示当前socket的阻塞等待,被信号打断了if (errno == EAGAIN || errno == EINTR){return 0; // 表示这次没用接收到数据}ERR_LOG("SOCKET RECV FAILED!");return -1;}return ret; // 实际接收的数据长度}ssize_t NonBlockRecv(void *buf, size_t len){return Recv(buf, len, MSG_DONTWAIT); // MSG_DONTWAIT 表示当前接收为非阻塞}// 发送数据ssize_t Send(const void *buf, size_t len, int flag = 0){// ssize_t send(int sockfd, void *data, size_t len, int flag)ssize_t ret = send(_sockfd, buf, len, flag);if (ret < 0){ERR_LOG("SOCKET SEND FAILED!");return -1;}return ret; // 实际发送的数据长度}ssize_t NonBlockSend(void *buf, size_t len){return Send(buf, len, MSG_DONTWAIT); // MSG_DONTWAIT 表示当前接收为非阻塞}// 关闭套接字void Close(){if (_sockfd != -1){close(_sockfd);_sockfd = -1;}}// 创建一个服务器连接bool CreateServer(uint64_t port, const std::string &ip = "0.0.0.0", bool block_flag = false) // 接收全部{// 1.创建套接字 2.绑定地址 3.开始监听 4.设置非阻塞 5.启动地址重用if (Create() == false)return false;if (block_flag) // 默认阻塞NonBlock();if (Bind(ip, port) == false)return false;if (Listen() == false)return false;ReuseAddress();return true;}// 创建一个客户端连接bool CreateClient(uint64_t port, const std::string &ip){// 1.创建套接字 2.指向连接服务器if (Create() == false)return false;if (Connect(ip, port) == false)return false;return true;}// 设置套接字选项 -- 开启地址端口重用void ReuseAddress(){// int setsockopt(int fd, int level, int optname, void *val, int vallen)int val = 1;setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(int)); // 地址val = 1;setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, (void *)&val, sizeof(int)); // 端口号}// 设置套接字阻塞属性 -- 设置为非阻塞void NonBlock(){// int fcntl(int fd, int cmd, .../*arg*/)int flag = fcntl(_sockfd, F_GETFD, 0);fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);}
};

测试

注意头文件的包含,我这里是直接使用相对位置,所以这里的头文件包含是这个样子的

测试服务器

#include "../source/server.hpp"int main()
{Socket lst_sock;bool ret = lst_sock.CreateServer(8500);while (1){int newfd = lst_sock.Accept();if(newfd < 0){continue;}Socket cli_sock(newfd);char buf[1024] = {0};int ret = cli_sock.Recv(buf, 1024);if(ret < 0){cli_sock.Close();continue;}cli_sock.Send(buf, ret);cli_sock.Close();}lst_sock.Close();return 0;
}

测试客户端

#include "../source/server.hpp"int main()
{Socket cli_sock;cli_sock.CreateClient(8500, "127.0.0.1");std::string str = "hello qingfengyuge!";cli_sock.Send(str.c_str(), str.size());char buf[1024] = {0};cli_sock.Recv(buf, 1023);DBG_LOG("%s", buf);return 0;
}

测试结果

符合预期

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

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

相关文章

PHP面试知识点--echo、print、print_r、var_dump区别

echo、print、print_r、var_dump 区别 echo 输出单个或多个字符&#xff0c;多个使用逗号分隔无返回值 echo "String 1", "String 2";print 只可以输出单个字符返回1&#xff0c;因此可用于表达式 print "Hello"; if ($expr && pri…

闲人闲谈PS之五十三——离散制造中的魔鬼--物料套裁

惯例闲话&#xff1a;最近和老婆大人商议买车事宜&#xff0c;闲人以为会陷入买油车还是电车的纠结&#xff0c;没想到老婆大人无比坚定&#xff0c;买电车。在买车这方面&#xff0c;老婆的想法居然比闲人超前。闲人对车定位在代步工具&#xff0c;2年前&#xff0c;对车还是印…

SAP下载word

事务代码&#xff1a;STRANS 启动转换器 步骤 1. 将参数填入模板&#xff0c;并另存为word 2003 xml文档 2.使用网页打开xml文档&#xff0c;并将xml拷贝到转换器tt:template中&#xff0c;添加参数 3.替换参数&#xff0c;部分xml可能存在错误或者跑偏根据实际情况检查修改 …

为什么游戏APP选择不上架?

游戏APP选择不上架的原因有很多&#xff0c;主要包括以下几个方面&#xff1a; 节省成本&#xff1a;自己运营游戏可以省去向应用商店缴纳的分成费用&#xff0c;降低运营成本。避免与竞争对手比较&#xff1a;有些公司不希望自己的游戏在应用商店中与竞争对手的产品进行比较&…

洛谷 P1980 [NOIP2013 普及组] 计数问题

题目背景 NOIP2013 普及组 T1 题目描述 试计算在区间 1 到 n 的所有整数中&#xff0c;数字 x&#xff08;0≤x≤9&#xff09;共出现了多少次&#xff1f;例如&#xff0c;在 1 到 11 中&#xff0c;即在 1,2,3,4,5,6,7,8,9,10,11 中&#xff0c;数字 1 出现了 4 次。 输入…

Ubuntu18.04安装Matlab流程笔记

提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 Ubuntu18.04 安装Matlab流程 下载安装包和破解文件安装Matlab注册并运行 下载安装包和破解文件 matlabR2019A源码 提取码:2ztb 下载的Linux matlab2018a文件夹内有三个文件&#xff1a; # 解压Matlab201…

<网络安全>《15 移动安全管理系统》

1 概念 移动安全管理系统&#xff0c;MSM&#xff0c;Mobile security management,提供大而全的功能解决方案&#xff0c;覆盖了企业移动信息化中所涉及到安全沙箱、数据落地保护、威胁防护、设备管理、应用管理、文档管理、身份认证等各个维度。移动安全管理系统将设备管理和…

freeswitch对接FunASR实时语音听写

1、镜像启动 通过下述命令拉取并启动FunASR软件包的docker镜像&#xff1a; sudo docker pull \registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.7 mkdir -p ./funasr-runtime-resources/models sudo docker run -p 10096:10095 -i…

Unity_Playable工具使用

Unity_Playable工具使用 目录 Unity_Playable工具使用 1. Default Playables(Timeline扩展) 2. PlayableGraph Visualizer&#x

armbian修改docker目录到硬盘

玩客云自带内存8G&#xff0c;根目录很快就满了&#xff0c;这里调整docker的目录到硬盘上/sda1。 docker info|grep "Docker Root Dir:" Docker Root Dir:/var/lib/docker 查看docker 默认目录在哪里 Docker 版本 > v17.05.0 docker -v Docker version 25.0.…

elementUI中el-tree组件单选没有复选框时,选中、current-node-key高亮、刷新后保留展开状态功能的实现

目录 一、代码实现1. 属性了解 &#xff08;[更多](https://element.eleme.cn/#/zh-CN/component/tree)&#xff09;2. 实现步骤3.代码示例 二、 效果图 一、代码实现 1. 属性了解 &#xff08;更多&#xff09; node-key 每个树节点用来作为唯一标识的属性&#xff0c;整棵树…

Linux 磁盘空间占用率100%的排查

&#x1f4d1;前言 使用 Linux 操作系统时&#xff0c;可能会遇到磁盘空间不足的错误&#xff0c;这种错误通常会导致系统运行缓慢或崩溃。本文将介绍磁盘排查的方法。⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1…

rust gui开发框架选择

作为一个系统编程强大语言&#xff0c;怎么能少得了图形界面的开发 实际上写这篇前我也不知道&#xff0c;于是我问了ai大模型&#xff0c;文心3.5和chatgpt4.0 答案实际上不能满意&#xff0c;最后我做了下筛选 参考博文&#xff1a; rust开发环境配置&#xff1a;链接 一、…

使用websocket建立长链接实现用户点对点即时通讯

WebSocket是一种网络传输协议&#xff0c;位于OSI模型的应用层&#xff0c;设计用于在单个TCP连接上进行全双工通信。 WebSocket的主要特点包括&#xff1a; 全双工通信&#xff1a;WebSocket允许服务器和客户端之间进行双向实时通信。这与传统的HTTP请求不同&#xff0c;后者…

MacOS安装JDK+Maven+Idea插件+nvm等

Java安装环境(MacOS)JDKMavenIdea插件nvm等 背景&#xff1a;新机安装开发环境发现需要找很多文章&#xff0c;&#xff0c;&#xff0c;&#xff0c;这里一篇文章安装所有环境 文章目录 Java安装环境(MacOS)JDKMavenIdea插件nvm等一、安装JDK①&#xff1a;下载②&#xff1a;…

阿里云a10GPU,centos7,cuda11.2环境配置

Anaconda3-2022.05-Linux-x86_64.sh gcc升级 centos7升级gcc至8.2_centos7 yum gcc8.2.0-CSDN博客 paddlepaddle python -m pip install paddlepaddle-gpu2.5.1.post112 -f https://www.paddlepaddle.org.cn/whl/linux/mkl/avx/stable.html 报错 ImportError: libssl.so…

C++ pair+map+set+multimap+multiset+AVL树+红黑树(深度剖析)

文章目录 1. 前言2. 关联式容器3. pair——键值对4. 树形结构的关联式容器4.1 set4.1.1 set 的介绍4.1.2 set 的使用 4.2 map4.2.1 map 的介绍4.2.2 map 的使用 4.3 multiset4.3.1 multiset 的介绍4.3.2 multiset 的使用 4.4 multimap4.4.1 multimap 的介绍4.4.2 multimap 的使…

Latex学习记录

目录 1.Latex各种箭头符号总结 2.[Latex]公式编辑&#xff0c;编号、对齐 3.Latex公式编号: 多行公式多编号&#xff0c;多行公式单编号 4.LaTex中输入空格以及换行 1.Latex各种箭头符号总结 箭头符号 - ➚ (piliapp.com)https://cn.piliapp.com/symbol/arrow/Latex各种箭头…

Windows巧用Git实现笔记自动备份

随着数字时代的来临&#xff0c;数据安全和备份变得尤为重要。特别是对于经常写笔记或需要整理资料的人来说&#xff0c;如何自动备份这些宝贵的信息成为了一个关键问题。今天&#xff0c;我们将探讨如何使用Git在Windows上实现笔记的自动备份。 一、Git简介 Git是一个分布式…

Flutter 开发3:创建第一个Flutter应用

Step 1: 安装Flutter 1.1 下载Flutter SDK 首先&#xff0c;你需要访问Flutter官方网站下载最新的Flutter SDK。选择适合你操作系统的安装包。 $ cd ~/development $ unzip ~/Downloads/flutter_macos_2.2.3-stable.zip1.2 更新环境变量 接下来&#xff0c;你需要将Flutter…