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

Socket通信(C++)

文章目录

  • 什么是Socket
  • Socket通信过程
  • C++ Socket通信API
    • int socket(int domain, int type, int protocol);
    • int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
      • struct sockaddr
      • struct sockaddr_un
      • struct sockaddr_in / struct sockaddr_in6
    • int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    • int listen(int sockfd, int backlog);
    • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    • ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    • ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    • int close(int fd);
  • C++ Socket通信
    • 服务端
    • 客户端
  • C++ Socket通信改进
    • UNIX socket文件已存在
    • 子线程通信
    • Socket通信流程改进
      • 服务端
        • 核心代码
        • 头文件
        • 全代码
    • 客户端

什么是Socket

Socket是用于计算机之间进行网络通信的端口的抽象。提供了应用进程利用底层网络协议交换数据的机制。

通过TCP/IP协议栈进行网络通信的过程中,每个设备都需要唯一的IP地址信息进行标识,Socket允许应用程序通过IP地址进行通信,而不需要关心底层TCP/IP协议的具体实现,在这个过程中,可以将Socket理解为对TCP/IP的进一步封装。

同时,Socket也可以在本地进程之间通信,Socket允许应用绑定本地Socket文件,通过Socket文件进行进程之间数据的交换。

Socket通信过程

Socket通信

  • 服务端:
    1. 创建Socket
    2. 绑定Socket: Socket支持绑定本地Socket或者IP端口
    3. 服务端开始监听客户端连接请求,当监听到连接请求后,调用accept()接收连接请求,建立服务端客户端连接
    4. 服务端客户端开始通信
      • send(): 通过send接口发送数据
      • receive(): 通过receive接口接收数据
    5. 服务端客户端处理通信异常信息
    6. 服务端主动关闭连接,或监听到客户端异常断开后,关闭连接
  • 客户端:
    1. 创建Socket
    2. 请求连接服务端Socket
    3. 服务端客户端开始通信
      • send(): 通过send接口发送数据
      • receive(): 通过receive接口接收数据
    4. 服务端客户端处理通信异常信息
    5. 客户端主动关闭连接,或监听到服务端异常断开后,关闭连接

C++ Socket通信API

C++在进行Socket通信时,通常有以下常用接口:

int socket(int domain, int type, int protocol);

以下参数信息为常用参数信息,更多内容参考man socket

  • int socket(int domain, int type, int protocol):用于创建Socket
  • 头文件:
    • #include <sys/types.h>
    • #include <sys/socket.h>
  • 参数:
    • domain:网络连接协议簇的标识
      • AF_INET:IPv4协议簇
      • AF_INET6:IPv6协议簇
      • AF_UNIX/AF_LOCAL:本地Socket
    • type:Socket通信的语义类型
      • SOCK_STREAM:提供有序、可靠、双向、基于连接的字节流。可以支持带外数据传输机制(TCP通信常用)
      • SOCK_DGRAM:支持数据报(固定最大长度的无连接、不可靠的消息)(UDP通信常用)。
    • protocol:与Socket通信语义相匹配的协议类型,注意,protocol的选择需要和语义类型匹配,不可以随意组合
      • 0:根据type自动匹配protocol
      • IPPROTO_TCP:TCP协议
      • IPPROTO_UDP:UDP协议
  • 返回值:
    • 成功:返回Socket文件描述符ID
    • 失败:返回-1,并设置errno

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen):socket()创建socket指定其协议类型后,我们还需要把socket绑定到具体的地址上才可以使用
  • 头文件:
    • #include <sys/types.h>
    • #include <sys/socket.h>
  • 参数:
    • sockfd:上述通过socket()函数创建的socketID信息
    • addr:socket绑定的协议地址,该参数需要与socket创建时指定的协议类型相关联
    • addrlen:addr结构的字节大小
  • 返回值:
    • 成功:返回0
    • 失败:返回-1,并设置errno

struct sockaddr

#include <sys/types.h>
#include <sys/socket.h>struct sockaddr {sa_family_t sa_family;      // socket addr familychar        sa_data[14];    // socket addr data
}

sockaddr结构体中,数据信息混合在了一起,针对TCP/UDP这种包含IP和Port等多个数据的类型时,不是很好区分,所以一般情况下通过定义对应协议的addr信息,然后通过类型转换为soctaddr结构体

struct sockaddr_un

#include <sys/socket.h>
#include <sys/un.h>struct sockaddr_un {sa_family_t sun_family;     // AF_UNIXchar        sun_path[108];  // 本地Socket路径
};

struct sockaddr_in / struct sockaddr_in6

#include <netinet/in.h>struct sockaddr_in {sa_family_t     sin_family;     // AF_INETin_port_t       sin_port;       // Port: 端口号struct in_addr  sin_addr;       // IPv4地址信息
};struct sockaddr_in6 {sa_family_t     sin6_family;    // AF_INET6in_port_t       sin6_port;      // Port: 端口号uint32_t        sin6_flowinfo;  // IPv6流标识符,用于标识特定的数据流,在发送数据时附加到数据包头部struct in6_addr sin6_addr;      // IPv6地址信息uint32_t        sin6_scope_id;  // Scope Id:用于确定IPv6地址的使用范围和权限
};

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen):客户端向服务端发送连接请求,等待连接
  • 头文件:
    • #include <sys/types.h>
    • #include <sys/socket.h>
  • 参数:
    • sockfd:上述通过socket()函数创建的socketID信息,该sockfd为客户端向服务端发起请求的socket
    • addr:socket绑定的协议地址,该参数需要与socket创建时指定的协议类型相关联
    • addrlen:addr结构的字节大小
  • 返回值:
    • 成功:返回0
    • 失败:返回-1,并设置errno

int listen(int sockfd, int backlog);

  • int listen(int sockfd, int backlog):设置socket队列,监听socket请求,以处理客户端的连接、断开操作
  • 头文件:
    • #include <sys/types.h>
    • #include <sys/socket.h>
  • 参数:
    • sockfd:上述通过socket()函数创建的socketID信息
    • backlog: socket监听可能的最大连接长度,若队列已满,新的客户端连接请求会收到ECONNREFUSED错误
  • 返回值:
    • 成功:返回0
    • 失败:返回-1,并设置errno

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

  • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen):监听sockfd队列的第一个连接请求,创建连接socket,并返回引用该socket的文件描述符
  • 头文件:
    • #include <sys/types.h>
    • #include <sys/socket.h>
  • 参数:
    • sockfd:上述通过socket()函数创建的socketID信息,该sockfd为客户端向服务端发起请求的socket
    • addr:socket绑定的协议地址,该参数需要与socket创建时指定的协议类型相关联
    • addrlen:addr结构的字节大小
  • 返回值:
    • 成功:返回socket文件描述符ID
    • 失败:返回-1,并设置errno

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

socket 数据的常用发送接收还有read(),write()和其他方法。

read()write()方法在遇到\0类型数据时会终止,所以通过该方法发送数据是受限的,这里不做介绍

  • ssize_t send(int sockfd, const void *buf, size_t len, int flags):发送指定长度的数据
  • 头文件:
    • #include <sys/types.h>
    • #include <sys/socket.h>
  • 参数:
    • sockfd:客户端和服务端已建立的socketID文件描述符信息
    • buf:需要发送的数据内容
    • len: buffer数据长度
    • flags: 消息发送或运算的特性,默认可为0
  • 返回值:
    • 成功:成功发送数据的字节数
    • 失败:返回-1,并设置errno

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

  • ssize_t recv(int sockfd, void *buf, size_t len, int flags):接收指定长度的数据
  • 头文件:
    • #include <sys/types.h>
    • #include <sys/socket.h>
  • 参数:
    • sockfd:客户端和服务端已建立的socketID文件描述符信息
    • buf:接收数据的地址
    • len: buffer数据长度
    • flags: 消息接收或运算的特性,默认可为0
  • 返回值:
    • 成功:成功发送数据的字节数
    • 失败:返回-1,并设置errno

int close(int fd);

  • int close(int fd):关闭socket连接
  • 头文件:
    • #include <unistd.h>
  • 参数:
    • fd:已连接的文件描述符ID
  • 返回值:
    • 成功:0
    • 失败:返回-1,并设置errno

C++ Socket通信

若要使用IPv4-TCP通信,请注释UNIX,取消注释IPv4

本示例不对IPv6通信进行说明

服务端

// socket
#include <sys/types.h>
#include <sys/socket.h>// socket addr
#include <sys/un.h>
#include <netinet/in.h>// file close
#include <unistd.h>// htons
#include <arpa/inet.h>#include <iostream>int main() {// 1. 创建服务端Socket// IPv4// int server_socket = socket(AF_INET, SOCK_STREAM, 0);// UNIXint server_socket = socket(AF_UNIX, SOCK_STREAM, 0);if (server_socket < 0) {std::cerr << "socket error: 创建失败" << std::endl;throw std::runtime_error("socket error: 创建失败");} else {std::cout << "socket 创建成功,socketId:" << server_socket << std::endl;}// 2. 绑定Socket// IPv4// struct sockaddr_in server_addr;// server_addr.sin_family = AF_INET;// // INADDR_ANY: 0.0.0.0(绑定到本机的所有网卡上)// server_addr.sin_addr.s_addr = INADDR_ANY;// // htons: 将 unsigned short 转换为网络字节序// server_addr.sin_port = htons(8888);// UNIXstruct sockaddr_un server_addr;server_addr.sun_family = AF_UNIX;strcpy(server_addr.sun_path, "/var/tmp/socket_test");if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {std::cerr << "socket bind error: 绑定套接字失败,请检查本地套接字是否被占用" << std::endl;throw std::runtime_error("socket bind error: 绑定套接字失败,请检查本地套接字是否被占用");}// 3. 创建socket监听队列,并开始监听int listen_ret = listen(server_socket, 2048);if (listen_ret < 0) {std::cerr << "socket listen error: Socket监听失败" << std::endl;throw std::runtime_error("socket listen error: Socket监听失败");} // 4. 接收客户端连接请求// IPv4// struct sockaddr_in client_addr;// UNIXstruct sockaddr_un client_addr;int client_addr_len = sizeof(client_addr);int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, (socklen_t *) &client_addr_len);if (client_socket < 0) {std::cerr << "socket client accpet error: Socket监听失败" << std::endl;throw std::runtime_error("socket listen error: Socket监听失败");}// 5. 接收客户端发送信息int recv_data_length = 15;char recv_data[recv_data_length];if (recv(client_socket, recv_data, recv_data_length, 0) < 0) {std::cerr << "socket recv error: 接收数据失败" << std::endl;throw std::runtime_error("socket recv error: 接收数据失败");} else {std::cout << "socket recv data: " << recv_data << std::endl;}// 6. 关闭socketclose(server_socket);close(client_socket);return 0;
}

客户端

// socket
#include <sys/types.h>
#include <sys/socket.h>// socket addr
#include <sys/un.h>
#include <netinet/in.h>// file close
#include <unistd.h>// htons
#include <arpa/inet.h>#include <iostream>
#include <string.h>int main() {// 1. 创建客户端Socket// IPv4// int client_socket = socket(AF_INET, SOCK_STREAM, 0);// UNIXint client_socket = socket(AF_UNIX, SOCK_STREAM, 0);if (client_socket < 0) {std::cerr << "socket error: 创建失败" << std::endl;throw std::runtime_error("socket error: 创建失败");} else {std::cout << "socket 创建成功,socketId:" << client_socket << std::endl;}// 2. 绑定Socket// IPv4// struct sockaddr_in client_addr;// client_addr.sin_family = AF_INET;// // INADDR_ANY: 0.0.0.0(绑定到本机的所有网卡上)// client_addr.sin_addr.s_addr = INADDR_ANY;// // htons: 将 unsigned short 转换为网络字节序// client_addr.sin_port = htons(8888);// UNIXstruct sockaddr_un client_addr;client_addr.sun_family = AF_UNIX;strcpy(client_addr.sun_path, "/var/tmp/socket_test");if (connect(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0) {std::cerr << "socket connect error: 连接服务端socket失败" << std::endl;throw std::runtime_error("socket connect error: 连接服务端socket失败");}// 3. 客户端发送信息std::string send_data = "Hello, Focus!!!";if (send(client_socket, send_data.c_str(), send_data.length(), 0) < 0) {std::cerr << "socket send error: 发送数据失败" << std::endl;throw std::runtime_error("socket send error: 发送数据失败");} // 4. 关闭socketclose(client_socket);return 0;
}

C++ Socket通信改进

以上代码可以实现简单的Socket通信流程,但是存在问题:

  • 服务端在第一次启动时,会创建本地Socket套接字文件,在以IP协议进行通信的Socket网络通信中,Socket关闭会释放端口,但是以UNIX本地Socket进行通信的Socket网络通信在关闭后,不会主动删除创建的Socket文件。在再次通信的过程中,会绑定失败
    • 解决方案:在程序第一次绑定时,判断文件是否存在,存在则删除
  • 在Socket通信过程中,send,recv,accept,connect等函数为阻塞函数,所以一旦调用此类方法,程序阻塞,就不能接收其他连接和处理了
    • 解决方案1:采用非阻塞模型进行Socket通信
    • 解决方案2:采用线程的方式,当新的socket连接建立后,将socket通信的过程放在线程中执行,线程结束释放该socket通信,这样可以通过accept方法以阻塞的方式一直等待新的客户端连接
    • 这里采用方案2进行改进
  • Socket通信过程中,采用sendrecv方法虽然解决了writeread方法不能发送\0的问题,但是存在的问题是,通过sendrecv传输数据需要已知数据大小,在数据大小确定的情况下,该方案很符合场景需要,但是在数据大小不确定的情况下。
    • 解决方案1: 确定"大"数据长度,数据长度不足后面补标志位、
    • 解决方案2:在传输数据前,先发送数据大小,然后初始化buffer接收数据
    • 这里采用方案2进行改进

在以下改进中,统一采用UNIX模型进行通信,IP模型请自行替换

UNIX socket文件已存在

#include <unistd.h>#include <iostream>
#include <string.h>void make_sure_socket_path(char *socket_path) {// F_OK:判断文件是否可读,若文件不存在则不可读if (access(socket_path, F_OK) == 0) {// unlink:删除文件if (unlink(socket_path) < 0) {std::cerr << "删除 socket:" << socket_path << " 失败" << std::endl;throw std::runtime_error("socket error: 创建失败");}}
}

子线程通信

头文件:

// socket
#include <sys/types.h>
#include <sys/socket.h>// socket addr
#include <sys/un.h>
#include <netinet/in.h>#include <pthread.h>#include <unistd.h>// htons
#include <arpa/inet.h>#include <iostream>class UnixSocketServer {private:std::string socket_path;// 记录服务端socket配置int socket_id;// 客户端最大连接数int max_client = 5;static void *thread_working(void *args);public:UnixSocketServer(std::string socket_path);~UnixSocketServer();void run();
};
#include "socket.h"UnixSocketServer::UnixSocketServer(std::string socket_path) {this->socket_path = socket_path;if (access(this->socket_path.c_str(), F_OK) == 0) {// unlink:删除文件if (unlink(this->socket_path.c_str()) < 0) {std::cerr << "删除 socket:" << this->socket_path << " 失败" << std::endl;throw std::runtime_error("socket error: 创建失败");}}// 1. 创建服务端socketthis->socket_id = socket(AF_UNIX, SOCK_STREAM, 0);if (this->socket_id < 0) {std::cerr << "socket error: 创建失败" << std::endl;throw std::runtime_error("socket error: 创建失败");} else {std::cout << "socket 创建成功,socketId:" << this->socket_id << std::endl;}// 2. 绑定Socketstruct sockaddr_un server_addr;server_addr.sun_family = AF_UNIX;strcpy(server_addr.sun_path, this->socket_path.c_str());if (bind(this->socket_id, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {std::cerr << "socket bind error: 绑定套接字失败,请检查本地套接字是否被占用" << std::endl;throw std::runtime_error("socket bind error: 绑定套接字失败,请检查本地套接字是否被占用");}// 3. 创建socket监听队列,并开始监听int listen_ret = listen(this->socket_id, this->max_client);if (listen_ret < 0) {std::cerr << "socket listen error: Socket监听失败" << std::endl;throw std::runtime_error("socket listen error: Socket监听失败");} 
}UnixSocketServer::~UnixSocketServer() {close(this->socket_id);unlink(this->socket_path.c_str());
}void *UnixSocketServer::thread_working(void *args) {}void UnixSocketServer::run() {while(true) {struct sockaddr_un client_addr;int client_addr_len = sizeof(client_addr);int client_id = accept(this->socket_id, (struct sockaddr*)&client_addr, (socklen_t *) &client_addr_len);if (client_id < 0) {std::cerr << "socket client accpet error: Socket监听失败" << std::endl;throw std::runtime_error("socket listen error: Socket监听失败");} else {std::cout << "socket client accpet success: 客户端:" << client_id << "连接成功" << std::endl;}pthread_t thread_id = client_id;pthread_create(&thread_id, NULL, this->thread_working, (void *) &client_id);pthread_detach(thread_id);}
}int main() {UnixSocketServer *unixSocketServer = new UnixSocketServer("/var/tmp/socket_test");unixSocketServer->run();return 0;
}

Socket通信流程改进

服务端

核心代码
void *UnixSocketServer::thread_working(void *args) {int client_id = *(int *) args;while (true) {// 1. 接收数据长度// 注意这里不能采用 size_t 等类型定义,该类型会导致在不同架构编译器生成的 sizeof 大小不一致int receive_data_length;if (recv(client_id, &receive_data_length, sizeof(receive_data_length), 0) <= 0) {std::cerr << "socket communication error: 接收客户端数据长度失败" << std::endl;break;} else {std::cout << "socket communication success: 客户端原始数据长度:" << receive_data_length << std::endl;}// 2. 接收数据char receive_data[receive_data_length];if (recv(client_id, &receive_data, receive_data_length, 0) <= 0) {std::cerr << "socket communication error: 接收客户端数据失败" << std::endl;break;} else { // 这里通过 std::string 构造函数初始化接收数据// 接收数据长度没有 `\0`,C/C++在输出 char 时,遇到 `\0` 才会停止std::cout << "socket communication success: 客户端原始数据:" << std::string(receive_data, receive_data_length) << std::endl;}// 3. 发送数据长度std::string send_data = "Hello, Focus!!!";int send_data_length = send_data.length();if (send(client_id, &send_data_length, sizeof(send_data_length), 0) < 0) {std::cerr << "socket communication error: 发送客户端数据长度失败" << std::endl;break;}// 4. 发送数据if (send(client_id, send_data.c_str(), send_data_length, 0) < 0) {std::cerr << "socket communication error: 发送客户端数据失败" << std::endl;break;} }std::cout << "socket client close:客户端 " << client_id << " 已断开连接" << std::endl;close(client_id);
}
头文件
// socket
#include <sys/types.h>
#include <sys/socket.h>// socket addr
#include <sys/un.h>
#include <netinet/in.h>#include <pthread.h>#include <unistd.h>// htons
#include <arpa/inet.h>#include <iostream>class UnixSocketServer {private:std::string socket_path;// 记录服务端socket配置int socket_id;// 客户端最大连接数int max_client = 5;static void *thread_working(void *args);public:UnixSocketServer(std::string socket_path);~UnixSocketServer();void run();
};
全代码
#include "socket.h"UnixSocketServer::UnixSocketServer(std::string socket_path) {this->socket_path = socket_path;if (access(this->socket_path.c_str(), F_OK) == 0) {// unlink:删除文件if (unlink(this->socket_path.c_str()) < 0) {std::cerr << "删除 socket:" << this->socket_path << " 失败" << std::endl;throw std::runtime_error("socket error: 创建失败");}}// 1. 创建服务端socketthis->socket_id = socket(AF_UNIX, SOCK_STREAM, 0);if (this->socket_id < 0) {std::cerr << "socket error: 创建失败" << std::endl;throw std::runtime_error("socket error: 创建失败");} else {std::cout << "socket 创建成功,socketId:" << this->socket_id << std::endl;}// 2. 绑定Socketstruct sockaddr_un server_addr;server_addr.sun_family = AF_UNIX;strcpy(server_addr.sun_path, this->socket_path.c_str());if (bind(this->socket_id, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {std::cerr << "socket bind error: 绑定套接字失败,请检查本地套接字是否被占用" << std::endl;throw std::runtime_error("socket bind error: 绑定套接字失败,请检查本地套接字是否被占用");}// 3. 创建socket监听队列,并开始监听int listen_ret = listen(this->socket_id, this->max_client);if (listen_ret < 0) {std::cerr << "socket listen error: Socket监听失败" << std::endl;throw std::runtime_error("socket listen error: Socket监听失败");} 
}UnixSocketServer::~UnixSocketServer() {close(this->socket_id);unlink(this->socket_path.c_str());
}void *UnixSocketServer::thread_working(void *args) {int client_id = *(int *) args;while (true) {// 1. 接收数据长度// 注意这里不能采用 size_t 等类型定义,该类型会导致在不同架构编译器生成的 sizeof 大小不一致int receive_data_length;if (recv(client_id, &receive_data_length, sizeof(receive_data_length), 0) <= 0) {std::cerr << "socket communication error: 接收客户端数据长度失败" << std::endl;break;} else {std::cout << "socket communication success: 客户端原始数据长度:" << receive_data_length << std::endl;}// 2. 接收数据char receive_data[receive_data_length];if (recv(client_id, &receive_data, receive_data_length, 0) <= 0) {std::cerr << "socket communication error: 接收客户端数据失败" << std::endl;break;} else { // 这里通过 std::string 构造函数初始化接收数据// 接收数据长度没有 `\0`,C/C++在输出 char 时,遇到 `\0` 才会停止std::cout << "socket communication success: 客户端原始数据:" << std::string(receive_data, receive_data_length) << std::endl;}// 3. 发送数据长度std::string send_data = "Hello, Focus!!!";int send_data_length = send_data.length();if (send(client_id, &send_data_length, sizeof(send_data_length), 0) < 0) {std::cerr << "socket communication error: 发送客户端数据长度失败" << std::endl;break;}// 4. 发送数据if (send(client_id, send_data.c_str(), send_data_length, 0) < 0) {std::cerr << "socket communication error: 发送客户端数据失败" << std::endl;break;} }std::cout << "socket client close:客户端 " << client_id << " 已断开连接" << std::endl;close(client_id);
}void UnixSocketServer::run() {while(true) {struct sockaddr_un client_addr;int client_addr_len = sizeof(client_addr);int client_id = accept(this->socket_id, (struct sockaddr*)&client_addr, (socklen_t *) &client_addr_len);if (client_id < 0) {std::cerr << "socket client accpet error: Socket监听失败" << std::endl;throw std::runtime_error("socket listen error: Socket监听失败");} else {std::cout << "socket client accpet success: 客户端:" << client_id << "连接成功" << std::endl;}pthread_t thread_id = client_id;pthread_create(&thread_id, NULL, this->thread_working, (void *) &client_id);pthread_detach(thread_id);}
}int main() {UnixSocketServer *unixSocketServer = new UnixSocketServer("/var/tmp/socket_test");unixSocketServer->run();return 0;
}

客户端

// socket
#include <sys/types.h>
#include <sys/socket.h>// socket addr
#include <sys/un.h>
#include <netinet/in.h>// file close
#include <unistd.h>// htons
#include <arpa/inet.h>#include <iostream>
#include <string.h>int socket_connect(const char *socket_path) {// 1. 创建客户端Socketint client_socket = socket(AF_UNIX, SOCK_STREAM, 0);if (client_socket < 0) {std::cerr << "socket error: 创建失败" << std::endl;throw std::runtime_error("socket error: 创建失败");} else {std::cout << "socket 创建成功,socketId:" << client_socket << std::endl;}// 2. 绑定Socketstruct sockaddr_un client_addr;client_addr.sun_family = AF_UNIX;strcpy(client_addr.sun_path, socket_path);if (connect(client_socket, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0) {std::cerr << "socket connect error: 连接服务端socket失败" << std::endl;throw std::runtime_error("socket connect error: 连接服务端socket失败");}return client_socket;
}int socket_close(int client_socket) {close(client_socket);
}void socket_communication(const char *socket_path, std::string send_data) {int client_socket = socket_connect(socket_path);// 1. 发送数据长度int send_data_length = send_data.length();if (send(client_socket, &send_data_length, sizeof(send_data_length), 0) < 0) {std::cerr << "socket communication error: 发送客户端数据长度失败" << std::endl;}// 2. 发送数据if (send(client_socket, send_data.c_str(), send_data_length, 0) < 0) {std::cerr << "socket communication error: 发送客户端数据失败" << std::endl;} // 3. 接收数据长度int receive_data_length;if (recv(client_socket, &receive_data_length, sizeof(receive_data_length), 0) <= 0) {std::cerr << "socket communication error: 接收客户端数据长度失败" << std::endl;} else {std::cout << "socket communication success: 客户端原始数据长度:" << receive_data_length << std::endl;}// 2. 接收数据char receive_data[receive_data_length];if (recv(client_socket, &receive_data, receive_data_length, 0) <= 0) {std::cerr << "socket communication error: 接收客户端数据失败" << std::endl;} else {std::cout << "socket communication success: 客户端原始数据:" << receive_data << std::endl;}socket_close(client_socket);
}int main() {std::string data = R"({"args":null,"function":"select_all_algorithm_log","module":"dal","package":"log"})";socket_communication("/var/tmp/socket_test", data);return 0;
}

相关文章:

Socket通信(C++)

文章目录 什么是SocketSocket通信过程C Socket通信APIint socket(int domain, int type, int protocol);int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);struct sockaddrstruct sockaddr_unstruct sockaddr_in / struct sockaddr_in6 int connect(int …...

小白学大模型:LLaMA-Factory 介绍与使用

最近这一两周看到不少互联网公司都已经开始秋招提前批了。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c;帮助一些球友…...

java算法day26

java算法day26 207 课程表208 实现Trie(前缀树) 207 课程表 这题对应的知识是图论里的拓扑排序的知识。从题意就可以感受出来了。题目说如果要学习某课程&#xff0c;那么就需要先完成某课程。 这里我描述比较复杂的情况&#xff1a;课程与课程之间也有可能是多对一的场景或者…...

docker笔记7-dockerfile

docker笔记7-dockerfile 一、dockerfile介绍二、dockerfile指令三、构建自己的镜像 一、dockerfile介绍 Dockerfile是用来构建Docker镜像的构建文件&#xff0c;是由一系列命令和参数构成的脚本。 以下是常用的 Dockerfile 关键字的完整列表和说明&#xff1a; 二、docker…...

Spring-cloud Alibaba组件--Dubbo

远程调用技术 RestFul风格 基于HTTP协议实现&#xff0c;而HTTP是一种网络传输协议&#xff0c;基于TCP&#xff0c;规定了数据传输的格式。 RPC协议 Remote Produce Call 远程过程调用&#xff0c;类似的还有 RMI &#xff08; remote method invoke&#xff09;。自定义数…...

右值引用--C++11

左值引用和右值引用 传统的C语法中就有引用的语法&#xff0c;而C11中新增了的右值引用语法特性&#xff0c;所以从现在开始我们 之前学习的引用就叫做左值引用。无论左值引用还是右值引用&#xff0c;都是给对象取别名。 什么是左值&#xff1f;什么是左值引用&#xff1f;…...

这样做外贸报价表,客户看了才想下单

报价&#xff0c;是外贸业务中最重要的一步&#xff0c;作为外贸人&#xff0c;不会做报价表可不行。有人说&#xff0c;直接在邮件里回复价格不就好了&#xff1f;是的&#xff0c;产品简单的可以这么做&#xff0c;但你也不能忽视报价表的价值&#xff0c;一份完美的价格表对…...

Swift学习入门,新手小白看过来

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…...

【Ant Design Pro】快速上手

初始化 初始化脚手架&#xff1a;快速开始 官方默认使用 umi4&#xff0c;这里文档还没有及时更新&#xff08;不能像文档一样选择 umi 的版本&#xff09;&#xff0c;之后我选择 simple。 然后安装依赖。 在 package.json 中&#xff1a; "start": "cross-e…...

Hive3:Hive初体验

1、创建表 CREATE TABLE test(id INT, name STRING, gender STRING);2、新增数据 INSERT INTO test VALUES(1, 王力红, 男); INSERT INTO test VALUES(2, 钉钉盯, 女); INSERT INTO test VALUES(3, 咔咔咔, 女);3、查询数据 简单查询 select * from test;带聚合函数的查询 …...

blender顶点乱飞的问题解决

初学blender&#xff0c;编辑模式下移动某些顶点&#xff0c;不管是移动还是滑动都会出现定点乱飞的问题&#xff0c;后来才发现是开了吸附工具的原因&#xff01;&#xff01;&#xff01;&#xff01; 像下面这样&#xff0c;其实我只是在Z轴上移动&#xff0c;但是就跑的很…...

Elasticsearch(ES) 集群脑裂

脑裂问题(split-brain problem)是指一个分布式系统中&#xff0c;当网络分裂&#xff08;network partition&#xff09;发生时&#xff0c;导致系统内部的两个或多个节点相互独立地认为自己仍然与其他节点连接&#xff0c;每个节点组都试图执行操作&#xff0c;这可能会导致数…...

spark 3.0.0源码环境搭建

环境 Spark版本&#xff1a;3.0.0 java版本&#xff1a;1.8 scala版本&#xff1a;2.12.19 Maven版本&#xff1a;3.8.1 编译spark 将spark-3.0.0的源码导入到idea中 执行mvn clean package -Phive -Phive-thriftserver -Pyarn -DskipTests 执行sparksql示例类SparkSQLExam…...

3.3、matlab彩色图和灰度图的二值化算法汇总

1、彩色图和灰度图的二值化算法汇总原理及流程 彩色图和灰度图的二值化算法的原理都是将图像中的像素值转化为二值(0或1),以便对图像进行简化或者特定的图像处理操作。下面分别介绍彩色图和灰度图的二值化算法的原理及流程: 1)彩色图的二值化算法原理及流程 (1)原理:…...

新手必看:Elasticsearch 入门全指南

Elasticsearch 入门介绍 Elasticsearch 是一个开源的分布式搜索和分析引擎&#xff0c;广泛应用于处理大规模数据和实时搜索需求。它基于 Apache Lucene 构建&#xff0c;具备高可扩展性和分布式特性&#xff0c;能够快速、可靠地存储、搜索和分析大量数据。本文将介绍 Elasti…...

【Linux】TCP全解析:构建可靠的网络通信桥梁

文章目录 前言1. TCP 协议概述2. TCP报头结构3. 如何理解封装和解包呢&#xff1f;4. TCP的可靠性机制4.1 TCP的确认应答机制4.2 超时重传机制 5. TCP链接管理机制5.1 经典面试题&#xff1a;为什么建立连接是三次握手&#xff1f;5.2 经典面试题&#xff1a;为什么要进行四次挥…...

图像处理 -- ISP中的3DNR与2DNR区别及实现原理

ISP中的3DNR与2DNR区别及实现原理 2DNR&#xff08;2D Noise Reduction&#xff09; 2DNR的原理&#xff1a; 2DNR主要针对单帧图像进行降噪处理。它利用空间域内的像素值&#xff0c;采用空间滤波的方法来减少噪声。常用的方法包括均值滤波、中值滤波和高斯滤波等。这些方法…...

硬盘分区读不出来的解决之道:从自救到专业恢复

在日常的计算机使用过程中&#xff0c;硬盘分区读不出来的问题常常令人头疼不已。这一问题不仅阻碍了用户对数据的正常访问&#xff0c;还可能预示着数据安全的潜在威胁。硬盘分区读不出来&#xff0c;通常是由于分区表损坏、文件系统错误、物理扇区损坏、驱动程序冲突或硬件连…...

盘点2024年网上很火的4个语音识别转文字工具。

语音识别转文字是一项非常实用的技术&#xff0c;可以帮助我们在会议记录中省去手动记录&#xff0c;在采访中迅速得到文字稿&#xff0c;在学习中快速生成课堂笔...运用十分广泛。但是很多人不知道要怎么转换&#xff0c;在这里我便给大家介绍几款效率非常高的语音转文字的工具…...

解决 Git 访问 GitHub 时的 SSL 错误

引言 在使用 Git 进行版本控制时&#xff0c;我们可能会遇到各种网络相关的错误。其中一种常见的错误是 SSL 连接问题&#xff0c;这会导致 Git 无法访问远程仓库。本文将介绍一个具体的错误 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 0&#xff0c;以及如何通过禁用 SSL 证…...

LinuxCentos中安装apache网站服务详细教程

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…...

LUA脚本改造redis分布式锁

在redis集群模式下&#xff0c;我们会启动多个tomcat实例&#xff0c;每个tomcat实例都有一个JVM&#xff0c;且不共享。而synchronize锁的作用范围仅仅是当前JVM&#xff0c;所以我们需要一个作用于集群下的锁&#xff0c;也就是分布式锁。&#xff08;就是不能用JVM自带的锁了…...

web端使用HTML5开发《贪吃蛇》小游戏教程【附源码】

自制游戏列表 1植物大战僵尸自制HTML5游戏《植物大战僵尸》2开心消消乐自制HTML5游戏《开心消消乐》3贪吃蛇自制HTML5游戏《贪吃蛇》4捕鱼达人自制HTML5游戏《捕鱼达人》 一、游戏简介 贪吃蛇是一款经典的电子游戏&#xff0c;最早在1976年由Gremlin公司推出&#xff0c;名为…...

Selenium使用教程-Selenium环境搭建与基础操作

Selenium环境搭建与基础操作 1. 引言&#xff1a;Selenium简介​ Selenium&#xff0c;作为自动化测试领域的明星工具&#xff0c;以其强大的跨浏览器测试能力而闻名。它支持多种编程语言&#xff08;如Java、Python、C#等&#xff09;&#xff0c;允许开发者编写脚本来模拟真…...

1950年-2021年中国历年民航航线里程统计报告

数据为1950年到2021年我国每年的民航航线总里程数据。 2021年&#xff0c;我国定期航班航线总里程为689.78万公里&#xff0c;相比2019年下降了258.44万公里。 数据统计单位为&#xff1a;公里. 数据说明&#xff1a; 2011年起民航航线里程改为定期航班航线里程 我国定期航班…...

前端了解到框架-网络复习

前端 HTML 超文本标记语言 画页面 各种各样的标签组成页面进行展示 桌面创建文本修改后缀即可 <!DOCTYPE html>: 声明文档类型和HTML版本。<html>: 根标签&#xff0c;所有其他标签都包含在内。<head>: 包含了文档的元数据&#xff0c;如字符编码、网页标…...

防火墙——网络环境支持

目录 网络环境支持 防火墙的组网 web连接上防火墙 web管理口 让防火墙接到网络环境中 ​编辑 管理员用户管理 缺省管理员 接口 配置一个普通接口 创建安全区域 路由模式 透明模式 混合模式 防火墙的安全策略 防火墙转发流程 与传统包过滤的区别 创建安全策略 …...

阅读笔记:明朝那些事儿之拐弯中的帝国

​万历皇帝时期内阁首辅&#xff1a; 张居正&#xff0c;申时行&#xff0c;王锡爵&#xff0c;许国&#xff0c;王家屏&#xff0c;赵志皋&#xff08;给皇帝写辞职信没有回音&#xff0c;自己不告而回家&#xff09;&#xff0c;沈一贯&#xff0c;于慎行&#xff0c;叶向高…...

React基础知识 精简全面 推荐

这篇博文主要对一些刚入门react框架的同学&#xff0c;以及对react基本知识进行巩固的&#xff0c;最后就是精简一下基本知识&#xff0c;以方便自己查看&#xff0c;感谢参考&#xff0c;有问题评论区交流&#xff0c;谢谢。 目录 1.JSX 2.Props 和 State 3.组件生命周期…...

OV SSL证书申请指南

OV SSL证书除了验证域名所有权外还需要验证组织信息&#xff0c;这类证书适用于对公司官网、品牌、安全性等有较高程度要求的企业级用户。具体申请流程如下&#xff1a; 一 、注册账号 注册账号填写230919注册码即可获得大额优惠券和全程一对一技术支持https://www.joyssl.co…...

变色树脂的变色原理?变色树脂在水处理中的应用?

变色树脂是一种具有特殊功能的高分子材料&#xff0c;能够在特定条件下改变其颜色&#xff0c;从而指示环境变化&#xff08;如pH值、温度、特定离子浓度等&#xff09;或反应进程。这类树脂通常含有能够响应特定刺激的化学结构&#xff0c;通过化学反应、离子交换、分子构象变…...

16 敏捷开发实践(1)

敏捷方法&#xff1a;是一种从1990年代开始逐渐引起广泛关注的一些新型软件开发方法&#xff0c;是一种应对快速变化的需求的一种软件开发能力。 敏捷开发&#xff1a;是一种以人为核心、迭代、循序渐进的开发方法。 敏捷实践&#xff1a;精益软件开发&#xff08;LSD&#x…...

如何使用虚拟机如何安装 Kali Linux ?

1.下载虚拟机&#xff1a;https://www.virtualbox.org/wiki/Downloads 选择你的系统版本 2.下载kali linux系统镜像&#xff1a;https://www.kali.org/get-kali/#kali-virtual-machines 全部下载完成后&#xff0c;我们会得到以下文件&#xff01; 1.压缩Kali Linux压缩包 2.安…...

Yarn UI 时间问题,相差8小时

位置 $HADOOP_HOME/share/hadoop/yarn/hadoop-yarn-common-2.6.1.jar 查看 jar tf hadoop-yarn-common-2.6.1.jar |grep yarn.dt.plugins.js webapps/static/yarn.dt.plugins.js 解压 jar -xvf hadoop-yarn-common-2.6.1.jar webapps/static/yarn.dt.plugins.js inflated: we…...

【JavaWeb项目】——外卖订餐系统之登入、登入后显示餐品信息、用户注册、注销部分

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…...

怎么保护电脑文件夹?文件夹保护方法大盘点

文件夹是管理电脑数据的重要工具&#xff0c;可以有效避免数据混乱。而为了避免文件夹数据泄露&#xff0c;我们需要严格保护文件夹。下面我们就来盘点一下文件夹的保护方法。 文件夹隐藏 隐藏文件夹是一种简单有效的保护方式&#xff0c;通过隐藏文件夹来避免其他人发现&…...

Temporal(时效)模式01

Andy Carlson, Sharon Estepp, Martin Fowler 著&#xff0c;透明 译 抽象 在面向对象设计中&#xff0c;我们不断使用“对象”&#xff08;object&#xff09;这个词。对象不仅仅用来表现真实世界中存在的物件&#xff0c;它们也被用来表现那些曾经存在但已经消失了的物件&…...

C语言 -- 动态内存管理

C语言 -- 动态内存管理 1. 为什么要有动态内存分配2. malloc 和 free2.1 malloc2.2 free 3. calloc 和 realloc3.1 calloc3.2 realloc 4. 常见的动态内存的错误4.1 对NULL指针的解引用操作4.2 对动态开辟空间的越界访问4.3 对非动态开辟内存使用free释放4.4 使用free释放一块动…...

docker 篇

简单描述下&#xff0c;有时候真的要熟练&#xff0c;否者上了生产真的不知所措。 背景&#xff1a;有个项目上线了&#xff0c;依赖的项目没有上线&#xff0c;因此需要紧急发布&#xff0c;发现&#xff1a;打包环境有问题&#xff0c;第一、架构不一致&#xff0c;第二、环…...

汽车、能源、烟草、电力行业洞见:TDengine 用户大会亮点荟萃

近年来&#xff0c;随着物联网、车联网、工业互联网等前沿技术的迅猛发展&#xff0c;全球数据量呈指数级增长。作为大数据的一个重要组成部分&#xff0c;时序数据因其在实时监控、预测分析和智能决策中的独特优势&#xff0c;正逐步成为数字化转型的关键要素。尤其在 AI 时代…...

从零开始编写一个Chrome插件:详细教程

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119@qq.com] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? 专栏导…...

photoshop学习笔记——选区3 快速选择工具

快速选择工具 W shift W 在3种快速选择工具之间切换 对象选择工具 photoshop CC中没有这个工具&#xff0c;利用AI&#xff0c;将款选中的对象快速的提取选区&#xff0c;测试了一下&#xff0c;选区制作的非常nice快速选择工具 跟磁性套索类似&#xff0c;自动识别颜色相似…...

Centos7下安装配置最新版本Jenkins

1、基础环境配置 1.1 服务器下载Jenkins安装包 下载地址&#xff1a;Download and deploy 下载命令&#xff1a;wget https://get.jenkins.io/war-stable/2.452.3/jenkins.war 1.2 服务器安装配置JDK Jenkins 是基于 Java 语言开发的&#xff0c;因此需要 Java 运行环境支…...

c++ 构造函数与析构函数

本文参考菜鸟教程&#xff0c;仅作笔记用。 构造函数 构造函数&#xff08;Constructor&#xff09;是一种特殊的方法&#xff0c;用于在创建对象时进行初始化操作。构造函数的名称与类的名称是完全相同的&#xff0c;并且不会返回任何类型&#xff0c;也不会返回 void。在面…...

Lc63---1859将句子排序(排序)---Java版(未写完)

1.题目描述 2.思路 &#xff08;1&#xff09;首先将句子按空格分割成若干单词。 &#xff08;2&#xff09;每个单词的最后一个字符是它的位置索引。我们可以通过这个索引将单词恢复到正确的位置。 &#xff08;3&#xff09;按照单词的索引顺序排序这些单词。 &#xff08;4…...

centos7-8/redhat7-8一键安装配置vsftp服务

1.脚本介绍 1.1.介绍&#xff1a; linux下一键安装及配置vsftpd服务 &#xff0c;通过执行install.sh脚本&#xff0c;脚本会根据参数区域的值执行安装和配置vsftp服务&#xff0c;安装后会创建一个默认ftp用户wangxf密码wangxf2023 1、支持自定义安装(更改脚本内参数值) 2、…...

7月24日JavaSE学习笔记

序列化版本控制 序列化&#xff1a;将内存对象转换成序列&#xff08;流&#xff09;的过程 反序列化&#xff1a;将对象序列读入程序&#xff0c;转换成对象的方式&#xff1b;反序列化的对象是一个新的对象。 serialVersionUID 是一个类的序列化版本号 private static fin…...

微信小程序教程008:事件绑定

文章目录 事件绑定1、什么是事件2、小程序中常用事件3、事件对象的属性列表4、target和currentTarget的区别5、bindtap的语法格式6、在事件处理函数中为data中的数据赋值7、事件传参数8、bind:input语法格式9、实现文本框和data之间的数据同步事件绑定 1、什么是事件 事件是渲…...

企业级-实现Nginx的静态文件服务器映射

作者&#xff1a;fyupeng 技术专栏&#xff1a;☞ https://github.com/fyupeng 项目地址&#xff1a;☞ https://github.com/fyupeng/distributed-blog-system-api 留给读者 开发人员往往会经常需要通过浏览器下载文件、图片或者PDF或者缩略图等&#xff0c;这时候我们可以根据…...

CTF Web SQL注入 10000字详解

这里写目录标题 涉及的数据库知识unionorder bydatabase()information_schemalimit--空格注释replaceinto outfilelikeGROUP BYHAVINGGROUP BY、HAVING、WHERE之间的关系regexp 原理信息收集操作系统数据库判断注入点注入点类型POST注入数字型注入字符型注入搜索型注入Insert/u…...