YYDD125 2024-04-13 12:13 采纳率: 20%
浏览 5
已结题

ws服务器和客户端沟通的问题

ws客户端与服务器发送的内容有差异,服务器直接发送的消息正常显示,客户端发送的消息服务器显示收到发送内容+eflate; client_max_window_bits如果再次输入更长的内容可能出现"一些字符e; client_max_window_bits""类似的情况,如果字符串更长会收到字符串+Upgrade Pragma: no-cache Cache-Control: no-cache User-Agent: Mozilla/5.0 (Linux; Android 11; 220233L2C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Mobile Safari/537.36 Upgrade: websocket Origin: null Sec-WebSocket-Version: 13 Accept-Encoding: gzip, deflate Accept-LanguageHTTP/1.1 101 Switching Protocols Upgrade: WebSocket Connection: Upgrade Sec-WebSocket-Accept: DrV91jrOPUr7fYeB+90T/XOxqF0=
服务器代码如下


#include <libwebsockets.h>
#include <string.h>

#define MAX_CLIENTS 10
#define LWS_PROTOCOL_NAME "my-protocol"
#define RX_BUFFER_BYTES 8500
#define TX_BUFFER_BYTES 1024

static struct lws *clients[MAX_CLIENTS];
static int client_count = 0;
char str[RX_BUFFER_BYTES];
static int send_to_all_clients(const char *msg, size_t len);
void c() {                                                                                                                                       if (str != NULL) {
        for (int i = 0;i<RX_BUFFER_BYTES; i++) {
            str[i] = '\0';
        }
    }
}
static int callback_my_protocol(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
    switch (reason) {
        case LWS_CALLBACK_ESTABLISHED:
            if (client_count < MAX_CLIENTS) {
                clients[client_count++] = wsi;
                lwsl_notice("Client connected\n");
                char message[] = "server.8:测试,成功";
                send_to_all_clients(message, strlen(message));

            } else {
                lwsl_notice("Too many clients\n");
                return -1;
            }
            break;

        case LWS_CALLBACK_RECEIVE:
            if (len < RX_BUFFER_BYTES - strlen(str))
            {
                c();
                strcat(str, (char *)in);
                puts(str);
                send_to_all_clients(str, strlen(str));
            } else {
                lwsl_notice("Buffer full\n");
            }
            break;

        case LWS_CALLBACK_CLOSED:

            for (int i = 0; i < client_count; i++) {
                if (clients[i] == wsi) {
                    memmove(clients + i, clients + i + 1, (client_count - i - 1) * sizeof(struct lws *));
                    client_count--;
                    break;
                }
            }
            lwsl_notice("Client disconnected\n");
            break;

        default:
            break;
    }

    return 0;
}

static int send_to_all_clients(const char *msg, size_t len) {
    for (int i = 0; i < client_count; i++) {
        lws_write(clients[i], (unsigned char *)msg, len, LWS_WRITE_TEXT);
    }
    return 0;
}

static struct lws_protocols protocols[] = {
    {
        .name = LWS_PROTOCOL_NAME,
        .callback = callback_my_protocol,
        .per_session_data_size = 0,
        .rx_buffer_size = RX_BUFFER_BYTES
    },
    {NULL, NULL, 0, 0}
};

int main() {
    struct lws_context_creation_info info;
    memset(&info, 0, sizeof(info));

    info.port = 7681;
    info.protocols = protocols;
    info.extensions = NULL;
    info.gid = -1;
    info.uid = -1;

    struct lws_context *context = lws_create_context(&info);
    if (context == NULL) {
        lwsl_err("lws init failed\n");
        return -1;
    }
    while (1) {
        lws_service(context, 50);
    }

    lws_context_destroy(context);
    return 0;
}

客户端代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>WebSocket</title>
</head>
<body>

<div id="messageContainer">
  <p>等待服务器消息...</p>
</div>

<input type="text" id="messageInput" placeholder="输入消息...">
<button id="sendButton">发送</button>

<script>
  
  var ws = new WebSocket("ws://127.0.0.1:7681");

  ws.onopen = function() {
    console.log("连接已打开");
  };

  // 接收到服务器消息
  ws.onmessage = function(event) {
    
    var p = document.createElement("p");
    
    p.textContent = "服务器消息: " + event.data;
    
    document.getElementById("messageContainer").appendChild(p);
  };

  
  ws.onclose = function() {
    console.log("连接已关闭");
  };

  
  ws.onerror = function(error) {
    console.log("连接出错: " + error);
  };

 
  document.getElementById("sendButton").onclick = function() {
   
    var message ="";
    var message = document.getElementById("messageInput").value;
    
     
  
    ws.send(message);
    
    document.getElementById("messageInput").value = "";
  };
</script>
</body>
</html>

  • 写回答

4条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2024-04-13 12:13
    关注

    引自免费微信小程序:皆我百晓生

    在分析了您的代码后,我注意到您可能遇到的问题是由于Websocket协议中的压缩设置导致的。当客户端发送消息时,服务器端接收到的数据包含了额外的信息,这可能是由于客户端使用的压缩方式(例如deflate)导致的。要解决这个问题,您可以尝试禁用压缩功能,或者在服务器端正确处理压缩数据。

    首先,在服务器端,我们需要添加对压缩的支持。在struct lws_protocols结构体中,我们可以设置per_session_data_size来存储一个额外的指针,用于保存解压状态。然后,我们需要实现一个函数来处理压缩消息。以下是修改后的代码:

    #include <zlib.h> // Include zlib library for decompression
    
    // ...
    
    // Add this structure to store decompression state
    typedef struct {
        z_stream stream;
    } decompress_state;
    
    static int decompress_message(z_streamp strm, const unsigned char *in, size_t len, unsigned char **out, size_t *out_len) {
        decompress_state *state = (decompress_state *)strm->state;
    
        if (strm->avail_in == 0) {
            return Z_OK;
        }
    
        if (strm->avail_out == 0) {
            lwsl_err("Out buffer is full during decompression\n");
            return Z_BUFERROR;
        }
    
        int result = inflate(strm, Z_SYNC_FLUSH);
    
        if (result == Z_STREAM_END) {
            // Message fully decompressed
            *out = in;
            *out_len = len;
            return Z_OK;
        } else if (result != Z_OK) {
            lwsl_err("Error during decompression: %d\n", result);
            return Z_DATA_ERROR;
        }
    
        return Z_OK;
    }
    
    // ...
    
    static struct lws_protocols protocols[] = {
        {
            .name = LWS_PROTOCOL_NAME,
            .callback = callback_my_protocol,
            .per_session_data_size = sizeof(decompress_state), // Increase per_session_data_size
            .rx_buffer_size = RX_BUFFER_BYTES
        },
        {NULL, NULL, 0, 0}
    };
    
    // ...
    
    static int callback_my_protocol(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
        decompress_state *decomp_state = (decompress_state *)lws_get_per_session_data(wsi); // Get decompression state
    
        switch (reason) {
            // ...
            
            case LWS_CALLBACK_RECEIVE:
                if (len < RX_BUFFER_BYTES - strlen(str)) {
                    c();
                    // Decompress the received data
                    unsigned char *decompressed_data;
                    size_t decompressed_length;
                    decompress_message(&decomp_state->stream, (const unsigned char *)in, len, &decompressed_data, &decompressed_length);
    
                    // Use decompressed_data and decompressed_length instead of in and len
                    strcat(str, decompressed_data);
                    free(decompressed_data);
                    puts(str);
                    send_to_all_clients(str, strlen(str));
                } else {
                    lwsl_notice("Buffer full\n");
                }
                break;
    
            // ...
        }
    
        return 0;
    }
    
    // ...
    
    int main() {
        // ...
    
        // Initialize decompression state for each new connection
        static void init_decompression_state(struct lws *wsi) {
            decompress_state *decomp_state = (decompress_state *)lws_zalloc(sizeof(decompress_state));
            if (!decomp_state) {
                lwsl_err("Failed to allocate decompression state\n");
                return -1;
            }
    
            memset(&decomp_state->stream, 0, sizeof(z_stream));
            decomp_state->stream.zalloc = Z_NULL;
            decomp_state->stream.zfree = Z_NULL;
            decomp_state->stream.opaque = Z_NULL;
    
            int result = inflateInit2(&decomp_state->stream, 15); // Window bits = 15 for DEFLATE
            if (result != Z_OK) {
                lwsl_err("Failed to initialize decompression: %d\n", result);
                lws_free(decomp_state);
                return -1;
            }
    
            lws_set_per_session_data(wsi, decomp_state);
        }
    
        // ...
    
        while (1) {
            lws_service(context, 50);
    
            // Check for newly established connections and initialize decompression state
            for (int i = 0; i < client_count; i++) {
                if (clients[i]->state == LWS_CONN_STATE_CLIENT_ESTABLISHED && !lws_get_per_session_data(clients[i])) {
                    init_decompression_state(clients[i]);
                }
            }
        }
    
        // ...
    
        return 0;
    }
    

    这个修改后的代码首先包含了<zlib.h>头文件,并定义了一个decompress_state结构体来存储解压状态。我们还实现了decompress_message函数来处理压缩消息。在callback_my_protocol回调中,我们使用decompress_message函数来解压缩从客户端接收到的消息,然后使用解压缩后的数据进行处理。

    另外,我们在main函数中增加了一个init_decompression_state辅助函数,它会在新连接建立时初始化解压状态。这样,每次服务循环都会检查是否有新的连接并为它们初始化解压状态。

    注意:这个示例代码仅适用于支持DEFLATE压缩的客户端。对于其他类型的压缩(如gzip),您需要相应地调整代码。如果客户端不使用任何压缩,这段代码将自动处理未压缩的消息。

    评论 编辑记录

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 4月13日
  • 修改了问题 4月13日
  • 修改了问题 4月13日
  • 创建了问题 4月13日