笔者在C++的学习过程中封装的一个简单的基于TCP 和 UDP 的网络通信类 Network
,具有创建服务器和客户端的基本功能。
1、主要功能
-
Socket 创建与初始化:
- 支持创建 TCP (
SOCK_STREAM
) 和 UDP (SOCK_DGRAM
) 协议的 socket。 - 通过构造函数初始化 socket,包括设置 IP 地址和端口号。
- 支持创建 TCP (
-
服务端功能:
- 支持 TCP 服务端绑定本地地址和端口,并监听连接请求。
- 能够接受客户端连接并返回一个新的
Network
对象,代表新的客户端会话。
-
客户端功能:
- 支持通过 TCP 连接到指定的服务端 IP 和端口。
-
数据发送与接收:
- 提供简便的方法发送和接收数据,无论是使用 UDP 还是 TCP。
- 支持发送反馈消息到客户端。
-
资源管理:
- 在析构函数中自动关闭 socket,确保资源及时释放。
-
获取客户端 IP:
- 通过
get_ip_nw
方法获取连接的客户端 IP 地址。
- 通过
2、使用场景
该类可以用于实现基本的网络服务,例如聊天应用、数据传输工具等,是学习网络编程的良好示范。
3、代码结构
-
Network::Network
构造函数:初始化网络,设置 socket。 -
Network::~Network
析构函数:关闭网络连接。 -
accept_nw
:接受客户端连接。 -
send_nw
和recv_nw
:处理数据的发送与接收。 -
send_feedback_nw
:发送反馈信息。 -
close_nw
:关闭 socket 确保资源释放。 -
get_ip_nw
:获取客户端的 IP 地址信息。
4、 代码
头文件:
#include <iostream>
#include <cstring>
#include <winsock2.h>
#include <cstdio>
#include <cstdlib>
#include <ws2tcpip.h>
class Network
{
private:
int type;//通信协议类型
int sockfd;//套接字描述符
struct sockaddr_in addr;//通信地址
socklen_t addrlen;//通信地址长度
bool is_svr;//是否是服务器端
public:
Network(int type,short port,const char* ip,bool is_svr);//构造函数,初始化套接字等信息
~Network();
Network* accept_nw();//只有tcp服务器端才有用,用于接受客户端连接
int send_nw(void* buf,size_t len);
int recv_nw(void* buf,size_t len);
int send_feedback_nw(void* feedback);
void close_nw();
const char* get_ip_nw();
};
成员函数:
#include "network.hpp"
Network::Network(int type, short port, const char *ip, bool is_svr) : type(type), is_svr(is_svr), sockfd(-1), addrlen(sizeof(sockaddr_in))
{
sockfd = socket(AF_INET, type, 0);
if (sockfd < 0)
{
perror("Network::Network: socket");
return;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);
if (is_svr)
{
if (type == SOCK_STREAM) // 是TCP服务端
{
if (bind(sockfd, (sockaddr *)&addr, addrlen))
{
perror("Network::Network: bind");
closesocket(sockfd);
return;
}
if (listen(sockfd, 10) < 0)
{
perror("Network::Network: listen");
closesocket(sockfd);
return;
}
}
else if (type == SOCK_DGRAM)
{
if (bind(sockfd, (sockaddr *)&addr, addrlen))
{
perror("Network::Network: bind");
closesocket(sockfd);
return;
}
}
}
else
{
if (type == SOCK_STREAM)
{
if (connect(sockfd, (sockaddr *)&addr, addrlen))
{
perror("Network::Network: connect");
closesocket(sockfd);
return;
}
}
}
}
Network::~Network()
{
close_nw();
}
Network *Network::accept_nw()
{
if (SOCK_STREAM != type || !is_svr)
{
perror("Network::accept_nw: not a server socket");
return NULL;
}
Network *nw = new Network(SOCK_STREAM, 0, nullptr, false); // 创建新的网络对象
if (!nw)
{
perror("Network::accept_nw: new Network");
return NULL;
}
memset(&nw->addr, 0, sizeof(nw->addr));
nw->addrlen = sizeof(sockaddr_in);
nw->sockfd = accept(sockfd, (sockaddr *)&nw->addr, &nw->addrlen); // 接收连接请求
if (nw->sockfd < 0)
{
perror("Network::accept_nw: accept");
delete nw;
return NULL;
}
return nw;
}
int Network::send_nw(void *buf, size_t len)
{
if (type == SOCK_DGRAM)
{
return sendto(sockfd, (const char *)buf, len, 0, (sockaddr *)&addr, addrlen);
}
else
{
return send(sockfd, (const char *)buf, len, 0);
}
}
int Network::recv_nw(void *buf, size_t len)
{
if (type == SOCK_DGRAM)
{
return recvfrom(sockfd, (char *)buf, len, 0, (sockaddr *)&addr, &addrlen);
}
else
{
return recv(sockfd, (char *)buf, len, 0);
}
}
int Network::send_feedback_nw(void *feedback)
{
return send_nw(feedback, strlen((const char *)feedback));
}
void Network::close_nw()
{
closesocket(sockfd);
}
const char *Network::get_ip_nw()
{
return inet_ntoa(addr.sin_addr);
}
5、简单的服务端使用示例
#include "network.hpp"
int main(int argc, const char *argv[])
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
std::cerr << "WSAStartup failed: " << WSAGetLastError() << std::endl;
return 0;
}
if (argc < 3)
{
std::cerr << "使用方法: " << argv[0] << " <端口号> <IP地址>" << std::endl;
return 1;
}
// 初始化服务端
Network *server = new Network(SOCK_STREAM, atoi(argv[1]), argv[2], true);
if (!server)
{
std::cerr << "无法初始化服务端" << std::endl;
return 1;
}
std::cout << "server端在端口 " << argv[1] << " 打开成功" << std::endl;
// 等待客户端连接
Network *client = server->accept_nw();
if (!client)
{
std::cerr << "无法等待连接client端" << std::endl;
server->close_nw();
delete server;
return 1;
}
std::cout << "client端连接:" << client->get_ip_nw() << std::endl;
char buffer[1024];
for (;;)
{
// 接收数据
int bytes_received = client->recv_nw(buffer, sizeof(buffer) - 1); // 留一个字节给'\0'
if (bytes_received < 0)
{
std::cerr << "接收数据失败" << std::endl;
break;
}
if (bytes_received == 0 || strcmp("quit", buffer) == 0)
{
std::cout << "client端:" << client->get_ip_nw() << " 取消连接" << std::endl;
break;
}
buffer[bytes_received] = '\0'; // 确保以'\0'结尾
std::cout << "接收到: " << buffer << std::endl;
// 发送反馈
char feedback[1024];
snprintf(feedback, sizeof(feedback), "return:%s", buffer); // 创建反馈信息
int bytes_sent = client->send_feedback_nw(feedback);
if (bytes_sent > 0)
{
std::cout << "Sent feedback: " << feedback << std::endl;
}
}
client->close_nw(); // 关闭客户端连接
server->close_nw(); // 关闭服务器连接
delete client; // 释放客户端对象
delete server; // 释放服务器对象
return 0;
}
6、总结
此 Network
类提供了一个简单清晰的接口来处理网络通信,让开发者可以轻松地建立服务器与客户端之间的联系,以及进行数据传输。