Last active
December 20, 2021 05:06
-
-
Save optman/620518202fcde3e5e5acfaf3f6035df6 to your computer and use it in GitHub Desktop.
libevent websocket client test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <event2/bufferevent.h> | |
#include <event2/buffer.h> | |
#include <event2/event.h> | |
#include <event2/dns.h> | |
#include <arpa/inet.h> | |
#include <iostream> | |
using namespace std; | |
#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) | |
#define ntohll(x) htonll(x) | |
const char* uri = "/echo"; | |
const char* host = "127.0.0.1"; | |
unsigned short port = 1234; | |
const char* fixed_key = "dGhlIHNhbXBsZSBub25jZQ=="; | |
const char* fixed_accept = "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="; | |
bool upgraded = false; | |
static void send(evbuffer* buf, const char* msg){ | |
size_t len = strlen(msg); | |
uint8_t a = 0; | |
a |= 1 << 7; //fin | |
a |= 1; //text frame | |
uint8_t b = 0; | |
b |= 1 << 7; //mask | |
uint16_t c = 0; | |
uint64_t d = 0; | |
//payload len | |
if(len < 126){ | |
b |= len; | |
} | |
else if(len < (1 << 16)){ | |
b |= 126; | |
c = htons(len); | |
}else{ | |
b |= 127; | |
d = htonll(len); | |
} | |
evbuffer_add(buf, &a, 1); | |
evbuffer_add(buf, &b, 1); | |
if(c) evbuffer_add(buf, &c, sizeof(c)); | |
else if(d) evbuffer_add(buf, &d, sizeof(d)); | |
uint8_t mask_key[4] = {1, 2, 3, 4}; //should be random | |
evbuffer_add(buf, &mask_key, 4); | |
uint8_t m; | |
for(int i = 0; i < len; i++){ | |
m = msg[i] ^ mask_key[i%4]; | |
evbuffer_add(buf, &m, 1); | |
} | |
} | |
static void receive(evbuffer* buf){ | |
auto data_len = evbuffer_get_length(buf); | |
if(data_len < 2) | |
return; | |
unsigned char* data = evbuffer_pullup(buf, data_len); | |
int fin = !!(*data & 0x80); | |
int opcode = *data & 0x0F; | |
int mask = !!(*(data+1) & 0x80); | |
uint64_t payload_len = *(data+1) & 0x7F; | |
size_t header_len = 2 + (mask ? 4 : 0); | |
if(payload_len < 126){ | |
if(header_len > data_len) | |
return; | |
}else if(payload_len == 126){ | |
header_len += 2; | |
if(header_len > data_len) | |
return; | |
payload_len = ntohs(*(uint16_t*)(data+2)); | |
}else if(payload_len == 127){ | |
header_len += 8; | |
if(header_len > data_len) | |
return; | |
payload_len = ntohll(*(uint64_t*)(data+2)); | |
} | |
if(header_len + payload_len > data_len) | |
return; | |
const unsigned char* mask_key = data + header_len - 4; | |
cout << "data_len " << data_len << " mask " << mask << " head_len " << header_len << " payload_len " << payload_len << endl; | |
for(int i = 0; mask && i < payload_len; i++) | |
data[header_len + i] ^= mask_key[i%4]; | |
if(opcode == 0x01) | |
cout << ">" <<string((const char*)data + header_len, payload_len) << "<"<< endl; | |
if(!fin){ | |
cout << "fram to be continue..." << endl; | |
} | |
evbuffer_drain(buf, header_len + payload_len); | |
//next frame | |
receive(buf); | |
} | |
static void request(bufferevent* bev){ | |
cout << "request" << endl; | |
auto out = bufferevent_get_output(bev); | |
evbuffer_add_printf(out, "GET %s HTTP/1.1\r\n", uri); | |
evbuffer_add_printf(out, "Host:%s:%d\r\n",host, port); | |
evbuffer_add_printf(out, "Upgrade:websocket\r\n"); | |
evbuffer_add_printf(out, "Connection:upgrade\r\n"); | |
evbuffer_add_printf(out, "Sec-WebSocket-Key:%s\r\n", fixed_key); | |
evbuffer_add_printf(out, "Sec-WebSocket-Version:13\r\n"); | |
evbuffer_add_printf(out, "Origin:http://%s:%d\r\n",host, port); //missing this key will lead to 403 response. | |
evbuffer_add_printf(out, "\r\n"); | |
} | |
static void read_cb(bufferevent* bev, void* ptr){ | |
cout << "read cb" << endl; | |
auto input = bufferevent_get_input(bev); | |
if(!upgraded){ | |
auto data_len = evbuffer_get_length(input); | |
unsigned char* data = evbuffer_pullup(input, data_len); | |
if(!strstr((const char*)data, "\r\n\r\n")) | |
return; | |
if(strncmp((const char*)data, "HTTP/1.1 101", strlen("HTTP/1.1 101")) != 0 | |
|| !strstr((const char*)data, fixed_accept)){ | |
cout << string((const char*)data, data_len) << endl; | |
cout << "websocket upgrade fail!" << endl; | |
}else{ | |
//drain | |
evbuffer_drain(input, data_len); | |
upgraded = true; | |
send(bufferevent_get_output(bev), "hello"); | |
char long_msg[1024]; | |
memset(long_msg, '#', sizeof(long_msg)); | |
long_msg[0]= 'A'; | |
long_msg[sizeof(long_msg)-2]= 'E'; | |
long_msg[sizeof(long_msg)-1]= 0; | |
send(bufferevent_get_output(bev), long_msg); | |
char longlong_msg[(1<< 16) + 10]; | |
memset(longlong_msg, '#', sizeof(longlong_msg)); | |
longlong_msg[0]= 'A'; | |
longlong_msg[sizeof(longlong_msg)-2]= 'X'; | |
longlong_msg[sizeof(longlong_msg)-1]= 0; | |
send(bufferevent_get_output(bev), longlong_msg); | |
} | |
}else{ | |
//handle frame | |
receive(input); | |
} | |
} | |
static void write_cb(bufferevent* bev, void* ptr){ | |
cout << "write cb" << endl; | |
} | |
static void event_cb(bufferevent* bev, short events, void* ptr){ | |
if(events & BEV_EVENT_CONNECTED){ | |
cout << "connected" << endl; | |
request(bev); | |
}else{ | |
cout << "diconnected " << events << endl; | |
} | |
} | |
int main(){ | |
auto base = event_base_new(); | |
auto dns_base = evdns_base_new(base, 1); | |
auto bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); | |
bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL); | |
bufferevent_enable(bev, EV_READ|EV_WRITE); | |
bufferevent_socket_connect_hostname(bev, dns_base, AF_INET, host, port); | |
event_base_dispatch(base); | |
return 0; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"golang.org/x/net/websocket" | |
"log" | |
"net/http" | |
) | |
func EchoServer(ws *websocket.Conn) { | |
for { | |
var msg string | |
if err := websocket.Message.Receive(ws, &msg); err != nil { | |
log.Println("receive msg fail!!!") | |
return | |
} else { | |
log.Println("msg len", len(msg)) | |
log.Println(msg) | |
websocket.Message.Send(ws, msg) | |
} | |
} | |
} | |
func main() { | |
http.Handle("/echo", websocket.Handler(EchoServer)) | |
log.Fatal(http.ListenAndServe(":1234", nil)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Reference: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers