-
-
Save bplaat/91840b97b740e6b2f92dfb79564799a4 to your computer and use it in GitHub Desktop.
A bare bones websocket client written in C
This file contains hidden or 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 <stdio.h> | |
| #include <stdint.h> | |
| #include <time.h> | |
| #ifdef _WIN32 | |
| #include <winsock2.h> | |
| #else | |
| #include <sys/socket.h> | |
| #include <netinet/in.h> | |
| #include <netdb.h> | |
| #include <unistd.h> | |
| #endif | |
| #define MAX_BUFFER_SIZE 4096 | |
| int create_socket() | |
| { | |
| #ifdef _WIN32 | |
| WSADATA wsaData; | |
| if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) | |
| { | |
| fprintf(stderr, "Failed to initialize winsock\n"); | |
| return -1; | |
| } | |
| #endif | |
| int sockfd = socket(AF_INET, SOCK_STREAM, 0); | |
| if (sockfd < 0) | |
| { | |
| perror("Error creating socket"); | |
| return -1; | |
| } | |
| return sockfd; | |
| } | |
| int connect_to_server(int sockfd, const char *hostname, int port) | |
| { | |
| struct hostent *server = gethostbyname(hostname); | |
| if (server == NULL) | |
| { | |
| fprintf(stderr, "Error, no such host\n"); | |
| return -1; | |
| } | |
| struct sockaddr_in server_addr; | |
| server_addr.sin_family = AF_INET; | |
| server_addr.sin_port = htons(port); | |
| server_addr.sin_addr = *((struct in_addr *)server->h_addr); | |
| if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) | |
| { | |
| perror("Error connecting to server"); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| #define KEY_LENGTH 16 | |
| char *generate_websocket_key() | |
| { | |
| char *key = (char *)malloc(KEY_LENGTH); | |
| srand((uint32_t)time(NULL)); | |
| for (int i = 0; i < KEY_LENGTH; ++i) | |
| { | |
| key[i] = rand() % 256; | |
| } | |
| return key; | |
| } | |
| static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
| char* base64_encode(const char* asciiString, size_t asciiLen) { | |
| size_t base64Len = 4 * ((asciiLen + 2) / 3) + 1; | |
| char* base64String = (char*)malloc(base64Len); | |
| size_t i = 0, j = 0; | |
| while (i < asciiLen) { | |
| unsigned char octet1 = (unsigned char)asciiString[i++]; | |
| unsigned char sextet1 = octet1 >> 2; | |
| base64String[j++] = base64_chars[sextet1]; | |
| if (i < asciiLen) { | |
| unsigned char octet2 = (unsigned char)asciiString[i++]; | |
| unsigned char sextet2 = ((octet1 & 0x03) << 4) | (octet2 >> 4); | |
| base64String[j++] = base64_chars[sextet2]; | |
| if (i < asciiLen) { | |
| unsigned char octet3 = (unsigned char)asciiString[i++]; | |
| unsigned char sextet3 = ((octet2 & 0x0F) << 2) | (octet3 >> 6); | |
| base64String[j++] = base64_chars[sextet3]; | |
| base64String[j++] = base64_chars[octet3 & 0x3F]; | |
| } else { | |
| base64String[j++] = base64_chars[(octet2 & 0x0F) << 2]; | |
| base64String[j++] = '='; | |
| } | |
| } else { | |
| base64String[j++] = base64_chars[(octet1 & 0x03) << 4]; | |
| base64String[j++] = '='; | |
| base64String[j++] = '='; | |
| } | |
| } | |
| base64String[j] = '\0'; | |
| return base64String; | |
| } | |
| void send_http_request(int sockfd) | |
| { | |
| char *websocket_key = generate_websocket_key(); | |
| char *base64_key = base64_encode((const uint8_t *)websocket_key, KEY_LENGTH); | |
| char request[512]; | |
| sprintf(request, "GET / HTTP/1.1\r\n" | |
| "Host: localhost:8080\r\n" | |
| "Connection: Upgrade\r\n" | |
| "Upgrade: websocket\r\n" | |
| "Sec-WebSocket-Version: 13\r\n" | |
| "Sec-WebSocket-Key: %s\r\n\r\n", base64_key); | |
| if (send(sockfd, request, strlen(request), 0) < 0) | |
| { | |
| perror("Error sending request"); | |
| } | |
| } | |
| int main() | |
| { | |
| int sockfd = create_socket(); | |
| if (sockfd < 0) | |
| { | |
| return 1; | |
| } | |
| if (connect_to_server(sockfd, "localhost", 8080) < 0) | |
| { | |
| #ifdef _WIN32 | |
| closesocket(sockfd); | |
| #else | |
| close(sockfd); | |
| #endif | |
| #ifdef _WIN32 | |
| WSACleanup(); | |
| #endif | |
| return 1; | |
| } | |
| send_http_request(sockfd); | |
| char buffer[MAX_BUFFER_SIZE]; | |
| recv(sockfd, buffer, sizeof(buffer), 0); | |
| for (;;) | |
| { | |
| recv(sockfd, buffer, sizeof(buffer), 0); | |
| uint8_t frame_type = buffer[0] & 15; | |
| // When binary frame | |
| if (frame_type == 2) | |
| { | |
| uint8_t *message; | |
| if ((buffer[1] & 127) == 126) { | |
| message = &buffer[2 + 2]; | |
| } else if ((buffer[1] & 127) == 127) { | |
| message = &buffer[2 + 4]; | |
| } else { | |
| message = &buffer[2]; | |
| } | |
| printf("type = 0x%02x\n", message[0]); | |
| } | |
| } | |
| #ifdef _WIN32 | |
| closesocket(sockfd); | |
| WSACleanup(); | |
| #else | |
| close(sockfd); | |
| #endif | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment