让trouple在自制的C / C ++ websocket实现中监听消息

时间:2017-04-30 18:30:12

标签: c++ c sockets websocket posix

所以我暂时在C / C ++中实现一个websocket,经过大量的混乱后我得到了握手工作(这是一个空白错误......)。无论如何,现在我已经迷失了握手建立后如何继续听取消息,我正在学习,所以它有点乱,但我会回答当然,有关代码的任何问题。这就是我现在所拥有的:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
//cpp                                                                                                       
#include <string>
#include <iostream>
#include <openssl/sha.h>
#include "include/base64.h"

int main(int argc, char *argv[])
{
    int listenfd = 0, connfd = 0;
    struct sockaddr_in serv_addr;

    time_t ticks;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&serv_addr, '0', sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(8080);

    bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    listen(listenfd, 8080);
    bool ws = false;
    while(1)
    {
        int res = 0;
        if(ws){
            while(true){
                char buffer_ws[1400];
                res = recv(listenfd, buffer_ws, 1400, 0);
                if(res > 0)
                    std::cout << "Data!" << std::endl;
                sleep(1);
            }
        }
        connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);

        std::cout << "connfd = " << connfd << std::endl;

        char buffer[1400];

        read(connfd, buffer, 1400);

        std::string buf(buffer);
        std::cout << buf << std::endl;

        std::string reply;

        ws = strstr(buffer, "Upgrade: websocket");
        if(ws){ // if websocket handshake. This works                                                       
            std::cout << "<websocket>" << std::endl;
            std::string key = buf.substr(buf.find("Sec-WebSocket-Key") + 19,
                                buf.substr(buf.find("Sec-WebSocket-Key")).find("\n") - 20);

            std::cout << "key = " << key << std::endl;
            key.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
            std::cout << "key = " << key << " length = " << key.length() << std::endl;
            unsigned char const* hash = SHA1(reinterpret_cast<const unsigned char*>(key.c_str()), key.lengt\
h(), nullptr);
            std::string b64 = base64_encode(hash, 20);
            std::cout << "b64 = " << b64 << std::endl;

            reply =
                "HTTP/1.1 101 Switching Protocols\r\n"
                "Upgrade: websocket\r\n"
                "Connection: Upgrade\r\n"
                "Sec-WebSocket-Accept: " + b64 + "\r\n\r\n";
        }else{
            std::cout << "<other>" << std::endl;
            reply =
                "HTTP/1.1 200 OK\n"
                "\n<script>var ws = new WebSocket('ws://192.168.10.117:8080');\n"
                "ws.addEventListener('open',function(event){"
                "\n\tconsole.log('open!!');\n\tws.send('yo bro!');\n});\n"
                "var sendmessage = function(){console.log('click');ws.send('test123');};</script>"
                "<input type=\"text\"><button onclick=\"sendmessage()\">send</button>";
        }
        send(connfd, reply.c_str(), reply.size(), 0);
        close(connfd);
        sleep(1);
     }
}

现在,当我尝试从客户端websocket发送时,我只是没有收到任何内容。

1 个答案:

答案 0 :(得分:1)

@Jontahan,

我认为你在这个项目上有一个良好的开端和乐趣 - 但这不是一个小小的努力,你需要学习和修复很多东西。

您的代码中存在一些设计问题:

  1. 阻止您进入下一阶段(在接受新连接时收听消息);和
  2. 引入可能导致DoS(拒绝服务)的有意义的安全漏洞。
  3. 例如,在阻塞accept之后(在此期间,您无法读取传入的客户端消息),您继续阻止read,等待HTTP请求(再次,您的代码正在等待网络事件,而它可能有更好的事情要做。)

    此行为阻止您的代码执行任务,因为您的代码正在等待IO(accept / read),而其他事件可能同时发生(即,如果您的websocket消息到达时会发生什么代码在最后10分钟忙于等待accept?)。

    此外,此行为还会引入与慢速客户端(即接收不良或恶意攻击者的客户端)相关的安全漏洞。例如,如果HTTP请求需要一分钟才能到达,该怎么办?如果它到达碎片整理怎么办?如果它一次到达一行怎么办?如果一次收到一封信怎么办?

    ...

    您的代码是测试握手的良好开端,但在将代码用作实际服务器之前,应重新考虑核心设计。

    一个解决方案 - 通常用于小型服务器 - 是使用每个连接的线程设计,因此在accept之后生成一个新线程并负责处理连接(它在{{1上阻塞)不破坏其他线程)。

    然而,这种解决方案是次优的并且引入了许多小的安全风险(每种设计都存在安全风险,而是关于选择可能的最小风险)。

    另一个解决方案,由一些最好的(read / nginx)实现,是使用单个线程的“事件”设计。这有时被称为“反应堆”设计。

    这个设计是迄今为止最好的设计之一,但它确实受到其他问题的困扰,它需要在代码中引起很多关注,因此运行缓慢的任务/功能不会使整个服务器停止运行。

    此设计通常使用node.jskqueue或(由于缺乏更好的选择)epoll / poll

    来实施

    在psedo-code中,它可能如下所示:

    select

    这是我写的任务调度系统的一个例子......它没有在生产中测试过,但它可能澄清了我对void defer_task(void (*func((void *)), void * arg) { // place task in queue } void run_tasks() { while (queue->not_empty()) { task = grab_oldest_task(); task.func(task.arg); } } void task_listen(void * data) { // open listening socket } void task_poll(void * _) { // poll existing clients and listening sockets // probably using `kqueue`/`epoll`/`poll`/`select` defer_task(task_ondata, (void*)fd); // finish with rescheduling the poll task defer_task(task_poll, NULL); } void task_ondata(void * data) { int fd = (intptr_t)data; // handle `accept` / `read` making sure all sockets are non-blocking } int main(void) { defer_task(task_listen, NULL); defer_task(task_poll, NULL); run_tasks(); } 的看法:

    defer_task