C++ 网络套接字编程 tcp udp

2024-06-19 13:12
文章标签 c++ 编程 udp 网络 tcp 接字

本文主要是介绍C++ 网络套接字编程 tcp udp,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 网络通信的本质
    • tcp 和 udp 协议
    • 网络字节序
    • 网络主机数据转化接口
  • udp 通信
    • 服务端逻辑
    • 客户端逻辑
  • TCP 通信
    • 服务端程序编写步骤
    • 客户端程序编写步骤
  • 两种通信程序代码
    • udp服务端程序编写
    • udp 客户端程序编写
    • tcp 服务端程序编写
    • tcp 客户端程序编写

在这里插入图片描述

网络通信的本质

网络之间的通信,本质上就是进程间通信

对双方主机的两个进程而言,需要先将数据发送到对方的主机(ip地址),再找到指定的进程(port:端口号),就能实现通信

ip地址用来标识互联网中唯一的一台主机;port端口号用来标识该指定机器中进程的唯一性

那么(ip, port) 则可以用来表示互联网中唯一一个进程,ip + port 也叫网络套接字 socket

如何理解port:
一个端口号和一个进程相绑定,一个进程可以绑定多个端口号,反之则不可以。
那么为什么不用进程pid来表示网络中进程的唯一性呢?
为了其他的进程模块和网络进行解耦(万一pid的规则变化,网络部分也不受影响),port是专门用于网络通信的

tcp 和 udp 协议

tcp 协议常用于可靠通信,适用于对数据要求比较高的场景(如游戏,传输重要文件等),复杂
udp 协议用于不可靠通信,适用于允许数据偶尔出现差错的场景(如体育赛事直播等),简单,快

这两个协议没有好坏之分,只是应用场景不同,如果不确定使用哪个的时候,就要 tcp,毕竟复杂一点比丢包好

网络字节序

机器有大小端之分,大小端机器存储数据方式不同。大端是“正着存储”的,可读性较好,因此在网络传输时规定,所以到达网络的数据,必须时大端存储的,因此,如果是小端机,收发数据到网络时需要先转化为大端

网络主机数据转化接口

ip 地址为4个字节,使用 uint32_t,port 为2个字节,使用 uint_16

htonl、htons 是转网络,ntohl、ntohs 是转主机数据,使用这些接口可以自动识别机器的大小端,并将数据转化为需要的大小端数据。

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uintl6_t ntohs(uint16_t netshort);

socket 创建 udp 套接字接口

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器) 
int socket(int domain, int type, int protocol); 
int socket(AF_INET, SOCK_DGRAM, 0); // 创建网络套接字, 成功返回文件描述符, 失败返回 -1
// AF_INET 说明协议家族使用的是ipv4, SOCK_DGRAM表示当前创建的是 udp套接字,本质是创建文件
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

填充 sockaddr_in 结构(套接字信息)

struct sockaddr_in local;
bzero(&local, sizeof(local));
local.sin_family = AF_INEF; // 设置协议家族
local.sin_port = htons(_port); // 将端口号转化为网络字节序列并赋值给 sockaddr_in 参数
local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 使用 inet_addr 接口,变为4字节网络序列

服务器收发消息

recvform(); //收消息
sendto(); //发消息 

查看网络状态 可以查看启动的服务器的信息

sudo netstat -anup

udp 通信

首先,要有一个客户端,一个服务端。这两台机器的通信可以看作是两个在这台机器上的进程进行
通信,udp 是全双工通信,因此两个进程可以互发消息,不会相互影响。

服务端逻辑

服务端的主体逻辑可以封装成一个类对象,主函数中通过调用类中的 Init 函数和 Start 函数来实现服务器的初始化和启动。

首先,Init 类内函数中,先创建 socket 套接字,再填充 sockaddr_in 结构,sockaddr_in是网络通信相关的结构体,有 ip 地址,端口号,协议家族这几个字段,然后将 socket 套接字和 sockaddr_in 结构体 bind 绑定(注意,不是C++那个bind),绑定后,所谓的互联网中唯一的进程就出现了!

然后 Start 类内函数中,设置一个死循环,定义一个缓冲区,并定义一个 sockaddr_in 结构体变量,sockaddr_in 结构体变量用来存储收到的消息的网络地址,缓冲区用于存放收到的消息。通过 recvfrom 函数收到来自另一个互联网中唯一进程的消息后,如果 recvfrom 函数接受成功,就可以对消息做处理,然后通过 sendto 函数,将消息转发给对面的主机,对面主机的网络地址,就是 recvfrom 函数收到的存储在 sockaddr_in 结构体中的网络地址!

客户端逻辑

客户端的逻辑比较简单,就是拿到服务端的 ip 地址和端口号后,和服务端进行通信。如果通信的形式是收->发->收->发->收…这种收到消息后再发消息,发消息后才能收消息的情况,那么只需要定义一个死循环即可,先发消息,然后检测是否发送成功,成功了才收消息这种的

![[Pasted image 20240608100519.png]]

也可以使用多线程,一个线程只负责死循环发消息,一个线程只负责死循环收消息,即可实现 qq、微信式通信

![[Pasted image 20240608100201.png]]
这大概就是udp通信写程序的基本逻辑

TCP 通信

TCP 套接字

Tcp 通信与 Udp不同的是,Tcp要先建立连接才能通信,因此与 dup 服务端不同的是,tcp要有一个 listensock与本地网络信息绑定,还要有一个普通socket 来与 client 通信

服务端程序编写步骤

  • 创建listensocket, 它是 file fd, 本质是文件描述符
  • 填充本地网络信息(初始化 struct sockaddr_in)并bind listensocket 和本地网络信息
  • 设置socket为监听状态,tcp特有 listen(_listensock, default_backlog)
  • 获取连接,用 listensocket 来获取连接,定义一个 socket 用来接收客户端的信息,再定义一个网络套接字信息,accept 接收成功后,socket 将自动与网络信息绑定
  • 提供服务,建立连接成功后,用 read 读 socket,即可接收客户端信息,用 write 往 socket 中写,即可向客户端发送信息
  • 规定一个端口号,就能启动服务器
// 获取连接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = accept(_listensock, struct sockaddr*(&peer), &len);// 填充网络信息时,ip地址的处理优化:
// p:process(进程), n(网络) -- 不太准确,但是好记忆
inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // 1. 字符串ip->4字节IP 2. 网络序列

客户端程序编写步骤

  • 先用服务端的ip 地址和它开放的端口号启动客户端
  • 创建 socket 并初始化
  • 填写 struct sockaddr_in 套接字信息
  • 用 sockfd 建立连接 connect
  • 收发信息用 read 和 write 向 sockfd 读写就行

如果要与多个客户端建立通信,则可使用多进程的方式:每检测到和一个客户端建立连接,就 fork 一个子进程,让子进程去与这个新建立的客户端通信(子进程继承父进程的文件描述符,但每个进程的文件描述符之间是独立的)

当然,也可以采用多线程,只需要将次级进程设置为分离即可!

两种通信程序代码

udp服务端程序编写

#pragma once
#include <iostream>
#include <cstring>
#include <string>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unordered_map>
#include <functional>
#include <pthread.h>
#include <strings.h>
#include <unistd.h>#include "nocopy.hpp"
#include "Comm.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"using task_t = std::function<void()>;const static int defaultport = 8888;
const static int defaultfd = -1;
// using func_t = std::function<std::string(std::string)>; // 定义了一个参数为 string, 返回值也为 string 的函数 
// 继承nocopy, 防止udpserver 被拷贝,因为基类中已经删除了拷贝构造和赋值
class UdpServer : public nocopy
{
public:UdpServer(u_int16_t port = defaultport): _port(port), _sockfd(defaultfd){pthread_mutex_init(&_user_mutex, nullptr);}void Init() // 初始化服务器{// 1. 创建 socket 文件细节 _sockfd = socket(AF_INET, SOCK_DGRAM, 0);std::cout << "sockfd: " << _sockfd << std::endl;if (_sockfd < 0){lg.LogMessage(Fatal, "sock errr: %d %s\n", errno, strerror(errno));exit(Socket_Err);}lg.LogMessage(Info, "sock success, sockfd: %d\n", _sockfd);struct sockaddr_in local;memset(&local, 0, sizeof local);local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY; // ip 设置为 0// local.sin_addr.s_addr = inet_addr(_ip.c_str()); // inet_addr 可以将点分十进制的ip地址转化为四字节网络字节序列int n = ::bind(_sockfd, (struct sockaddr*)(&local), (socklen_t)sizeof(local)); // 绑定到内核中, 成功就返回 0if (n != 0){lg.LogMessage(Fatal, "bind errr: %d %s\n", errno, strerror(errno));exit(Bind_Err);}ThreadPool<task_t>::GetInstance()->Start();}void Map(){// 添加用户即可 ..}void AddOnlineUser(InetAddr addr) // 添加用户 {LockGuard lockguard(&_user_mutex);for (auto& user : _online_user){if (addr == user) return;}_online_user.push_back(addr);lg.LogMessage(Debug, "%s : %d is add onlineusers!\n", addr.Ip().c_str(), addr.Port());}void Route(int sockfd, const std::string& message){// 先加锁,然后转化给所有人LockGuard lockguard(&_user_mutex);for (auto& user : _online_user){lg.LogMessage(Debug, "send to %s %d  success!\n", user.Ip().c_str(), user.Port());sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&user.GetAddr(), (socklen_t)sizeof(user.GetAddr()));}}void Start(){char buffer[1024];while (true){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){InetAddr addr(peer);buffer[n] = 0;AddOnlineUser(addr);std::string message;// 检测是否加入if (_hash.count(addr.Ip()) > 0) message = _hash[addr.Ip()] + buffer;else message = "[" + addr.Ip() + ":" + std::to_string(addr.Port()) + "]# " + buffer;std::cout << "message:" << message << std::endl;task_t task = std::bind(&UdpServer::Route, this, _sockfd, message);ThreadPool<task_t>::GetInstance()->Push(task);}}}~UdpServer(){pthread_mutex_destroy(&_user_mutex);}private:u_int16_t _port;int _sockfd;// func_t _OnMessage;std::vector<InetAddr> _online_user;pthread_mutex_t _user_mutex;std::unordered_map<std::string, std::string> _hash;
};

udp 客户端程序编写

#include <string>
#include <iostream>
#include <cstring>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Comm.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "Pthread.hpp"void Usage(std::string proc)
{std::cout << "Usage:" << proc << " server_ip server_port" << std::endl;
}class ThreadData
{
public:ThreadData(int sock, struct sockaddr_in& server):_sockfd(sock), _serveraddr(server){}~ThreadData(){}
public:int _sockfd;InetAddr _serveraddr;
};// 负责收消息 
void RecverRoutine(ThreadData& td)
{while (true){char buffer[4096];struct sockaddr_in tmp;socklen_t len = sizeof(tmp);ssize_t m = recvfrom(td._sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&tmp, &len);if (m > 0){buffer[m] = 0;std::cout << buffer << std::endl;}}
}// 负责发消息 
void SenderRoutine(ThreadData& td)
{while (true){std::string inbuffer;std::getline(std::cin, inbuffer);auto server = td._serveraddr.GetAddr();ssize_t n = sendto(td._sockfd, inbuffer.c_str(), inbuffer.size(), 0, (struct sockaddr*)&server, sizeof server);if (n <= 0) std::cout << "send erro......" << std::endl;usleep(50);}
}int main(int argc, char* argv[])
{if (argc != 3){Usage(argv[0]);return Usage_Err;}std::string server_ip = argv[1];u_int16_t port = std::stoi(argv[2]);// 创建 socketint sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){lg.LogMessage(Fatal, "sock errr: %d %s\n", errno, strerror(errno));return Socket_Err;}lg.LogMessage(Info, "sock success, sockfd: %d\n", sockfd);// lient 需要bind,但是不需要显示bind,让本地OS自动随机bind,选择随机端口号/// 填充 server 信息, 方便后面收发信息使用 struct sockaddr_in server;memset(&server, 0, sizeof server);server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(server_ip.c_str());server.sin_port = htons(port);ThreadData td(sockfd, server);Thread<ThreadData> recver("recver", RecverRoutine, td);Thread<ThreadData> sender("sender", SenderRoutine, td);recver.Start();sender.Start();recver.Join();sender.Join();close(sockfd);return 0;
}

tcp 服务端程序编写

#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unordered_map>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"class ThreadData{public:ThreadData(int sock, TcpServer *ptr, struct sockaddr_in &peer):sockfd(sock),  svr_ptr(ptr), addr(peer){}int SockFd() {return sockfd;}TcpServer *GetServer() { return svr_ptr;};~ThreadData(){close(sockfd);}private:int sockfd;TcpServer* svr_ptr;public:InetAddr addr;};class TcpServer : public nocopy
{
public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 创建socket, file fd, 本质是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, "create socket error, errno code: %d, error string: %s\n", errno, strerror(errno));exit(Fatal);}lg.LogMessage(Debug, "create socket success, sockfd: %d\n", _listensock);// 固定写法,解决一些少量的bind失败的问题 -- TODOint opt = 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));// 2. 填充本地网络信息并bindstruct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = htonl(INADDR_ANY);// 2.1 bindif (bind(_listensock, CONV(&local), sizeof(local)) != 0){lg.LogMessage(Fatal, "bind socket error, errno code: %d, error string: %s\n", errno, strerror(errno));exit(Bind_Err);}lg.LogMessage(Debug, "bind socket success, sockfd: %d\n", _listensock);// 3. 设置socket为监听状态,tcp特有的if (listen(_listensock, default_backlog) != 0){lg.LogMessage(Fatal, "listen socket error, errno code: %d, error string: %s\n", errno, strerror(errno));exit(Listen_Err);}lg.LogMessage(Debug, "listen socket success, sockfd: %d\n", _listensock);ThreadNs::ThreadPool<task_t>::GetInstance()->Start();funcs.insert(std::make_pair("defaultService", std::bind(&TcpServer::DefaultService, this, std::placeholders::_1, std::placeholders::_2)));}void Service(int sockfd, InetAddr addr){char buffer[1024];// 一直进行IOwhile (true){ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;std::cout << addr.PrintDebug() << "# " << buffer << std::endl;std::string echo_string = "server echo# ";echo_string += buffer;write(sockfd, echo_string.c_str(), echo_string.size());}else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client quit...\n");break;}else{lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));break;}}}// static void *HandlerRequest(void *args) // this// {//     pthread_detach(pthread_self());//     ThreadData *td = static_cast<ThreadData*>(args);//     td->GetServer()->Service(td->SockFd(), td->addr);//     delete td;//     return nullptr;// }void Start(){_isrunning = true;signal(SIGCHLD, SIG_IGN); // 在Linux环境中,如果对SIG_IGN进行忽略,子进程退出的时候,自动释放自己的资源while (_isrunning){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);if (sockfd < 0){lg.LogMessage(Warning, "accept socket error, errno code: %d, error string: %s\n", errno, strerror(errno));continue;}lg.LogMessage(Debug, "accept success, get n new sockfd: %d\n", sockfd);// 5. 提供服务啊, v1~v4// v1 单进程// Service(sockfd);// close(sockfd);// v2 多进程// pid_t id = fork();// if (id < 0)// {//     close(sockfd);//     continue;// }// else if (id == 0)// {//     // child//     close(_listensock);//     if (fork() > 0)//         exit(0);//     // 孙子进程,孤儿进程,被系统领养,正常处理//     Service(sockfd);//     close(sockfd);//     exit(0);// }// else// {//     close(sockfd);//     pid_t rid = waitpid(id, nullptr, 0);//     if (rid == id)//     {//         // do nothing//     }// }// v3 多进程 - 信号版// pid_t id = fork();// if (id < 0)// {//     close(sockfd);//     continue;// }// else if (id == 0)// {//     // child//     close(_listensock);//     Service(sockfd);//     close(sockfd);//     exit(0);// }// else// {//     close(sockfd);//     // 父进程不想等待呢?// }// v3 进程池 - 课堂不讲了 - 试一试 -- 问题?先创建子进程,然后才获取的连接,子进程看不到新获取的文件fd.// v4 多线程// ThreadData *td = new ThreadData(sockfd, this, peer);// pthread_t tid;// pthread_create(&tid, nullptr, HandlerRequest, td);// 主线程和新线程,不需要关闭所谓文件描述符, 将线程设置为分离// v5 线程池 -- 不能让我们的服务器,提供长服务task_t t = std::bind(&TcpServer::Routine, this, sockfd, InetAddr(peer));ThreadNs::ThreadPool<task_t>::GetInstance()->Push(t);}}std::string Read(int sockfd){char type[1024];ssize_t n = read(sockfd, type, sizeof(type) - 1);if (n > 0){type[n] = 0;}else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client quit...\n");}else{lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));}return type;}void Routine(int sockfd, InetAddr addr){funcs["defaultService"](sockfd, addr);std::string type = Read(sockfd);lg.LogMessage(Debug, "%s select %s\n", addr.PrintDebug().c_str(), type.c_str());if (type == "ping")funcs[type](sockfd, addr);else if (type == "translate")funcs[type](sockfd, addr);else if (type == "transform")funcs[type](sockfd, addr);else{}close(sockfd);}void DefaultService(int sockfd, InetAddr& addr){(void)addr;std::string service_list = " |";for (auto func : funcs){service_list += func.first;service_list += "|";}write(sockfd, service_list.c_str(), service_list.size());}void RegisterFunc(const std::string& name, callback_t func){funcs[name] = func;}~TcpServer(){}private:uint16_t _port;int _listensock; // TODObool _isrunning;// 构建业务逻辑std::unordered_map<std::string, callback_t> funcs;
};

tcp 客户端程序编写

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include "Comm.hpp"using namespace std;void handler(int signo)
{std::cout << "signo: " << signo << std::endl;exit(0);
}#define Retry_Count 5void Usage(const std::string &process)
{std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}bool visitServer(std::string &serverip, uint16_t &serverport, int *cnt)
{// 1. 创建socketstring inbuffer;char service_list[1024];ssize_t m = 0;ssize_t n = 0;int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){cerr << "socket error" << endl;return false;}bool ret = true;// 2. 要不要bind?必须要有Ip和Port, 需要bind,但是不需要用户显示的bind,client系统随机端口// 发起连接的时候,client会被OS自动进行本地绑定// 2. connectstruct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);// p:process(进程), n(网络) -- 不太准确,但是好记忆inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // 1. 字符串ip->4字节IP 2. 网络序列n = connect(sockfd, CONV(&server), sizeof(server)); // 自动进行bind哦!if (n < 0){cerr << "connect error" << endl;ret = false;goto END;}*cnt = 0;m = read(sockfd, service_list, sizeof(service_list) - 1);if (m > 0){service_list[m] = 0;cout << "服务器提供的服务列表是: " << service_list << endl;}// 并没有向server一样,产生新的sockfd.未来我们就用connect成功的sockfd进行通信即可.cout << "请你选择服务#  ";getline(cin, inbuffer);write(sockfd, inbuffer.c_str(), inbuffer.size());cout << "Enter> ";getline(cin, inbuffer);if (inbuffer == "quit")return true;// std::cout << "echo : " << inbuffer << std::endl;n = write(sockfd, inbuffer.c_str(), inbuffer.size());if (n > 0){char buffer[1024];m = read(sockfd, buffer, sizeof(buffer) - 1);if (m > 0){buffer[m] = 0;cout << buffer << endl;}else if (m == 0){return true;}else{ret = false;goto END;}}else{std::cout << "hello write Error" << std::endl;ret = false;goto END;}END:close(sockfd);return ret;
}// ./tcp_client serverip serverport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);return 1;}std::string serverip = argv[1];uint16_t serverport = stoi(argv[2]);signal(SIGPIPE, SIG_IGN);// for (int i = 1; i <= 31; i++)// {//     signal(i, handler);// }int cnt = 1;while (cnt <= Retry_Count){bool result = visitServer(serverip, serverport, &cnt);if (result){break;}else{sleep(1);std::cout << "server offline, retrying..., count : " << cnt << std::endl;cnt++;}}if (cnt >= Retry_Count){std:cout << "server offline" << std::endl;}return 0;
}

这篇关于C++ 网络套接字编程 tcp udp的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1075128

相关文章

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同

使用C/C++调用libcurl调试消息的方式

《使用C/C++调用libcurl调试消息的方式》在使用C/C++调用libcurl进行HTTP请求时,有时我们需要查看请求的/应答消息的内容(包括请求头和请求体)以方便调试,libcurl提供了多种... 目录1. libcurl 调试工具简介2. 输出请求消息使用 CURLOPT_VERBOSE使用 C

C++实现获取本机MAC地址与IP地址

《C++实现获取本机MAC地址与IP地址》这篇文章主要为大家详细介绍了C++实现获取本机MAC地址与IP地址的两种方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 实际工作中,项目上常常需要获取本机的IP地址和MAC地址,在此使用两种方案获取1.MFC中获取IP和MAC地址获取