[toc]

用C语言使用TCP协议发送文本消息

1.用C语言使用TCP协议发送一句文本消息

1.使用C语言在Linux中创建服务端程序接收一句文本消息

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
// LinServer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for close()
#include <arpa/inet.h> // for sockaddr_in, inet_ntop
#include <sys/socket.h> // for socket(), bind(), listen(), accept()

int main() {
int serverSocket, clientSocket;
struct sockaddr_in server, client;
socklen_t clientSize;
char buffer[1024];
int recvSize;

// 创建 socket
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0) {
perror("Could not create socket");
return 1;
}

// 设置服务器信息
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY; // 监听所有可用的接口
server.sin_port = htons(8888); // 服务器端口

// 绑定 socket
if (bind(serverSocket, (struct sockaddr*)&server, sizeof(server)) < 0) {
perror("Bind failed");
close(serverSocket);
return 1;
}

// 开始监听
if (listen(serverSocket, 3) < 0) {
perror("Listen failed");
close(serverSocket);
return 1;
}
printf("Waiting for incoming connections...\n");

clientSize = sizeof(client);
// 接受连接
clientSocket = accept(serverSocket, (struct sockaddr*)&client, &clientSize);
if (clientSocket < 0) {
perror("Accept failed");
close(serverSocket);
return 1;
}

char clientIP[INET_ADDRSTRLEN]; // 用于存储客户端 IP 地址
inet_ntop(AF_INET, &client.sin_addr, clientIP, sizeof(clientIP)); // 使用 inet_ntop
printf("Connection accepted from %s:%d\n", clientIP, ntohs(client.sin_port));

// 接收消息
while ((recvSize = recv(clientSocket, buffer, sizeof(buffer) - 1, 0)) > 0) {
buffer[recvSize] = '\0'; // 添加字符串结束符
printf("Received message: %s\n", buffer);
}

if (recvSize < 0) {
perror("Receive failed");
}

// 关闭 sockets
close(clientSocket);
close(serverSocket);
return 0;
}

2.使用C语言在windows中创建客户端程序发送一句文本消息

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
//WinClient.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib") // 链接 Winsock 库

int main() {
WSADATA wsaData;
SOCKET sock;
struct sockaddr_in server;
char* message;
int sendResult;

// 初始化 Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("Failed to initialize Winsock. Error Code: %d\n", WSAGetLastError());
return 1;
}

// 创建 socket
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
printf("Could not create socket. Error Code: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}

// 设置服务器信息
server.sin_family = AF_INET;
server.sin_port = htons(8888); // 服务器端口

// 使用 inet_pton 将 IP 地址转换为网络字节顺序
if (inet_pton(AF_INET, "10.0.2.107", &server.sin_addr) <= 0) {
printf("Invalid address/ Address not supported \n");
closesocket(sock);
WSACleanup();
return 1;
}

// 连接到服务器
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0) {
printf("Connection failed. Error Code: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}

// 要发送的消息
message = "Hello, Server!";

// 发送消息
sendResult = send(sock, message, strlen(message), 0);
if (sendResult == SOCKET_ERROR) {
printf("Send failed. Error Code: %d\n", WSAGetLastError());
}
else {
printf("Message sent: %s\n", message);
}

// 关闭 socket
closesocket(sock);
WSACleanup();
return 0;
}

2.用C语言使用TCP协议让两个客户端之间可以相互发送文本消息

1.使用C语言在Linux中创建服务端接收与转发文本消息到所有客户端中

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
// LinServer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for close()
#include <arpa/inet.h> // for sockaddr_in, inet_ntop
#include <pthread.h> // for pthreads
#include <errno.h>

#define MAX_CLNT 256
#define BUF_SIZE 100

void error_handling(const char* msg);
void* ThreadProc(void* arg);
void send_msg(char* msg, int len);
int clnt_cnt = 0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int main() {
int serv_sock;
struct sockaddr_in sin;

// 创建套接字
serv_sock = socket(AF_INET, SOCK_STREAM, 0);
if (serv_sock < 0)
error_handling("Failed socket()");

// 设置服务器地址结构
sin.sin_family = AF_INET;
sin.sin_port = htons(8888); // 监听端口 8888
sin.sin_addr.s_addr = INADDR_ANY; // 监听所有可用的接口

// 绑定套接字
if (bind(serv_sock, (struct sockaddr*)&sin, sizeof(sin)) < 0)
error_handling("Failed bind()");

// 开始监听
if (listen(serv_sock, 256) < 0)
error_handling("Failed listen()");
printf("Start listen:\n");

struct sockaddr_in remoteAddr;
socklen_t addrLen = sizeof(remoteAddr);
pthread_t tid;

while (1) {
printf("Waiting for new connection...\n");
int clnt_sock = accept(serv_sock, (struct sockaddr*)&remoteAddr, &addrLen);
if (clnt_sock < 0) {
perror("Failed accept()");
continue;
}

// 添加客户端套接字到数组
pthread_mutex_lock(&mutex);
clnt_socks[clnt_cnt++] = clnt_sock;
pthread_mutex_unlock(&mutex);

char ip_str[INET_ADDRSTRLEN]; // 用于存储 IP 地址字符串
inet_ntop(AF_INET, &remoteAddr.sin_addr, ip_str, sizeof(ip_str));
printf("Connection accepted: %s\n", ip_str);

// 创建线程处理客户端
pthread_create(&tid, NULL, ThreadProc, (void*)&clnt_sock);
pthread_detach(tid); // 使线程在结束时自动释放资源
}

close(serv_sock);
return 0;
}

void error_handling(const char* msg) {
perror(msg);
exit(1);
}

void* ThreadProc(void* arg) {
int clnt_sock = *((int*)arg);
int str_len = 0;
char msg[BUF_SIZE];

// 接收消息并广播
while ((str_len = recv(clnt_sock, msg, sizeof(msg), 0)) > 0) {
send_msg(msg, str_len);
printf("Broadcast message sent\n");
}

printf("Client disconnected: %d\n", clnt_sock);
pthread_mutex_lock(&mutex);
// 从客户端数组中移除断开的客户端
for (int i = 0; i < clnt_cnt; i++) {
if (clnt_sock == clnt_socks[i]) {
while (i < clnt_cnt - 1) {
clnt_socks[i] = clnt_socks[i + 1];
i++;
}
break;
}
}
clnt_cnt--;
pthread_mutex_unlock(&mutex);
close(clnt_sock);
return NULL;
}

void send_msg(char* msg, int len) {
pthread_mutex_lock(&mutex);
// 向所有客户端发送消息
for (int i = 0; i < clnt_cnt; i++) {
send(clnt_socks[i], msg, len, 0);
}
pthread_mutex_unlock(&mutex);
}

2.使用C语言在windows中创建客户端程序接收与发送文本消息

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
//WinClient.c
#include <winsock2.h>
#include <ws2tcpip.h> // 包含 inet_ntop 和 inet_pton 的定义
#include <stdio.h>
#include <windows.h>
#include <string.h>
#pragma comment(lib,"WS2_32.lib")

#pragma warning(disable:4996)
#define BUF_SIZE 256
#define NAME_SIZE 30

DWORD WINAPI send_msg(LPVOID lpParam);
DWORD WINAPI recv_msg(LPVOID lpParam);
void error_handling(const char* msg);

char name[NAME_SIZE] = "[DEFAULT]";
char msg[BUF_SIZE];

int main()
{
HANDLE hThread[2];
DWORD dwThreadId;
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 2);
WSAStartup(sockVersion, &wsaData);

printf("Input your Chat Name: ");
scanf("%s", name);
getchar(); // 接收换行符

SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
error_handling("Failed socket()");

struct sockaddr_in servAddr; // 使用 sockaddr_in 结构体
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(8888);

// 使用 inet_pton 替代 inet_addr
if (inet_pton(AF_INET, "10.0.2.107", &servAddr.sin_addr) <= 0) {
error_handling("Invalid address/ Address not supported");
}

if (connect(sock, (struct sockaddr*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
error_handling("Failed connect()");
printf("connect success\n");

hThread[0] = CreateThread(
NULL,
NULL,
send_msg,
(LPVOID)&sock,
0,
&dwThreadId);
hThread[1] = CreateThread(
NULL,
NULL,
recv_msg,
(LPVOID)&sock,
0,
&dwThreadId);

WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
printf("Thread Over, Enter anything to over.\n");
getchar();
closesocket(sock);
WSACleanup();
return 0;
}

DWORD WINAPI send_msg(LPVOID lpParam)
{
int sock = *((int*)lpParam);
char name_msg[NAME_SIZE + BUF_SIZE];
while (1)
{
fgets(msg, BUF_SIZE, stdin);
if (!strcmp(msg, "q\n") || !strcmp(msg, "Q\n"))
{
closesocket(sock);
exit(0);
}
sprintf(name_msg, "[%s]: %s", name, msg);
send(sock, name_msg, strlen(name_msg), 0);
}
return NULL;
}

DWORD WINAPI recv_msg(LPVOID lpParam)
{
int sock = *((int*)lpParam);
char name_msg[NAME_SIZE + BUF_SIZE];
int str_len;
while (1)
{
str_len = recv(sock, name_msg, NAME_SIZE + BUF_SIZE - 1, 0);
if (str_len == -1)
return -1;
name_msg[str_len] = 0;
fputs(name_msg, stdout);
}
return NULL;
}

void error_handling(const char* msg)
{
printf("%s\n", msg);
WSACleanup();
exit(1);
}

3.运行结果

  1. 在Linux中运行LinuxServer

  2. 在windows中运行WinClient.exe

  3. 运行结果符合预期

3. 使用http协议通信

1. Liunx中创建LinServer.c

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
// LinServer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>

#define PORT 8080
#define BUFFER_SIZE 1024
#define BASE_PATH "/home/kali/cc++/http/" // 基础文件路径,确保使用Linux格式的路径

void handle_request(int client_socket) {
char buffer[BUFFER_SIZE];
int bytes_received = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0'; // 添加字符串结束符
printf("Received request:\n%s\n", buffer);

// 解析请求行
char method[16], path[256], version[16];
sscanf(buffer, "%s %s %s", method, path, version);

// 响应内容
const char* response_header;
char response_body[BUFFER_SIZE];
FILE* file;

// 根据请求路径返回不同的响应
if (strcmp(method, "GET") == 0) {
// 构建文件路径
char file_path[BUFFER_SIZE];
snprintf(file_path, sizeof(file_path), "%s%s", BASE_PATH, path + 1); // 路径前加上基础路径,去掉前面的'/'

// 打开文件并读取内容
file = fopen(file_path, "r");
if (file) {
// 读取文件内容
size_t bytes_read = fread(response_body, sizeof(char), sizeof(response_body) - 1, file);
response_body[bytes_read] = '\0'; // 确保字符串结束
fclose(file);

// 确定 Content-Type
const char* content_type;
if (strstr(path, ".html")) {
content_type = "text/html";
}
else if (strstr(path, ".css")) {
content_type = "text/css";
}
else if (strstr(path, ".js")) { // 添加对JavaScript文件的支持
content_type = "application/javascript";
}
else {
content_type = "application/octet-stream"; // 默认类型
}

// 发送响应头
response_header = "HTTP/1.1 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %lu\r\n"
"Connection: close\r\n\r\n";
char header[BUFFER_SIZE];
sprintf(header, response_header, content_type, bytes_read);
send(client_socket, header, strlen(header), 0);
send(client_socket, response_body, bytes_read, 0);
}
else {
// 文件未找到
response_header = "HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n\r\n";
const char* error_body = "<html><body><h1>404 Not Found</h1></body></html>";
send(client_socket, response_header, strlen(response_header), 0);
send(client_socket, error_body, strlen(error_body), 0);
}
}
}

// 关闭客户端套接字
close(client_socket);
}

int main() {
int server_socket, client_socket;
struct sockaddr_in server, client;
socklen_t client_len = sizeof(client);

// 创建套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
perror("Could not create socket");
return 1;
}

// 设置服务器信息
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY; // 监听所有IP
server.sin_port = htons(PORT);

// 绑定套接字
if (bind(server_socket, (struct sockaddr*)&server, sizeof(server)) < 0) {
perror("Bind failed");
close(server_socket);
return 1;
}

// 开始监听
listen(server_socket, SOMAXCONN);
printf("Server listening on port %d...\n", PORT);

// 主循环,接受客户端连接
while (1) {
client_socket = accept(server_socket, (struct sockaddr*)&client, &client_len);
if (client_socket < 0) {
perror("Accept failed");
continue;
}

// 处理请求
handle_request(client_socket);
}

// 关闭服务器套接字
close(server_socket);
return 0;
}

2.windows创建Winclient.c

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
//Winclient.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <conio.h>

#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable:4996)

#define SERVER_IP "10.0.2.107" // 服务器IP地址
#define PORT 8080
#define BUFFER_SIZE 4096

void set_console_utf8() {
// 设置控制台为UTF-8编码
SetConsoleOutputCP(CP_UTF8);
}

void print_html_text(const char* response) {
const char* pos = response;
int in_tag = 0; // 标记是否在标签内

while (*pos) {
if (*pos == '<') {
in_tag = 1; // 进入标签
}
else if (*pos == '>') {
in_tag = 0; // 退出标签
}
else if (!in_tag) {
// 如果不在标签内,打印字符
putchar(*pos);
}
pos++;
}
}

void send_request(const char* path) {
WSADATA wsaData;
SOCKET sock;
struct sockaddr_in server;
char request[BUFFER_SIZE];
char response[BUFFER_SIZE];
int bytes_received;
int header_end = 0; // 标记HTTP头部结束的位置

// 初始化WinSock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("Failed to initialize WinSock. Error Code: %d\n", WSAGetLastError());
return;
}

// 创建套接字
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
printf("Could not create socket. Error Code: %d\n", WSAGetLastError());
WSACleanup();
return;
}

// 设置服务器信息
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(SERVER_IP);
server.sin_port = htons(PORT);

// 连接到服务器
if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0) {
printf("Connect failed. Error Code: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return;
}

// 构建HTTP GET请求
sprintf(request,
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Connection: close\r\n"
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\r\n"
"DNT: 1\r\n"
"Accept: */*\r\n"
"Referer: http://%s/First/First.html\r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,zh-TW;q=0.6\r\n"
"\r\n",
path, SERVER_IP, SERVER_IP);

// 发送请求
send(sock, request, strlen(request), 0);

// 接收响应
while ((bytes_received = recv(sock, response, sizeof(response) - 1, 0)) > 0) {
response[bytes_received] = '\0'; // 添加字符串结束符

// 查找HTTP头部结束的位置
if (!header_end) {
char* header_end_pos = strstr(response, "\r\n\r\n");
if (header_end_pos) {
header_end = 1; // 找到头部结束
*header_end_pos = '\0'; // 将头部结束符替换为字符串结束符
printf("HTTP Response Headers:\n%s\n", response); // 打印HTTP头部
print_html_text(header_end_pos + 4); // 打印HTML文本内容
}
else {
printf("%s", response); // 打印接收到的内容
}
}
else {
// 头部已经结束,直接打印HTML文本内容
print_html_text(response);
}
}

// 关闭套接字
closesocket(sock);
WSACleanup();
}


int main() {
char path[256];

// 设置控制台为UTF-8编码
set_console_utf8();

// 输入请求路径
printf("Enter the path to request (e.g., /index.html): ");
scanf("%s", path);

// 发送请求
send_request(path);
getche();
return 0;
}

3.linux中创建First.html

在当前目录下创建文件夹与文件,当前路径为/home/kali/cc++/http/,完整路径为/home/kali/cc++/http/First/First.html

First.html

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>简单的JavaScript Hello World</title>
<script src="example.js"></script>
</head>
<body>
    HTML内容……
</body>
</html>

example.js

1
2
3
4

document.write("Hello, world!"); // 直接插入页面中
alert("Hello, world!"); // 弹窗显示
console.log("Hello, world!"); // 在控制台(console)里显示,需要先开启开发工具控制台

4.运行结果

  1. 在Linux中运行服务端

  2. 在Windows中运行客户端,运行后输入/First/First.html

  3. 或者在浏览器中访问http://10.0.2.107:8080/First/First.html

  4. 结果符合预期

4. 用C语言使用TCP协议让两个客户端之间可以相互发送文本消息,同时支持ipv4与ipv6以及域名解析

1.使用C语言在Linux中创建服务端接收与转发文本消息到所有客户端中,同时支持ipv4与ipv6,ipv6优先。

要使服务器同时支持 IPv4 和 IPv6,需要创建两个套接字:一个用于 IPv4,另一个用于 IPv6

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
// LinServer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for close()
#include <arpa/inet.h> // for sockaddr_in, sockaddr_in6, inet_ntop
#include <pthread.h> // for pthreads
#include <errno.h>

#define MAX_CLNT 256
#define BUF_SIZE 100

void error_handling(const char* msg);
void* ThreadProc(void* arg);
void send_msg(char* msg, int len);
int clnt_cnt = 0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int main() {
int serv_sock;
struct sockaddr_in6 sin; // 使用 IPv6 地址结构

// 创建套接字
serv_sock = socket(AF_INET6, SOCK_STREAM, 0); // 创建 IPv6 套接字
if (serv_sock < 0)
error_handling("Failed socket()");

// 设置服务器地址结构
memset(&sin, 0, sizeof(sin));
sin.sin6_family = AF_INET6;
sin.sin6_port = htons(8888); // 监听端口 8888
sin.sin6_addr = in6addr_any; // 监听所有可用的接口

// 绑定套接字
if (bind(serv_sock, (struct sockaddr*)&sin, sizeof(sin)) < 0)
error_handling("Failed bind()");

// 开始监听
if (listen(serv_sock, 256) < 0)
error_handling("Failed listen()");
printf("Start listen:\n");

struct sockaddr_in6 remoteAddr; // 使用 IPv6 地址结构
socklen_t addrLen = sizeof(remoteAddr);
pthread_t tid;

while (1) {
printf("Waiting for new connection...\n");
int clnt_sock = accept(serv_sock, (struct sockaddr*)&remoteAddr, &addrLen);
if (clnt_sock < 0) {
perror("Failed accept()");
continue;
}

// 添加客户端套接字到数组
pthread_mutex_lock(&mutex);
clnt_socks[clnt_cnt++] = clnt_sock;
pthread_mutex_unlock(&mutex);

char ip_str[INET6_ADDRSTRLEN]; // 用于存储 IP 地址字符串
inet_ntop(AF_INET6, &remoteAddr.sin6_addr, ip_str, sizeof(ip_str));
printf("Connection accepted: %s\n", ip_str);

// 创建线程处理客户端
pthread_create(&tid, NULL, ThreadProc, (void*)&clnt_sock);
pthread_detach(tid); // 使线程在结束时自动释放资源
}

close(serv_sock);
return 0;
}

void error_handling(const char* msg) {
perror(msg);
exit(1);
}

void* ThreadProc(void* arg) {
int clnt_sock = *((int*)arg);
int str_len = 0;
char msg[BUF_SIZE];

// 接收消息并广播
while ((str_len = recv(clnt_sock, msg, sizeof(msg), 0)) > 0) {
send_msg(msg, str_len);
printf("Broadcast message sent\n");
}

printf("Client disconnected: %d\n", clnt_sock);
pthread_mutex_lock(&mutex);
// 从客户端数组中移除断开的客户端
for (int i = 0; i < clnt_cnt; i++) {
if (clnt_sock == clnt_socks[i]) {
while (i < clnt_cnt - 1) {
clnt_socks[i] = clnt_socks[i + 1];
i++;
}
break;
}
}
clnt_cnt--;
pthread_mutex_unlock(&mutex);
close(clnt_sock);
return NULL;
}

void send_msg(char* msg, int len) {
pthread_mutex_lock(&mutex);
// 向所有客户端发送消息
for (int i = 0; i < clnt_cnt; i++) {
send(clnt_socks[i], msg, len, 0);
}
pthread_mutex_unlock(&mutex);
}

2.使用C语言在windows中创建客户端程序接收与发送文本消息,同时支持ipv4与ipv6,并且支持通过域名与计算机名称访问

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
// WinClient.c
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <windows.h>
#include <string.h>
#pragma comment(lib,"WS2_32.lib")

#pragma warning(disable:4996)
#define BUF_SIZE 256
#define NAME_SIZE 30

// 常量定义
#define SERVER_PORT "8888"
#define DEFAULT_NAME "[DEFAULT]"

// 函数声明
DWORD WINAPI send_msg(LPVOID lpParam);
DWORD WINAPI recv_msg(LPVOID lpParam);
void error_handling(const char* msg);
SOCKET create_socket(const char* server_address);
void connect_to_server(SOCKET sock, const char* server_address);

int main()
{
HANDLE hThread[2];
DWORD dwThreadId;
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 2);
WSAStartup(sockVersion, &wsaData);

char name[NAME_SIZE];
printf("Input your Chat Name: ");
scanf("%s", name);
getchar(); // 接收换行符

char server_address[INET6_ADDRSTRLEN];
printf("Input server address (IPv4/IPv6 or domain name): ");
scanf("%s", server_address);

SOCKET sock = create_socket(server_address);
connect_to_server(sock, server_address);

hThread[0] = CreateThread(NULL, NULL, send_msg, (LPVOID)&sock, 0, &dwThreadId);
hThread[1] = CreateThread(NULL, NULL, recv_msg, (LPVOID)&sock, 0, &dwThreadId);

WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
printf("Thread Over, Enter anything to over.\n");
getchar();
closesocket(sock);
WSACleanup();
return 0;
}

SOCKET create_socket(const char* server_address) {
SOCKET sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); // 创建 IPv6 套接字
if (sock == INVALID_SOCKET)
error_handling("Failed socket()");
return sock;
}

void connect_to_server(SOCKET sock, const char* server_address) {
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // 支持 IPv4 和 IPv6
hints.ai_socktype = SOCK_STREAM;

if (getaddrinfo(server_address, SERVER_PORT, &hints, &res) != 0) {
error_handling("getaddrinfo failed");
}

// 连接到第一个可用的地址
for (struct addrinfo* p = res; p != NULL; p = p->ai_next) {
if (connect(sock, p->ai_addr, (int)p->ai_addrlen) == SOCKET_ERROR) {
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
break; // 连接成功
}

freeaddrinfo(res); // 释放 addrinfo 结构
if (sock == INVALID_SOCKET)
error_handling("Failed connect()");
printf("connect success\n");
}

DWORD WINAPI send_msg(LPVOID lpParam)
{
SOCKET sock = *((SOCKET*)lpParam);
char name_msg[NAME_SIZE + BUF_SIZE];
char msg[BUF_SIZE];

while (1)
{
fgets(msg, BUF_SIZE, stdin);
if (!strcmp(msg, "q\n") || !strcmp(msg, "Q\n"))
{
closesocket(sock);
exit(0);
}
sprintf(name_msg, "[%s]: %s", name, msg);
send(sock, name_msg, strlen(name_msg), 0);
}
return NULL;
}

DWORD WINAPI recv_msg(LPVOID lpParam)
{
SOCKET sock = *((SOCKET*)lpParam);
char name_msg[NAME_SIZE + BUF_SIZE];
int str_len;

while (1)
{
str_len = recv(sock, name_msg, NAME_SIZE + BUF_SIZE - 1, 0);
if (str_len == -1)
return -1;
name_msg[str_len] = 0;
fputs(name_msg, stdout);
}
return NULL;
}

void error_handling(const char* msg)
{
printf("%s\n", msg);
WSACleanup();
exit(1);
}

3.运行结果

  1. 在Linux中运行LinuxServer.exe

  2. 在Windows中运行WinClient.exe

  3. 在Windows中运行第二个WinClient.exe

  4. 运行结果符合预期