Created
December 29, 2016 05:02
-
-
Save bitnenfer/1b88152dd48b0a07dcee1be4c7f350fd to your computer and use it in GitHub Desktop.
WebSocket Server using WinSock2 and gaius engine.
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
void socket_run(void*) | |
{ | |
//addrinfo hints; | |
//addrinfo* result = NULL; | |
//addrinfo* pointer = NULL; | |
uint32 success = 0; | |
SOCKET listen_socket = INVALID_SOCKET; | |
SOCKET client_socket = INVALID_SOCKET; | |
WSADATA wsa_data; | |
success = WSAStartup(MAKEWORD(2, 2), &wsa_data); | |
if (success != 0) | |
{ | |
GS_ASSERT(0, "WSAStartup failed %d", success); | |
} | |
//ZeroMemory(&hints, sizeof(addrinfo)); | |
//hints.ai_family = AF_INET; | |
//hints.ai_socktype = SOCK_STREAM; | |
//hints.ai_protocol = IPPROTO_TCP; | |
//hints.ai_flags = AI_PASSIVE; | |
/*success = getaddrinfo("127.0.0.1", "8888", &hints, &result); | |
if (success != 0) | |
{ | |
WSACleanup(); | |
GS_ASSERT(0, "Failed getaddrinfo %s", gai_strerror(success)); | |
}*/ | |
sockaddr_in server; | |
ZeroMemory(&server, sizeof(server)); | |
listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//socket(result->ai_family, result->ai_socktype, result->ai_protocol); | |
if (listen_socket == INVALID_SOCKET) | |
{ | |
//freeaddrinfo(result); | |
WSACleanup(); | |
GS_ASSERT(0, "failed socket %d", WSAGetLastError()); | |
} | |
server.sin_family = AF_INET; | |
server.sin_port = htons(8888); | |
server.sin_addr.s_addr = inet_addr("localhost"); | |
success = bind(listen_socket, (sockaddr*)&server, sizeof(server));//bind(listen_socket, result->ai_addr, (int)result->ai_addrlen); | |
if (success == SOCKET_ERROR) | |
{ | |
//freeaddrinfo(result); | |
closesocket(listen_socket); | |
WSACleanup(); | |
GS_ASSERT(0, "failed bind %d", WSAGetLastError()) | |
} | |
//freeaddrinfo(result); | |
success = listen(listen_socket, SOMAXCONN); | |
if (success == SOCKET_ERROR) | |
{ | |
closesocket(listen_socket); | |
WSACleanup(); | |
GS_ASSERT(0, "failed listen %d", WSAGetLastError()); | |
} | |
client_socket = accept(listen_socket, NULL, NULL); | |
if (client_socket == INVALID_SOCKET) | |
{ | |
closesocket(listen_socket); | |
WSACleanup(); | |
GS_ASSERT(0, "failed accept %d", WSAGetLastError()); | |
} | |
char receive_buffer[4096]; | |
int32 receive_buffer_length = 4096; | |
int32 send_success = 0; | |
success = 0; | |
bool send_handshake = true; | |
do | |
{ | |
ZeroMemory(receive_buffer, receive_buffer_length); | |
success = recv(client_socket, receive_buffer, receive_buffer_length, 0); | |
if (success > 0) | |
{ | |
GS_LOG("Bytes received: %d\n", success); | |
if (!send_handshake) | |
{ | |
char* payload; | |
char mask[4]; | |
uint16 bits16 = *((uint16*)&receive_buffer[0]); | |
uint8 fin = (!!(bits16 & 0b0000000010000000)); | |
uint8 rv1 = (!!(bits16 & 0b0000000001000000)); | |
uint8 rv2 = (!!(bits16 & 0b0000000000100000)); | |
uint8 rv3 = (!!(bits16 & 0b0000000000010000)); | |
uint8 opc = ((bits16 & 0b0000000000001111)); | |
uint8 msk = (!!(bits16 & 0b1000000000000000)); | |
uint64 len = ((bits16 >> 8) & 0b0000000001111111); | |
if (opc == 8) | |
{ | |
closesocket(client_socket); | |
break; | |
} | |
if (len <= 125) | |
{ | |
GS_LOG("small data\n"); | |
mask[0] = receive_buffer[2]; | |
mask[1] = receive_buffer[3]; | |
mask[2] = receive_buffer[4]; | |
mask[3] = receive_buffer[5]; | |
payload = &receive_buffer[6]; | |
} | |
else if (len == 126) | |
{ | |
GS_LOG("medium data\n"); | |
mask[0] = receive_buffer[4]; | |
mask[1] = receive_buffer[5]; | |
mask[2] = receive_buffer[6]; | |
mask[3] = receive_buffer[7]; | |
payload = &receive_buffer[8]; | |
uint32 bits32 = *((uint32*)&receive_buffer[0]); | |
len = ((bits32 & 0b00000000111111110000000000000000) >> 8) | ((bits32 & 0b11111111000000000000000000000000) >> 24); | |
} | |
else if (len == 127) | |
{ | |
GS_LOG("large data\n"); | |
mask[0] = receive_buffer[10]; | |
mask[1] = receive_buffer[11]; | |
mask[2] = receive_buffer[12]; | |
mask[3] = receive_buffer[13]; | |
payload = &receive_buffer[14]; | |
uint32 bits64 = *((uint32*)&receive_buffer[4]); | |
len = bits64 >> 5; | |
len = len; | |
} | |
if (len < receive_buffer_length) | |
{ | |
GS_LOG("Payload byte length %" GS_PRINTF_USIZE "\n", len); | |
for (usize i = 0; i < len; ++i) | |
{ | |
payload[i] = payload[i] ^ mask[i % 4]; | |
} | |
GS_LOG("Message: %s\n", payload); | |
printf("client> %s\n", payload); | |
} | |
else | |
{ | |
GS_LOG("Data to big = %lu\n", len); | |
} | |
{ | |
char out_text[512]; | |
char send_buffer[512]; | |
ZeroMemory(send_buffer, sizeof(send_buffer)); | |
ZeroMemory(out_text, sizeof(out_text)); | |
uint8 data_size = 124; | |
send_buffer[0] = 0b10000001; | |
char get = 0; | |
uint32 inc = 0; | |
printf("server> "); | |
while (1) | |
{ | |
get = getchar(); | |
if (!(get == '\n' || get == '\r')) | |
{ | |
out_text[inc++] = get; | |
} | |
else | |
{ | |
break; | |
} | |
} | |
usize txt_len = strlen(out_text); | |
if (txt_len <= 125) | |
{ | |
send_buffer[1] = txt_len; | |
snprintf(&send_buffer[2], 510, out_text); | |
} | |
else | |
{ | |
send_buffer[1] = 126; | |
send_buffer[2] = ((txt_len >> 8) & 0xFF); | |
send_buffer[3] = ((txt_len >> 0) & 0xFF); | |
//*((uint32*)&send_buffer[2]) = txt_len; | |
snprintf(&send_buffer[4], 508, out_text); | |
} | |
send_success = send(client_socket, send_buffer, (int32)strlen(send_buffer), 0); | |
if (send_success == SOCKET_ERROR) | |
{ | |
closesocket(client_socket); | |
WSACleanup(); | |
GS_ASSERT(0, "Failed send %d", WSAGetLastError()); | |
} | |
} | |
} | |
else | |
{ | |
send_handshake = false; | |
uint32 index = 0; | |
char handshake_test[] = "Sec-WebSocket-Key: "; | |
gs::FixedString<120> handshake_hash; | |
char base64_hash[120]; | |
ZeroMemory(base64_hash, sizeof(base64_hash)); | |
while (index < success) | |
{ | |
if (index + sizeof(handshake_test) < receive_buffer_length && | |
gs::StringCompare(&receive_buffer[index], handshake_test, sizeof(handshake_test) - 1)) | |
{ | |
index += sizeof(handshake_test) - 1; | |
while (index < success && (receive_buffer[index] != '\n' && receive_buffer[index] != '\r')) | |
{ | |
handshake_hash += receive_buffer[index]; | |
++index; | |
} | |
handshake_hash += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | |
GS_LOG("Key: %s\n", handshake_hash.get_cstr()); | |
uint32 digest[5]; | |
gs::HashSHA1((unsigned char*)digest, handshake_hash.get_cstr(), (uint32)strlen(handshake_hash.get_cstr())); | |
gs::EncodeBase64(base64_hash, sizeof(base64_hash), (const unsigned char*)digest, 20); | |
char handshake_buffer[256]; | |
ZeroMemory(handshake_buffer, sizeof(handshake_buffer)); | |
snprintf(handshake_buffer, sizeof(handshake_buffer), | |
"HTTP/1.1 101 Switching Protocols\r\n" | |
"Upgrade: websocket\r\n" | |
"Connection: Upgrade\r\n" | |
"Sec-WebSocket-Accept: %s\r\n" | |
"\r\n", | |
base64_hash | |
); | |
GS_LOG("%s\n", handshake_buffer); | |
send_success = send(client_socket, handshake_buffer, (int32)strlen(handshake_buffer), 0); | |
if (send_success == SOCKET_ERROR) | |
{ | |
closesocket(client_socket); | |
WSACleanup(); | |
GS_ASSERT(0, "Failed send %d", WSAGetLastError()); | |
} | |
GS_LOG("Handshake sent. bytes: %d\n", send_success); | |
break; | |
} | |
++index; | |
} | |
} | |
} | |
else if (success == 0) | |
{ | |
GS_LOG("Connection closed\n"); | |
} | |
else | |
{ | |
closesocket(client_socket); | |
WSACleanup(); | |
GS_ASSERT(0, "Failed to receive %d", WSAGetLastError()); | |
} | |
} | |
while (success > 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
snprintf(&send_buffer[2], 510, "%s", out_text);
is better thansnprintf(&send_buffer[2], 510, out_text);
strlen
may incorrect becausesend_buffer
may contain zero byte