所以我暂时在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发送时,我只是没有收到任何内容。
答案 0 :(得分:1)
@Jontahan,
我认为你在这个项目上有一个良好的开端和乐趣 - 但这不是一个小小的努力,你需要学习和修复很多东西。
您的代码中存在一些设计问题:
例如,在阻塞accept
之后(在此期间,您无法读取传入的客户端消息),您继续阻止read
,等待HTTP请求(再次,您的代码正在等待网络事件,而它可能有更好的事情要做。)
此行为阻止您的代码执行任务,因为您的代码正在等待IO(accept
/ read
),而其他事件可能同时发生(即,如果您的websocket消息到达时会发生什么代码在最后10分钟忙于等待accept
?)。
此外,此行为还会引入与慢速客户端(即接收不良或恶意攻击者的客户端)相关的安全漏洞。例如,如果HTTP请求需要一分钟才能到达,该怎么办?如果它到达碎片整理怎么办?如果它一次到达一行怎么办?如果一次收到一封信怎么办?
...
您的代码是测试握手的良好开端,但在将代码用作实际服务器之前,应重新考虑核心设计。
一个解决方案 - 通常用于小型服务器 - 是使用每个连接的线程设计,因此在accept
之后生成一个新线程并负责处理连接(它在{{1上阻塞)不破坏其他线程)。
然而,这种解决方案是次优的并且引入了许多小的安全风险(每种设计都存在安全风险,而是关于选择可能的最小风险)。
另一个解决方案,由一些最好的(read
/ nginx
)实现,是使用单个线程的“事件”设计。这有时被称为“反应堆”设计。
这个设计是迄今为止最好的设计之一,但它确实受到其他问题的困扰,它需要在代码中引起很多关注,因此运行缓慢的任务/功能不会使整个服务器停止运行。
此设计通常使用node.js
,kqueue
或(由于缺乏更好的选择)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