【Linux】网络基础_3
文章目录
- 十、网络基础
- 5. socket编程
- socket 常见API
- sockaddr结构
- 简单的UDP网络程序
- 未完待续
十、网络基础
5. socket编程
socket 常见API
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockaddr结构
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX DomainSocket. 然而, 各种网络协议的地址格式并不相同。
IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。
我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型,端口号,IP地址。
简单的UDP网络程序
我们需要我们之前写的日志程序:
Log.hpp:
#pragma once#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include "LockGuard.hpp"// 宏定义,用于定义日志格式
#define LOG(level, format, ...) do{LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__);}while (0)
// 将日志输入到文件
#define EnableFile() do{gIsSave = true;}while (0)
// 将日志输出到显示器
#define EnableScreen() do{gIsSave = false;}while (0)bool gIsSave = false;
// 日志文件名
const std::string logname = "log.txt";// 枚举日志级别
enum Level
{DEBUG = 0,INFO,WARNING,ERROR,FATAL
};// 保存日志到文件
void SaveFile(const std::string &filename, const std::string &message)
{std::ofstream out(filename, std::ios::app);if (!out.is_open()){return;}out << message;out.close();
}// 日志级别转字符串
std::string LevelToString(int level)
{switch (level){case DEBUG:return "Debug";case INFO:return "Info";case WARNING:return "Warning";case ERROR:return "Error";case FATAL:return "Fatal";default:return "Unknown";}
}// 获取当前时间字符串
std::string GetTimeString()
{time_t curr_time = time(nullptr);struct tm *format_time = localtime(&curr_time);if (format_time == nullptr)return "None";char time_buffer[1024];snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",format_time->tm_year + 1900,format_time->tm_mon + 1,format_time->tm_mday,format_time->tm_hour,format_time->tm_min,format_time->tm_sec);return time_buffer;
}// 日志锁,同一时刻只能写一个日志
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;// 日志信息
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{// 日志级别std::string levelstr = LevelToString(level);// 时间std::string timestr = GetTimeString();// 进程idpid_t selfid = getpid();// 日志内容char buffer[1024];va_list arg;va_start(arg, format);vsnprintf(buffer, sizeof(buffer), format, arg);va_end(arg);// 日志格式化std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +"[" + std::to_string(selfid) + "]" +"[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer;LockGuard lockguard(&lock);// 输出日志if (!issave){std::cout << message;}else{SaveFile(logname, message);}
}
封装的RAII模式的锁:
LockGuard.hpp:
#ifndef __LOCK_GUARD_HPP__
#define __LOCK_GUARD_HPP__#include <iostream>
#include <pthread.h>class LockGuard
{
public:// 构造函数加锁LockGuard(pthread_mutex_t *mutex):_mutex(mutex){pthread_mutex_lock(_mutex);}// 析构函数解锁~LockGuard(){pthread_mutex_unlock(_mutex);}
private:pthread_mutex_t *_mutex;
};#endif
服务端代码:
UdpServer.hpp:
#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"
#include "InetAddr.hpp"enum
{SOCKET_ERROR = 1,BIND_ERROR,USAGE_ERROR
};const static int defaultfd = -1;class UdpServer
{
public:UdpServer(uint16_t port):_sockfd(defaultfd),_port(port),_isrunning(false){}void InitServer(){// 创建 UDP socket 套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno);exit(SOCKET_ERROR);}LOG(INFO, "socket create success, sockfd: %d\n", _sockfd);struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;// 端口号要经过网络传输,需要转换成网络字节序local.sin_port = htons(_port);local.sin_addr.s_addr = 0;// 绑定本地地址int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));if (n < 0){LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);exit(BIND_ERROR);}LOG(INFO, "socket bind success\n");}void Start(){// 启动服务器_isrunning = true;while (true){// 接收数据char buffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);if (n > 0){// 收到数据,打印buffer[n] = '\0';InetAddr addr(peer);LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), buffer);sendto(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&peer, len);}// sleep(1);// LOG(DEBUG, "server is running...\n");}_isrunning = false;}~UdpServer(){}
private:int _sockfd;// std::string _ip;// 服务器所用端口号uint16_t _port;bool _isrunning;
};
客户端代码:
UdpClient.hpp:
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " local_ip local_prot\n" << std::endl;
}int main(int argc, char* argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}// 获取本地IP地址和端口号std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;}// 构建目标主机的socket信息struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());std::string message;// 循环发送消息while (true){// 输入消息std::cout << "Please Enter# ";std::getline(std::cin, message);sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));// 接收消息struct sockaddr_in peer;socklen_t len = sizeof(peer);char buffer[1024];ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);if (n > 0){std::cout << "server echo# " << buffer <<std::endl;}}return 0;
}
发送者数据:
InetAddr.hpp:
#pragma once#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{
private:void GetAddress(std::string *ip, uint16_t *port){*port = ntohs(_addr.sin_port);*ip = inet_ntoa(_addr.sin_addr);}public:InetAddr(const struct sockaddr_in &addr) : _addr(addr){GetAddress(&_ip, &_port);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}~InetAddr(){}
private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;
};
Main.cc:
#include <iostream>
#include <memory>
#include "UdpServer.hpp"void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " local_prot\n" << std::endl;
}int main(int argc, char* argv[])
{if (argc != 2){Usage(argv[0]);exit(USAGE_ERROR);}EnableScreen();// std::string local_ip = argv[1];uint16_t port = std::stoi(argv[1]);std::unique_ptr<UdpServer> usvr(new UdpServer(port));// 初始化服务器usvr->InitServer();// 启动服务器usvr->Start();return 0;
}
Makefile:
.PHONY:all
all:udpserver udpclientudpserver:Main.ccg++ -o $@ $^ -std=c++11
udpclient:UdpClient.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f udpserver udpclient
结果:
服务端输入:./udpserver 端口号 即可
客户端输入:./udpclient 服务器的ip地址(若是本地互联,输入0即可) 端口号 即可
成功通信!
未完待续
相关文章:

【Linux】网络基础_3
文章目录 十、网络基础5. socket编程socket 常见APIsockaddr结构简单的UDP网络程序 未完待续 十、网络基础 5. socket编程 socket 常见API // 创建 socket 文件描述符 (TCP/UDP, 客户端 服务器) int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服…...

C++之从C过渡(上)
C之从C过渡 前言 暂时告别C语言,我们走进C。对于有C语言基础,初学C的我们来说,在正式学习C的主体内容之前,我们需要先有一个过渡,本文中会总结过渡需要了解的零散知识,主要是语法。 正文 C的第一个程序 …...
MongoDB 100问
基础问题 1. 什么是MongoDB? MongoDB是一种面向文档的NoSQL数据库,使用BSON(二进制JSON)格式存储数据。它支持动态模式设计,具有高性能、高可用性和易扩展性。 2. MongoDB和传统关系型数据库的区别是什么?…...
Arduino ESP32使用 HardwareSerial创建一个任意串口
文章目录 前言使用 `HardwareSerial` 创建任意串口创建 `HardwareSerial` 对象示例代码`begin` 函数总结前言 在 Arduino 项目中,串口通信是一种常见且重要的通信方式。ESP32 作为一款功能强大的微控制器,提供了多个 UART(通用异步收发传输器)接口,允许用户灵活地进行串口…...

数据中台建设之数据存储
目录 一、技术选型需要明确的问题 二、技术选型需要考虑的几个方面 2.1 数据规模 2.2 数据生产方式 2.3 数据应用方式 三、技术选型的场景分析 3.1 概述 3.2 在线与离线 3.2.1 在线存储 3.2.2 离线存储 3.3 OLTP与OLAP 3.3.1 OLTP 3.3.2 OLAP 3.3.3 OLTP与OLAP的关…...

最常见的AI大模型总结
前言:大模型可以根据其主要的应领域和功能,可以分类为“文生文”(Text-to-Text)、“文生图”(Text-to-Image)和“文生视频”(Text-to-Video),都是基于自然语言处理&#…...
源码安装docker和docker-compose
前言 前提条件:内核要求 目前,CentOS 仅发行版本中的内核支持 Docker。 Docker 运行在 CentOS 7 上,要求系统为64位、系统内核版本为3.10 以上。 Docker 运行在 CentOS-6.5 或更高的版本的 CentOS 上,要求系统为64位、系统内核版…...

Java、PHP、Node 操作 MySQL 数据库常用方法
一、Java 操作 MySQL 数据库 1、Java 连接 MySQL 数据库 1. 使用 JDBC 驱动程序连接 使用这种方式,首先需要导入 MySQL 的 JDBC 驱动程序依赖,然后通过 Class.forName() 方法加载驱动程序类。其创建连接的过程相对直接,只需提供准确的数据库…...

nVisual分享社区正式上线啦!
nVisual分享社区正式上线啦! 访问地址:分享社区 nVisual是耐威迪基于数字孪生技术物联网技术开发的一款基础设施数字孪生软件工具,主要实现OSP室外与ISP室内基础设施的规划、记录、分析的可视化管理。 规划:nVisual可视化、智能化…...

4.5.门控循环单元GRU
门控循环单元GRU 对于一个序列,不是每个观察值都是同等重要的,可能会遇到一下几种情况: 早期观测值对预测所有未来观测值都具有非常重要的意义。 考虑极端情况,第一个观测值包含一个校验和,目的是在序列的末尾辨别…...
10种 Python数据结构,从入门到精通
今天我们将深入探讨 Python 中常用的数据结构,帮助你从基础到精通。每种数据结构都有其独特的特点和适用场景,通过实际代码示例和生活中的比喻,让你更容易理解这些概念。 学习数据结构的三个阶段 1、掌握基本用法:使用这些数据结…...

【AI】人工智能时代,程序员如何保持核心竞争力?
目录 程序员在AI时代的应对策略1. 引言2. AI在编程领域的影响2.1 AI辅助编程工具的现状2.2 AI对编程工作的影响2.3 程序员的机遇与挑战 3. 深耕细作:专注领域的深度学习3.1 专注领域的重要性3.2 深度学习的策略3.2.1 选择合适的领域3.2.2 持续学习和研究3.2.3 实践与…...

WPF学习(3)- WrapPanel控件(瀑布流布局)+DockPanel控件(停靠布局)
WrapPanel控件(瀑布流布局) WrapPanel控件表示将其子控件从左到右的顺序排列,如果第一行显示不了,则自动换至第二行,继续显示剩余的子控件。我们来看看它的结构定义: public class WrapPanel : Panel {pub…...

【python】Python中实现定时任务常见的几种方式原理分析与应用实战
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...

老公请喝茶,2024年老婆必送老公的养生茶,暖暖的很贴心
在这个快节奏的时代,每个人都在为生活奔波,而家的温馨与关怀,成了我们最坚实的后盾。随着2024年的已经过半,作为妻子,你是否也在寻找一份特别的礼物,来表达对老公深深的爱意与关怀?在这个充满爱…...
3d打印相关资料
模型库 拓竹makerworld爱给...

MySQL1 DDL语言
安装与配置 官网: MySQL :: Download MySQL Installer 阿里云: MySQL8 https://www.alipan.com/s/auhN4pTqpRp 点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线查看,视频原画倍速…...

el-tree懒加载状态下实现搜索筛选(纯前端)
1.效果图 (1)初始状态 (2)筛选后 2.代码 <template><div><el-inputplaceholder"输入关键字进行过滤"v-model"filterText"input"searchValue"></el-input><el-tree…...

NLP——Transfromer 架构详解
Transformer总体架构图 输入部分:源文本嵌入层及其位置编码器、目标文本嵌入层及其位置编码器 编码器部分 由N个编码器层堆叠而成 每个编码器层由两个子层连接结构组成 第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接 第二个子层连接结构包…...

大模型算法面试题(二十)
本系列收纳各种大模型面试题及答案。 1、描述Encoder和Decoder中Attention机制的不同之处 Encoder和Decoder中的Attention机制在自然语言处理(NLP)和序列到序列(Seq2Seq)模型中扮演着重要角色,它们虽然都利用了Attent…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...