websocket C语言实现
WebSocket是一种基于TCP协议的双向通信协议,可在客户端和服务器之间进行实时通信。下面是使用C语言实现WebSocket的步骤:
-
建立TCP连接:使用socket API函数创建客户端套接字,然后使用connect函数连接到服务器。
-
发送WebSocket握手请求:客户端发送HTTP GET请求,请求升级到WebSocket连接。请求头中包含Sec-WebSocket-Key和Sec-WebSocket-Version字段,分别表示随机生成的字符串和WebSocket版本号。
-
接收WebSocket握手响应:服务器响应HTTP 101 Switching Protocols状态码,表示成功升级到WebSocket连接。响应头中包含Sec-WebSocket-Accept字段,表示服务器同意升级。客户端验证Sec-WebSocket-Accept字段是否正确,以确保连接是安全的。
-
发送和接收数据:客户端和服务器可以通过send和recv函数发送和接收数据,数据格式为WebSocket帧。WebSocket帧分为控制帧和数据帧,控制帧用于控制连接状态,数据帧用于传输数据。
-
断开WebSocket连接:客户端发送Close帧,表示断开连接。服务器发送Close帧作为响应,然后关闭连接。
示例代码:
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define SERVER_PORT 8080
#define SERVER_ADDR "127.0.0.1"
void send_handshake(int sockfd)
{
char buf[1024];
sprintf(buf, "GET / HTTP/1.1\r\n"
"Host: %s:%d\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n", SERVER_ADDR, SERVER_PORT);
send(sockfd, buf, strlen(buf), 0);
}
void recv_handshake(int sockfd)
{
char buf[1024];
recv(sockfd, buf, sizeof(buf), 0);
printf("%s\n", buf);
}
void send_frame(int sockfd, char *data)
{
char buf[1024];
int len = strlen(data);
buf[0] = 0x81; // FIN + TEXT
if (len <= 125) {
buf[1] = len;
memcpy(buf + 2, data, len);
send(sockfd, buf, len + 2, 0);
} else if (len <= 65535) {
buf[1] = 126;
buf[2] = len >> 8;
buf[3] = len & 0xff;
memcpy(buf + 4, data, len);
send(sockfd, buf, len + 4, 0);
} else {
buf[1] = 127;
buf[2] = 0;
buf[3] = 0;
buf[4] = 0;
buf[5] = 0;
buf[6] = len >> 24;
buf[7] = (len >> 16) & 0xff;
buf[8] = (len >> 8) & 0xff;
buf[9] = len & 0xff;
memcpy(buf + 10, data, len);
send(sockfd, buf, len + 10, 0);
}
}
void recv_frame(int sockfd)
{
char buf[1024];
recv(sockfd, buf, sizeof(buf), 0);
int opcode = buf[0] & 0x0f;
int fin = buf[0] & 0x80;
int mask = buf[1] & 0x80;
int len = buf[1] & 0x7f;
if (len == 126) {
len = (buf[2] << 8) | buf[3];
} else if (len == 127) {
len = (buf[6] << 24) | (buf[7] << 16) | (buf[8] << 8) | buf[9];
}
if (mask) {
char *mask_key = buf + 2;
char *data = buf + 6;
for (int i = 0; i < len; i++) {
data[i] ^= mask_key[i % 4];
}
} else {
char *data = buf + 2;
}
printf("%.*s\n", len, data);
}
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERVER_PORT);
servaddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
send_handshake(sockfd);
recv_handshake(sockfd);
send_frame(sockfd, "hello");
recv_frame(sockfd);
close(sockfd);
return 0;
}
服务器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define SERVER_PORT 8080
#define SERVER_ADDR "127.0.0.1"
void send_handshake(int sockfd)
{
char buf[1024];
recv(sockfd, buf, sizeof(buf), 0);
printf("%s\n", buf);
char *key = strstr(buf, "Sec-WebSocket-Key: ");
if (key) {
key += strlen("Sec-WebSocket-Key: ");
char *end = strchr(key, '\r');
if (end) {
*end = '\0';
strcat(key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
unsigned char sha1[20];
SHA1((unsigned char *)key, strlen(key), sha1);
char accept[32];
base64_encode(sha1, 20, accept);
sprintf(buf, "HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n"
"\r\n", accept);
send(sockfd, buf, strlen(buf), 0);
}
}
}
void recv_frame(int sockfd)
{
char buf[1024];
recv(sockfd, buf, sizeof(buf), 0);
int opcode = buf[0] & 0x0f;
int fin = buf[0] & 0x80;
int mask = buf[1] & 0x80;
int len = buf[1] & 0x7f;
if (len == 126) {
len = (buf[2] << 8) | buf[3];
} else if (len == 127) {
len = (buf[6] << 24) | (buf[7] << 16) | (buf[8] << 8) | buf[9];
}
if (mask) {
char *mask_key = buf + 2;
char *data = buf + 6;
for (int i = 0; i < len; i++) {
data[i] ^= mask_key[i % 4];
}
} else {
char *data = buf + 2;
}
printf("%.*s\n", len, data);
send(sockfd, buf, len + 2, 0);
}
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERVER_PORT);
servaddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(sockfd, 5);
int connfd = accept(sockfd, NULL, NULL);
send_handshake(connfd);
while (1) {
recv_frame(connfd);
}
close(sockfd);
return 0;
}
``
原文地址: https://www.cveoy.top/t/topic/hfMB 著作权归作者所有。请勿转载和采集!