ZBLOG

c 实现websocket 服务端 客户端

WebSocket是一种基于TCP协议的全双工通信协议,它在建立连接之后,客户端和服务端之间可以进行实时的双向通信。下面是C语言实现WebSocket服务端和客户端的示例代码。

WebSocket服务端实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

#define MAX_CLIENTS 10

typedef struct {
    int fd;
    char *data;
    int data_len;
} client_t;

int main(int argc, char *argv[])
{
    int server_fd, new_socket, max_fd, i, j, activity, valread;
    struct sockaddr_in address;
    fd_set readfds;
    client_t clients[MAX_CLIENTS] = {0};
    char buffer[1024] = {0};
    int addrlen = sizeof(address);
 
    // 创建套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    
    // 设置套接字选项
    int opt = 1;
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt failed");
        exit(EXIT_FAILURE);
    }
    
    // 绑定地址和端口号
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    
    // 监听连接请求
    if (listen(server_fd, MAX_CLIENTS) < 0) {
        perror("listen failed");
        exit(EXIT_FAILURE);
    }
    
    printf("WebSocket server started on port %d\n", ntohs(address.sin_port));
 
    while (1) {
        FD_ZERO(&readfds);
        FD_SET(server_fd, &readfds);
        max_fd = server_fd;
        
        // 将所有已连接的客户端加入到文件描述符集合中
        for (i = 0 ; i < MAX_CLIENTS ; i++) {
            if (clients[i].fd > 0) {
                FD_SET(clients[i].fd , &readfds);
                if (clients[i].fd > max_fd) {
                    max_fd = clients[i].fd;
                }
            }
        }
        
        // 等待事件发生
        activity = select(max_fd + 1, &readfds, NULL, NULL, NULL);
        if ((activity < 0) && (errno != EINTR)) {
            perror("select failed");
        }
        
        // 处理新的连接请求
        if (FD_ISSET(server_fd, &readfds)) {
            if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
                perror("accept failed");
                exit(EXIT_FAILURE);
            }
            
            printf("New client connected: socket fd is %d, ip is : %s, port : %d\n", new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
            
            // 将新连接的客户端加入到客户端列表中
            for (i = 0; i < MAX_CLIENTS; i++) {
                if (clients[i].fd == 0) {
                    clients[i].fd = new_socket;
                    break;
                }
            }
        }
        
        // 处理数据
        for (i = 0; i < MAX_CLIENTS; i++) {
            int sock_fd = clients[i].fd;
            if (FD_ISSET(sock_fd, &readfds)) {
                valread = read(sock_fd, buffer, sizeof(buffer));
                if (valread == 0) {
                    printf("Client disconnected: socket fd is %d, ip is : %s, port : %d\n", sock_fd, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
                    close(sock_fd);
                    clients[i].fd = 0;
                } else {
                    if (clients[i].data_len == 0 && strncmp(buffer, "GET /", 5) == 0) {
                        printf("WebSocket handshake with client: socket fd is %d, ip is : %s, port : %d\n", sock_fd, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
                        
                        char *key_start = strstr(buffer, "Sec-WebSocket-Key: ");
                        if (key_start != NULL) {
                            key_start += 19;
                            char *key_end = strchr(key_start, '\r');
                            if (key_end != NULL) {
                                char key[64] = {0};
                                memcpy(key, key_start, key_end - key_start);
                                const char *guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
                                char sha1_input[128] = {0};
                                strcat(sha1_input, key);
                                strcat(sha1_input, guid);
                                unsigned char sha1_output[20] = {0};
                                SHA1((const unsigned char *)sha1_input, strlen(sha1_input), sha1_output);
                                char base64_input[32] = {0};
                                memcpy(base64_input, sha1_output, 20);
                                char base64_output[64] = {0};
                                b64_encode(base64_output, base64_input, 20);
                                char response[1024] = {0};
                                snprintf(response, sizeof(response), "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n\r\n", base64_output);
                                write(sock_fd, response, strlen(response));
                            }
                        }
                    } else {
                        // 解析 WebSocket 帧
                        int opcode = buffer[0] & 0x0F;
                        int mask_flag = (buffer[1] >> 7) & 0x01;
                        int payload_len = buffer[1] & 0x7F;
                        unsigned char *mask_key = (unsigned char *)&buffer[2];
                        unsigned char *payload_data = (unsigned char *)&buffer[6];
                        
                        if (payload_len == 126) {
                            payload_len = buffer[2] << 8 | buffer[3];
                            mask_key += 2;
                            payload_data += 4;
                        } else if (payload_len == 127) {
                            printf("Payload length exceeds 64 bits.\n");
                            continue;
                        }
                        
                        if (mask_flag) {
                            for (j = 0; j < payload_len; j++) {
                                payload_data[j] ^= mask_key[j%4];
                            }
                        }
                        
                        // 处理接收到的数据
                        if (opcode == 8) { // 断开连接
                            printf("Client disconnected: socket fd is %d, ip is : %s, port : %d\n", sock_fd, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
                            close(sock_fd);
                            clients[i].fd = 0;
                        } else if (opcode == 1) { // 文本数据
                            printf("Received message from client: %s\n", payload_data);
                            // 回复文本数据
                            char response[1024] = {0};
                            int response_len = snprintf(response, sizeof(response), "%c%c%s", 0x81, strlen(payload_data), payload_data);
                            write(sock_fd, response, response_len);
                        } else if (opcode == 2) { // 二进制数据
                            printf("Received binary data from client.\n");
                            // 回复二进制数据
                            unsigned char response[1024] = {0};
                            int response_len = 0;
                            response[response_len++] = 0x82; // FIN + opcode=2
                            if (payload_len <= 125) {
                                response[response_len++] = payload_len;
                            } else if (payload_len <= 65535) {
                                response[response_len++] = 126;
                                response[response_len++] = (payload_len >> 8) & 0xFF;
                                response[response_len++] = payload_len & 0xFF;
                            } else {
                                response[response_len++] = 127;
                                for (j = 7; j >= 0; j--) {
                                    response[response_len++] = (payload_len >> (j*8)) & 0xFF;
                                }
                            }
                            memcpy(&response[response_len], payload_data, payload_len);
                            response_len += payload_len;
                            write(sock_fd, response, response_len);
                        } else {
                            printf("Received unsupported WebSocket frame.\n");
                        }
                    }
                }
            }
        }
    }
    
    return 0;
}

WebSocket客户端实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    char buffer[1024] = {0};
    const char *request = "GET / HTTP/1.1\r\nHost: localhost:8080\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n";
    
    // 创建套接字
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }
 
    memset(&serv_addr, '0', sizeof(serv_addr));
    
    // 设置服务器地址和端口号
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(8080);
    
    // 将 IPv4 和 IPv6 地址转换为通用格式
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }
    
    // 连接服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }
    
    // 发送 WebSocket 握手请求
    send(sock, request, strlen(request), 0);
    printf("WebSocket handshake sent.\n");
    
    while (1) {
        memset(buffer, 0, sizeof(buffer));
        valread = read(sock, buffer, sizeof(buffer));
        if (valread == 0) {
            printf("Server disconnected\n");
            break;
        }
        
        // 解析 WebSocket 帧
        int opcode = buffer[0] & 0x0F;
        int mask_flag = (buffer[1] >> 7) & 0x01;
        int payload_len = buffer[1] & 0x7F;
        unsigned char *mask_key = (unsigned char *)&buffer[2];
        unsigned char *payload_data = (unsigned char *)&buffer[6];
        
        if (payload_len == 126) {
            payload_len = buffer[2] << 8 | buffer[3];
            mask_key += 2;
            payload_data += 4;
        } else if (payload_len == 127) {
            printf("Payload length exceeds 64 bits.\n");
            continue;
        }
        
        if (mask_flag) {
            int i;
            for (i = 0; i < payload_len; i++) {
                payload_data[i] ^= mask_key[i%4];
            }
        }
        
        // 处理接收到的数据
        if (opcode == 8) { // 断开连接
            printf("Server disconnected with opcode 8\n");
            break;
        } else if (opcode == 1) { // 文本数据
            printf("Received message from server: %s\n", payload_data);
        } else if (opcode == 2) { // 二进制数据
            printf("Received binary data from server.\n");
        } else {
            printf("Received unsupported WebSocket frame.\n");
        }
    }
    
    close(sock);
    return 0;
}

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://sdn.0voice.com/?id=25

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?