[toc]

用C语言在Windows中使用WebSocks进行实时双向通信

1.C语言在linux中编写支持websocket服务端代码

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

#define PORT 9000

void handle_client(int client_socket) {
char buffer[1024];
int bytes_received = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0';
printf("收到消息: %s\n", buffer);
}
const char *response = "HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n";
send(client_socket, response, strlen(response), 0);
close(client_socket);
}

int main() {
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);

server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("创建套接字失败");
exit(EXIT_FAILURE);
}

server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);

if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("绑定失败");
close(server_socket);
exit(EXIT_FAILURE);
}

if (listen(server_socket, 3) < 0) {
perror("监听失败");
close(server_socket);
exit(EXIT_FAILURE);
}

printf("等待连接...\n");
while ((client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len)) > 0) {
printf("连接到客户端\n");
handle_client(client_socket);
}

if (client_socket < 0) {
perror("接受连接失败");
close(server_socket);
exit(EXIT_FAILURE);
}

close(server_socket);
return 0;
}

2.C语言在windows中编写支持websocket客户端代码

  • 此刻服务端地址默认为10.0.1.20

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

int main() {
WSADATA wsa;
SOCKET s;
struct sockaddr_in server;
char* message, server_reply[2000];
int recv_size;

// 初始化Winsock
printf("\n初始化Winsock...");
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
printf("初始化失败. 错误代码 : %d", WSAGetLastError());
return 1;
}
printf("初始化成功.\n");

// 创建套接字
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
printf("创建套接字失败 : %d", WSAGetLastError());
}
printf("套接字创建成功.\n");

server.sin_family = AF_INET;
server.sin_port = htons(9000);

// 确保IP地址格式正确并且没有多余的空格
const char* ip_address = "10.0.1.20";
printf("正在转换IP地址: %s...\n", ip_address);
int result = InetPtonA(AF_INET, ip_address, &server.sin_addr);
if (result <= 0) {
if (result == 0) {
printf("IP地址格式无效。\n");
}
else {
printf("IP地址转换失败。错误代码: %d\n", WSAGetLastError());
}
return 1;
}
printf("IP地址转换成功。\n");

// 连接远程服务器
if (connect(s, (struct sockaddr*)&server, sizeof(server)) < 0) {
puts("连接失败");
return 1;
}
puts("连接成功");

// 发送握手请求
message = "GET / HTTP/1.1\r\n"
"Host: 10.0.1.20\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
"Sec-WebSocket-Version: 13\r\n\r\n";
if (send(s, message, strlen(message), 0) < 0) {
puts("发送握手请求失败");
return 1;
}
puts("握手请求已发送");

// 接收服务器响应
if ((recv_size = recv(s, server_reply, 2000, 0)) == SOCKET_ERROR) {
puts("接收失败");
}
server_reply[recv_size] = '\0';
puts("服务器响应:");
puts(server_reply);

closesocket(s);
WSACleanup();
return 0;
}

使用libwebsockets库与C语言进行支持websocket协议的程序开发

一、使用C语言在linux中安装libwebscokets库并写一个服务端程序

1.安装libwebscokets库

安装命令

1
sudo apt-get install libwebsockets-dev

2.使用C语言在linux中写一个服务端程序

websockets.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
//websockets.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libwebsockets.h>

static int callback_websocket(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
printf("WebSocket 客户端连接已建立。\n");
break;
case LWS_CALLBACK_RECEIVE:
printf("收到消息: %s\n", (char *)in);
break;
default:
break;
}
return 0;
}

static struct lws_protocols protocols[] = {
{
"ws",
callback_websocket,
0,
4096,
},
{ NULL, NULL, 0, 0 }
};

int main(void) {
struct lws_context_creation_info info;
struct lws_context *context;

memset(&info, 0, sizeof(info));
info.port = 9000;
info.protocols = protocols;

context = lws_create_context(&info);

while (1) {
lws_service(context, 1000);
}

lws_context_destroy(context);
return 0;
}

3.编译命令

1
gcc -o websockets -s websockets.c -lwebsockets

二、使用C语言在windows中安装libwebscokets库并写一个服务端程序

1.安装libwebscokets库

  1. 安装好vcpkg

  2. 在vcpkg目录中安装libwebsockets库

    1
    .\vcpkg install libwebsockets

2.使用C语言在linux中写一个客户端程序

websocketc.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
//websocketc.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <libwebsockets.h>
#pragma comment(lib, "ws2_32.lib")

static int callback_websocket(struct lws* wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len) {
switch (reason) {
case LWS_CALLBACK_CLIENT_ESTABLISHED:
printf("WebSocket 客户端连接已建立。\n");
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
printf("收到消息: %s\n", (char*)in);
break;
default:
break;
}
return 0;
}

static struct lws_protocols protocols[] = {
{
"ws",
callback_websocket,
0,
4096,
},
{ NULL, NULL, 0, 0 }
};

int main(void) {
struct lws_context_creation_info info;
struct lws_client_connect_info ccinfo;
struct lws_context* context;

memset(&info, 0, sizeof(info));
info.port = CONTEXT_PORT_NO_LISTEN;
info.protocols = protocols;
context = lws_create_context(&info);

memset(&ccinfo, 0, sizeof(ccinfo));
ccinfo.context = context;
ccinfo.address = "10.0.1.20";
ccinfo.port = 9000;
ccinfo.path = "/";
ccinfo.host = lws_canonical_hostname(context);
ccinfo.origin = "origin";
ccinfo.protocol = "ws";
ccinfo.ietf_version_or_minus_one = -1;
ccinfo.userdata = NULL;

lws_client_connect_via_info(&ccinfo);

while (1) {
lws_service(context, 1000);
}

lws_context_destroy(context);
return 0;
}

三、给客户端与服务端代码添加实时通信功能

1.Linux服务端程序

websockets.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
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

static int interrupted = 0;

/* 处理 Ctrl+C 退出 */
static void signal_handler(int sig) {
interrupted = 1;
}

/* WebSocket 回调函数 */
static int callback_ws(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len) {
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
printf("Client connected\n");
fflush(stdout);
break;

case LWS_CALLBACK_RECEIVE:
((char *)in)[len] = '\0'; // 确保消息以 NULL 结尾
printf("Received: %s\n", (char *)in); // 打印收到的消息
fflush(stdout);
/* 回显数据 */
lws_write(wsi, in, len, LWS_WRITE_TEXT);
break;

case LWS_CALLBACK_CLOSED:
printf("Client disconnected\n");
fflush(stdout);
break;

default:
break;
}
return 0;
}

/* WebSocket 协议 */
static struct lws_protocols protocols[] = {
{ "ws-protocol", callback_ws, 0, 1024 },
{ NULL, NULL, 0, 0 }
};

/* 主函数 */
int main(void) {
struct lws_context_creation_info info = {0};
struct lws_context *context;

signal(SIGINT, signal_handler);

/* 设置 WebSockets 服务器 */
info.port = 9001;
info.iface = NULL; // 绑定服务器 IP
info.protocols = protocols;
info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;

/* 启动服务器 */
context = lws_create_context(&info);
if (!context) {
fprintf(stderr, "LWS init failed\n");
return -1;
}

printf("WebSocket server running at ws://10.0.1.20:9001\n");

/* 事件循环 */
while (!interrupted) {
lws_service(context, 100);
}

lws_context_destroy(context);
return 0;
}

windows客户端程序

websocketc.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
#include <libwebsockets.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

static int interrupted = 0;
static struct lws* client_wsi = NULL;

/* 处理 Ctrl+C 退出 */
static void signal_handler(int sig) {
interrupted = 1;
}

/* WebSocket 回调函数 */
static int callback_ws(struct lws* wsi, enum lws_callback_reasons reason,
void* user, void* in, size_t len) {
switch (reason) {
case LWS_CALLBACK_CLIENT_ESTABLISHED:
printf("Connected to server\n");
fflush(stdout);
lws_callback_on_writable(wsi); // 请求写入权限
break;

case LWS_CALLBACK_CLIENT_RECEIVE:
printf("Received from server: %s\n", (char*)in);
fflush(stdout);
lws_callback_on_writable(wsi); // 再次请求写入
break;

case LWS_CALLBACK_CLIENT_WRITEABLE: {
char msg[128];
printf("Enter message: ");
fflush(stdout);
fgets(msg, sizeof(msg), stdin);

size_t msg_len = strlen(msg);
if (msg[msg_len - 1] == '\n') {
msg[msg_len - 1] = '\0';
msg_len--;
}

unsigned char buf[LWS_PRE + 128];
memcpy(&buf[LWS_PRE], msg, msg_len);
lws_write(wsi, &buf[LWS_PRE], msg_len, LWS_WRITE_TEXT);

printf("Sent: %s\n", msg);
fflush(stdout);
break;
}

case LWS_CALLBACK_CLOSED:
printf("Connection closed\n");
fflush(stdout);
client_wsi = NULL;
break;

default:
break;
}
return 0;
}

/* WebSocket 协议 */
static struct lws_protocols protocols[] = {
{ "ws-protocol", callback_ws, 0, 1024 },
{ NULL, NULL, 0, 0 }
};

/* 连接 WebSocket 服务器 */
int main() {
struct lws_context_creation_info info = { 0 };
struct lws_client_connect_info ccinfo = { 0 };
struct lws_context* context;

signal(SIGINT, signal_handler);

/* 初始化 libwebsockets */
info.port = CONTEXT_PORT_NO_LISTEN;
info.protocols = protocols;
context = lws_create_context(&info);
if (!context) {
fprintf(stderr, "LWS init failed\n");
return -1;
}

/* 连接 WebSockets 服务器 */
ccinfo.context = context;
ccinfo.address = "10.0.1.20";
ccinfo.port = 9001;
ccinfo.path = "/";
ccinfo.host = ccinfo.address;
ccinfo.origin = ccinfo.address;
ccinfo.protocol = "ws-protocol";
ccinfo.ssl_connection = 0;
ccinfo.pwsi = &client_wsi;

if (!lws_client_connect_via_info(&ccinfo)) {
fprintf(stderr, "Failed to connect\n");
lws_context_destroy(context);
return -1;
}

/* 处理 WebSocket 事件 */
while (!interrupted) {
lws_service(context, 100);
}

lws_context_destroy(context);
return 0;
}