阅读量:0
声明 : 本代码是在Ubuntu环境下写的。
目 录
1.Socket协议简介:
Socket
是一种通信机制,它允许不同计算机之间的进程互相通信。在计算机网络中,Socket 提供了一种基于网络的、进程间通信的方式。Socket API(应用程序接口)被设计为跨平台的,这意味着几乎可以在任何操作系统上使用相同的接口来开发网络应用程序。
(1) Socket 的基本概念
- Socket:
- 一个 Socket 可以被看作是一个端点,用于网络中的两个进程之间的双向通信。每个 Socket 都有一个唯一的地址,通常包括 IP 地址和端口号。
- IP 地址:
- 用来标识网络中的设备。IPv4 地址由四个字节组成,每个字节表示为一个介于 0 到 255 之间的十进制数,并且用点分隔开。
- 端口号:
- 用来标识运行在同一台机器上的特定服务或应用程序。端口号是一个 16 位的数字,范围从 0 到 65535。
(2) Socket 的类型
- 流式套接字 (SOCK_STREAM):
- 提供面向连接的服务,基于 TCP 协议,保证数据无差错、按顺序到达。
- 数据报套接字 (SOCK_DGRAM):
- 提供无连接的服务,基于 UDP 协议,不保证数据的顺序性和可靠性。
(3) Socket 编程的基本步骤
- 创建 Socket:根据需要选择合适的 Socket 类型,例如 TCP 或 UDP。
- 绑定地址:对于服务器端,需要将 Socket 绑定到一个本地地址和端口。
- 监听连接:如果是 TCP 服务器,需要调用
listen()
函数进入监听状态。 - 接受连接:服务器端调用
accept()
接受来自客户端的连接请求。 - 数据交换:客户端和服务端之间通过
send()
和recv()
函数发送和接收数据。 - 关闭连接:完成数据交换后,双方需要关闭 Socket。
(4) Socket 在 C++ 中的应用
在 C++ 中,Socket 编程通常涉及到 POSIX 标准库的使用,这些库提供了创建和管理 Socket 的函数。例如,在 Linux 环境下,可以使用 <sys/socket.h>
和 <netinet/in.h>
头文件来编写 Socket 程序。
2. 客户端完整代码
/* summary:socket client author:main date:2024.08.26 */ // 引入头文件 #include <iostream> // 引入标准输入输出库,如 cout 等。 #include <stdio.h> // 引入标准输入输出库,如 printf 等。 #include <string.h> // 引入字符串处理库,如 strlen 等。 #include <unistd.h> // 引入 POSIX 标准的 API,如 fork, exec, waitpid 等。 #include <stdlib.h> // 引入标准库,如 exit 等。 #include <netdb.h> // 引入网络地址转换库,如 hostent 等。 #include <sys/socket.h> // 引入 socket API,如 socket, bind, listen, accept 等。 #include <arpa/inet.h> // 引入网络地址转换库,如 inet_addr 等。 #include <sys/types.h> // 引入系统类型定义库,如 pid_t 等。 #include<chrono> // 引入时间库,如 chrono 等。 #include<thread> // 引入线程库,如 thread 等。 using namespace std; // 引入标准命名空间,用于简化代码。 const char *ip_address = "255.255.255.255"; // 服务器IP地址 const char *port = "8080"; // 服务器端口号 /* @brief 延时函数 @param ms 延时时间,单位为毫秒 @return 无 */ void delay(int ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); } /* @brief 创建客户端类 @param ip_address 服务器IP地址 @param port 服务器端口号 @return 无 */ class SOCKET_CLIENT { public: int sockfd; int iret; // 定义返回值 /*--------------------------------------创建套接字-------------------------------------------------*/ int sock_create() { if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ // 如果创建失败返回-1 perror("socket"); // 输出错误信息 return -1; } } /*--------------------------------------连接服务器-------------------------------------------------*/ int sock_connect() { struct sockaddr_in servaddr; // 初始化服务器地址结构体 memset(&servaddr, 0, sizeof(servaddr)); // 将结构体清零 servaddr.sin_family = AF_INET; // 设置协议族为 IPv4 servaddr.sin_port = htons(atoi(port)); // 设置端口号 servaddr.sin_addr.s_addr = inet_addr(ip_address); // 设置 IP 地址 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) // 如果连接服务器失败返回-1 { perror("connect"); // 输出错误信息 close(sockfd); // 关闭套接字 return -1; } } /*--------------------------------------发送数据--------------------------------------------------*/ int sock_send(char*msg) { char buffer[1024]; // 创建缓冲区 - 栈 memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 size_t len = strlen(msg); // 获取消息长度 if (len >= sizeof(buffer)) { perror("消息长度超过缓冲区大小"); // 输出错误信息 return -1; } strncpy(buffer, msg, len); // 将消息复制到缓冲区 buffer[strlen(buffer)] = '\0'; // 添加字符串结束符 if ((iret = send(sockfd, buffer, strlen(buffer), 0)) <= 0) // 发送数据 { printf("数据发送错误,IRET=%d",iret); // 输出错误信息 return -1; } printf("send data is %s\n",buffer); // 打印发送的数据 memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 } /*--------------------------------------接收数据--------------------------------------------------*/ int sock_recv() { char buffer[1024]; // 创建缓冲区 memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 if ((iret = recv(sockfd, buffer, sizeof(buffer), 0)) <= 0) // 接收数据 { printf("数据接受错误,iret=%d",iret); // 打印错误信息 return -1; } printf("recv data is %s",buffer); // 打印接收到的数据 memset(buffer, 0, sizeof(buffer)); // 清空缓冲区 return 0; } /*--------------------------------------关闭套接字------------------------------------------------*/ void sock_close() { close(sockfd); // 关闭套接字 } }; class JSON_DATA { public: string facility_1(char time, char HS, char TEMP, char ATM, char HLux, char LLux){ return "{" "\'imei\':\'***************\'," // 设备IMEI "\'time\':\'" + to_string(time) + "\'," // 上报时间 "\'name\':\'WeatherSensors\'," // 气象传感器 "\'data\':{" "\'HS\':\'" + to_string(HS) + "\'," // 湿度值 "\'TEMP\':\'" + to_string(TEMP) + "\'," // 湿度值 "\'ATM\':\'" + to_string(ATM) + "\'," // 气压值 "\'HLux\':\'" + to_string(HLux) + "\'," // 光照最高值 "\'LLux\':\'" + to_string(LLux) + "\'}" // 光照最低值 "}"; } string facility_2(char time, char PPM, char TEMP_2, char EC, char PH){ return "{" "\'imei\':\'***************\'," // 设备IMEI "\'time\':\'" + to_string(time) + "\'," // 上报时间 "\'name\':\'SoilSensor\'," // 土壤传感器 "\'data\':{" "\'PPM\':\'" + to_string(PPM) + "\'," // 水分值 "\'TEMP\':\'" + to_string(TEMP_2) + "\'," // 温度值 "\'EC\':\'" + to_string(EC) + "\'," // 导电率 "\'PH\':\'" + to_string(PH) + "\'}" // PH值 "}"; } string facility_3(char time, char PRCP){ return "{" "\'imei\':\'***************\'," // 设备IMEI "\'time\':\'" + to_string(time) + "\'," // 上报时间 "\'name\':\'RainSensor\'," // 雨量传感器 "\'data\':{" "\'PRCP\':\'" + to_string(PRCP) + "\'}" // 雨量值 "}"; } }; int main() { SOCKET_CLIENT client1; // 创建客户端对象 JSON_DATA json_socket; // 创建JSON数据对象 string json_data; // JSON数据 字符串 if (client1.sock_create() == -1) return -1; // 创建套接字 if (client1.sock_connect() == -1) return -1; // 连接服务器 json_data = json_socket.facility_2(time(NULL), 50, 25, 10, 10); // 赋值JSON数据 client1.sock_send((char*)json_data.c_str()); // 发送数据 client1.sock_recv(); // 接收数据 client1.sock_close(); // 关闭套接字 }
3. 注意事项
(1) 将如图所示的位置的参数使用时修改为自己的。
(2)我运行代码时所使用的IP和端口号是对方给我提供的(这里的255.255.255.255,8080是我随意填写的),是正常链接对方的服务端的。
(3) 我用Python写了一个服务端,用我写的Python版本的客户端是正常连接该服务端的。但使用上述C++写的客户端连接该服务端时总是显示拒绝连接。(由于时间关系就没去排查问题,后续有时间排查了,给大伙奉上结果)。
(4) 这里的设备IMEI按照自己的实际情况填写。
(5) 有疑问在评论区提问或者私信即可,看到了第一时间回复。