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
145
146
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <cstring>
#include <unistd.h>
#include <cerrno>
#include <cstdlib>

using namespace std;

/**
* 建立TCP连接
* @param node 服务器域名或IP地址
* @param service 服务名或端口号
* @return 成功返回socket文件描述符,失败返回-1
*/
int tcpConnect(const char* node, const char* service) {
// 地址信息提示结构,用于指定getaddrinfo的查询条件
struct addrinfo hints;
// 存储查询结果的指针
struct addrinfo *result, *p;

// 初始化hints结构为0
memset(&hints, 0, sizeof(hints));
// 不指定地址族,支持IPv4和IPv6
hints.ai_family = AF_UNSPEC;
// 指定套接字类型为TCP
hints.ai_socktype = SOCK_STREAM;

// 解析域名和服务,获取地址信息
int err = getaddrinfo(node, service, &hints, &result);
if (err != 0) {
cerr << "解析地址失败: " << gai_strerror(err) << endl;
return -1;
}

int sockfd = -1;
// 遍历所有可能的地址,尝试创建连接
for (p = result; p != nullptr; p = p->ai_next) {
// 创建套接字
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
cerr << "创建套接字失败: " << strerror(errno) << ", 尝试下一个地址..." << endl;
continue;
}

// 尝试连接到服务器
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd); // 连接失败,关闭当前套接字
cerr << "连接失败: " << strerror(errno) << ", 尝试下一个地址..." << endl;
continue;
}

// 连接成功,退出循环
break;
}

// 释放地址信息结构占用的内存
freeaddrinfo(result);

// 检查是否成功建立连接
if (p == nullptr) {
cerr << "所有地址尝试均失败,无法建立连接" << endl;
return -1;
}

return sockfd;
}

/**
* 发送数据,确保所有数据都被发送
* @param sockfd 套接字文件描述符
* @param data 要发送的数据
* @param len 数据长度
* @return 成功返回true,失败返回false
*/
bool sendAll(int sockfd, const char* data, size_t len) {
size_t totalSent = 0; // 已发送的字节数
while (totalSent < len) {
ssize_t sent = send(sockfd, data + totalSent, len - totalSent, 0);
if (sent == -1) {
cerr << "发送数据失败: " << strerror(errno) << endl;
return false;
}
totalSent += sent;
}
return true;
}

int main(int argc, char * argv[]) {
// 目标服务器和服务
const char *node = "www.baidu.com";
const char *service = "http"; // HTTP默认端口80

// 建立TCP连接
int connfd = tcpConnect(node, service);
if (connfd == -1) {
cerr << "无法连接到 " << node << ":" << service << endl;
return 1;
}
cout << "成功连接到 " << node << ":" << service << endl;

// 构造HTTP GET请求
// 注意:相邻的字符串字面值会在编译时自动拼接
const char* request = "GET / HTTP/1.1\r\n"
"Host: www.baidu.com\r\n"
"User-Agent: Simple TCP Client\r\n"
"Connection: close\r\n\r\n";

// 发送HTTP请求
size_t requestLen = strlen(request);
cout << "发送HTTP请求,长度: " << requestLen << " 字节" << endl;
if (!sendAll(connfd, request, requestLen)) {
cerr << "发送HTTP请求失败" << endl;
close(connfd);
return 1;
}

// 接收并打印服务器响应
const size_t BUFFER_SIZE = 4096;
char buffer[BUFFER_SIZE];
ssize_t recvLen;

cout << "\n----- 开始接收服务器响应 -----\n" << endl;

while ((recvLen = recv(connfd, buffer, BUFFER_SIZE - 1, 0)) > 0) {
// 确保字符串正确终止
buffer[recvLen] = '\0';
// 输出接收到的数据
cout << buffer;
}

// 检查接收是否出现错误
if (recvLen == -1) {
cerr << "\n接收数据时发生错误: " << strerror(errno) << endl;
close(connfd);
return 1;
}

cout << "\n\n----- 服务器响应接收完毕 -----" << endl;

// 关闭连接
close(connfd);
cout << "连接已关闭" << endl;

return 0;
}