Skip to content

Instantly share code, notes, and snippets.

@bitnenfer
Created December 29, 2016 05:02
Show Gist options
  • Save bitnenfer/1b88152dd48b0a07dcee1be4c7f350fd to your computer and use it in GitHub Desktop.
Save bitnenfer/1b88152dd48b0a07dcee1be4c7f350fd to your computer and use it in GitHub Desktop.
WebSocket Server using WinSock2 and gaius engine.
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);
}
@ianfun
Copy link

ianfun commented May 30, 2022

snprintf(&send_buffer[2], 510, "%s", out_text); is better than snprintf(&send_buffer[2], 510, out_text);

strlen may incorrect because send_buffer may contain zero byte

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment