Cpp网络通信02-基本的Socket封装

Cpp网络通信02-基本的Socket封装

封装 TcpServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// TcpServer.h
#pragma once

#include <cstdint>
#include <string>

namespace Net {

using namespace std;

class TcpServer {
private:
int32_t m_ListenFd; // 用于监听的socket,-1:未初始化
int32_t m_ClientFd; // 客户端连接上来的socket, -1:客户端未连接
string m_ClientIp;
uint16_t m_Port;
public:
TcpServer();
~TcpServer();

bool Init(const uint16_t inPort);
bool Accept();
bool Send(const string& buffer);
bool Recv(string& buffer, const size_t maxLen);
bool CloseClient();
bool Close();

const string& GetClientIp() const { return m_ClientIp; }
};

}

// TcpServer.cpp
#include "TcpServer.h"

#include <arpa/inet.h>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <netinet/in.h>
#include <string>
#include <sys/socket.h>
#include <unistd.h>

namespace Net {

TcpServer::TcpServer()
: m_ListenFd(-1), m_ClientFd(-1) {}

TcpServer::~TcpServer() {
CloseClient();
Close();
}

bool TcpServer::Init(const uint16_t inPort) {
// 如果已经初始化(进入监听状态),则直接返回失败
if (m_ListenFd != -1) return false;

// 1.创建服务端的socket
// AF_INET: ipv4 SOCK_STREAM: 流模式(TCP)
m_Port = inPort;

m_ListenFd = socket(AF_INET, SOCK_STREAM, 0);
if (m_ListenFd == -1) {
perror("socket");
return false;
}

// 2.把服务端用于通信的IP和端口绑定到socket上
struct sockaddr_in serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET; // ipv4
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 表示接收所有地址的请求
serverAddr.sin_port = htons(m_Port); // 服务器端口
// 绑定
if (bind(m_ListenFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
perror("bind");
close(m_ListenFd);
return false;
}

// 3.把socket设置为监听状态
if (listen(m_ListenFd, 5) != 0) {
perror("listen");
close(m_ListenFd);
return false;
}

return true;
}

bool TcpServer::Accept() {
// 4.手里客户端的连接请求, accept是阻塞的
struct sockaddr_in clientAddr; // 客户端的地址信息
socklen_t addrLen = sizeof(clientAddr);

if ((m_ClientFd = accept(m_ListenFd, (struct sockaddr*)&clientAddr, &addrLen)) == -1) {
return false;
}
m_ClientIp = inet_ntoa(clientAddr.sin_addr); // 把客户端的地址从大端序转换成字符串

return true;
}

bool TcpServer::Send(const string& buffer) {
if (m_ClientFd == -1) return false;
if ((send(m_ClientFd, buffer.data(), buffer.size(), 0)) <= 0) return false;
return true;
}

bool TcpServer::Recv(string& buffer, const size_t maxLen) {
buffer.clear();
buffer.resize(maxLen);
int n = recv(m_ClientFd, &buffer[0], buffer.size(), 0);
if (n <= 0 ) {
buffer.clear();
return false;
}
buffer.resize(n);

return true;
}

bool TcpServer::CloseClient() {
if (m_ClientFd == -1) return false;

close(m_ClientFd);
m_ClientFd = -1;

return true;
}

bool TcpServer::Close() {
if (m_ListenFd == -1) return false;

close(m_ListenFd);
m_ClientFd = -1;

return true;
}

}

封装 TcpClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// TcpClient.h
#pragma once

#include <cstdint>
#include <string>

namespace Net {

using namespace std;

class TcpClient {
private:
int32_t m_ClientFd;
string m_Ip;
uint16_t m_Port;
public:
TcpClient();
~TcpClient();

bool Connect(const string& inIp, const uint16_t inPort);
bool Send(const string& buffer);
bool Recv(string& buffer, const size_t maxLen);
bool Close();
};

}

// TcpClient.cpp
#include "TcpClient.h"

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <iterator>
#include <string>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>

namespace Net {

TcpClient::TcpClient()
: m_ClientFd(-1) {}

TcpClient::~TcpClient()
{
Close();
}

bool TcpClient::Connect(const string& inIp, const uint16_t inPort) {
// 如果已连接,直接失败
if (m_ClientFd != -1) return false;

m_Ip = inIp;
m_Port = inPort;

// 1.创建客户端的socket
m_ClientFd = socket(AF_INET, SOCK_STREAM, 0);
if (m_ClientFd == -1) {
perror("socket");
return false;
}

// 2.向服务器发起连接请求
struct hostent* h; // 用于存放服务端ip
if ((h = gethostbyname(m_Ip.c_str())) == 0) {
close(m_ClientFd);
m_ClientFd = -1;
return false;
}

struct sockaddr_in serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET; // 协议族
memcpy(&serverAddr.sin_addr, h->h_addr, h->h_length); // IP地址
serverAddr.sin_port = htons(m_Port); // 端口

if (connect(m_ClientFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
return false;
}

return true;
}

bool TcpClient::Send(const string& buffer) {
if (m_ClientFd == -1) return false;

if ((send(m_ClientFd, buffer.data(), buffer.size(), 0)) <= 0) {
return false;
}

return true;
}

bool TcpClient::Recv(string& buffer, const size_t maxLen) {
buffer.clear();
buffer.resize(maxLen);
int n = recv(m_ClientFd, &buffer[0], buffer.size(), 0);
if (n <= 0 ) {
buffer.clear();
return false;
}
buffer.resize(n);

return true;
}

bool TcpClient::Close() {
if (m_ClientFd == -1) return false;

close(m_ClientFd);

return true;
}

}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Server.cpp
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include <sys/socket.h>

#include "network/TcpServer.h"

using namespace std;

int main(int argc, char *argv[]) {

if (argc != 2) {
printf("usage:\n\tServer <port>\n");
return -1;
}

// 创建服务端实例
Net::TcpServer server;

// 初始化服务端,并开始监听端口
if (!server.Init(atoi(argv[1]))) {
perror("TcpServer::Init()");
return -1;
}
cout << "服务器端口: " << argv[1] << endl;

// 服务端开始受理客户端连接
if (!server.Accept()) {
perror("TcpServer::Accept()");
return -1;
}
cout << "客户端已连接: " << server.GetClientIp() << endl;

string buffer;
while (true) {
// 接收对端的报文
if (!server.Recv(buffer, 1024)) {
perror("TcpServer::Recv()");
break;
}
cout << "接收: " << buffer << endl;

buffer = "ok";

if (!server.Send(buffer)) {
perror("TcpServer::Send()");
break;
}
cout << "发送: " << buffer << endl;
}

return 0;
}



// Client.cpp
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>

#include "network/TcpClient.h"

using namespace std;

int main(int argc, char *argv[]) {

if (argc != 3) {
cout << "Usage:\n\t./Client <服务端IP> <服务端端口>\n";
return -1;
}

Net::TcpClient client;

if (!client.Connect(argv[1], atoi(argv[2]))) {
perror("TcpClient::Connect()");
return -1;
}


// 3.与服务器通信,发送一个请求报文后等待回复,然后再发下一个报文
string buffer;
for (int i = 0; i < 3; i++) {
buffer = "这是第" + to_string(i+1) + "条报文";

if (!client.Send(buffer)) {
perror("TcpClient::Send()");
break;
}
cout << "发送: " << buffer << endl;

// 接收服务端的回应,recv会阻塞
if (!client.Recv(buffer, 1024)) {
break;
}
cout << "接收: " << buffer << endl;
}

return 0;
}


Cpp网络通信02-基本的Socket封装
https://chordfish-k.github.io/2024/12/30/cpp/cpp-webserver02/
作者
超弦鱼
发布于
2024年12月30日
许可协议