Created
January 31, 2023 09:56
-
-
Save Crafting1i/fedbb3555c7d7a980a2c04ca435b815b to your computer and use it in GitHub Desktop.
My tries to make websocket with libcurl
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
#pragma comment(lib, "Ws2_32.Lib") | |
#pragma comment(lib, "Wldap32.Lib") | |
#pragma comment(lib, "Crypt32.Lib") | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <iostream> | |
#define CURL_STATICLIB | |
#include <curl/curl.h> | |
#ifdef WIN32 | |
#include <windows.h> | |
#include <winsock2.h> | |
#include <ws2tcpip.h> | |
#define close closesocket | |
#else | |
#include <sys/types.h> /* socket types */ | |
#include <sys/socket.h> /* socket definitions */ | |
#include <netinet/in.h> | |
#include <arpa/inet.h> /* inet (3) functions */ | |
#include <unistd.h> /* misc. Unix functions */ | |
#endif | |
#include <errno.h> | |
/* The IP address and port number to connect to */ | |
#define TWITCH_WS "eventsub-beta.wss.twitch.tv/ws" | |
#define TWITCH_DOMAIN "eventsub-beta.wss.twitch.tv" | |
#define PORTNUM 443 | |
#ifndef INADDR_NONE | |
#define INADDR_NONE 0xffffffff | |
#endif | |
void* get_sin_addr(addrinfo* addr) | |
{ | |
switch (addr->ai_family) | |
{ | |
case AF_INET: | |
return &(reinterpret_cast<sockaddr_in*>(addr->ai_addr)->sin_addr); | |
case AF_INET6: | |
return &(reinterpret_cast<sockaddr_in6*>(addr->ai_addr)->sin6_addr); | |
} | |
return NULL; | |
} | |
char* get_ip() { | |
addrinfo hints = {}; | |
hints.ai_flags = AI_CANONNAME; | |
hints.ai_family = AF_UNSPEC; | |
hints.ai_socktype = SOCK_STREAM; | |
hints.ai_protocol = IPPROTO_TCP; | |
addrinfo* res; | |
char ip[INET6_ADDRSTRLEN]; | |
int ret = getaddrinfo(TWITCH_DOMAIN, NULL, &hints, &res); | |
if (ret != 0) { | |
std::cerr << "getaddrinfo() failed: " << gai_strerror(ret) << " " << ret << "\n"; | |
system("pause"); | |
exit(1); | |
} | |
else { | |
std::cout << res->ai_canonname << "\n"; | |
for (addrinfo* addr = res; addr; addr = addr->ai_next) { | |
inet_ntop(addr->ai_family, get_sin_addr(addr), ip, sizeof(ip)); | |
} | |
freeaddrinfo(res); | |
} | |
return ip; | |
} | |
static size_t write_data(void* ptr, size_t size, size_t nmemb, void* stream) | |
{ | |
size_t written = fwrite(ptr, size, nmemb, (FILE*)stream); | |
std::cout << written << std::endl; | |
std::string val = *(std::string*)ptr; | |
std::cout << val << " writedata" << std::endl; | |
return written; | |
} | |
static int closecb(void* clientp, curl_socket_t item) | |
{ | |
(void)clientp; | |
printf("libcurl wants to close %d now\n", (int)item); | |
return 0; | |
} | |
static curl_socket_t opensocket(void* clientp, | |
curlsocktype purpose, | |
struct curl_sockaddr* address) | |
{ | |
curl_socket_t sockfd; | |
(void)purpose; | |
(void)address; | |
sockfd = *(curl_socket_t*)clientp; | |
//std::string val = *(std::string*)clientp; | |
std::cout << clientp << " opensocket" << std::endl; | |
/* the actual externally set socket is passed in via the OPENSOCKETDATA | |
option */ | |
return sockfd; | |
} | |
static int sockopt_callback(void* clientp, curl_socket_t curlfd, | |
curlsocktype purpose) | |
{ | |
(void)clientp; | |
(void)curlfd; | |
(void)purpose; | |
std::cout << clientp << " sockopt" << std::endl; | |
/* This return code was added in libcurl 7.21.5 */ | |
return CURL_SOCKOPT_ALREADY_CONNECTED; | |
} | |
int main(void) | |
{ | |
CURL* curl; | |
CURLcode res; | |
struct sockaddr_in servaddr; /* socket address structure */ | |
curl_socket_t sockfd; | |
#ifdef WIN32 | |
WSADATA wsaData; | |
int initwsa = WSAStartup(MAKEWORD(2, 2), &wsaData); | |
if (initwsa) { | |
printf("WSAStartup failed: %d\n", initwsa); | |
return 1; | |
} | |
#endif | |
curl = curl_easy_init(); | |
if (curl) { | |
struct curl_slist* chunk = NULL; | |
/* Remove a header curl would otherwise add by itself */ | |
chunk = curl_slist_append(chunk, "Connection: Upgrade"); | |
chunk = curl_slist_append(chunk, "Upgrade: websocket"); | |
chunk = curl_slist_append(chunk, "Host: eventsub-beta.wss.twitch.tv"); | |
chunk = curl_slist_append(chunk, "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ=="); | |
chunk = curl_slist_append(chunk, "Sec-WebSocket-Version: 13"); | |
/* set our custom set of headers */ | |
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); | |
/* | |
* Note that libcurl will internally think that you connect to the host | |
* and port that you specify in the URL option. | |
*/ | |
curl_easy_setopt(curl, CURLOPT_URL, TWITCH_WS); | |
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); | |
/* Create the socket "manually" */ | |
sockfd = socket(AF_INET, SOCK_STREAM, 0); | |
if (sockfd == CURL_SOCKET_BAD) { | |
printf("Error creating listening socket.\n"); | |
return 3; | |
} | |
memset(&servaddr, 0, sizeof(servaddr)); | |
servaddr.sin_family = AF_INET; | |
servaddr.sin_port = htons(PORTNUM); | |
char* char_ip = get_ip(); | |
//char* char_ip = "108.138.246.28"; | |
unsigned long int_ip; | |
std::cout << char_ip << std::endl; | |
std::cout << inet_pton(servaddr.sin_family, char_ip, &int_ip) << std::endl; | |
servaddr.sin_addr.s_addr = int_ip; | |
if (INADDR_NONE == servaddr.sin_addr.s_addr) { | |
std::cout << "Can't connect by this ip" << std::endl; | |
close(sockfd); | |
return 2; | |
} | |
int code = connect( | |
sockfd, (struct sockaddr*)&servaddr, | |
sizeof(servaddr) | |
); | |
int err_code = WSAGetLastError(); | |
if (err_code == WSAETIMEDOUT) printf("Timeout\n"); | |
if (code == SOCKET_ERROR) { | |
close(sockfd); | |
printf("client error: connect: %s\n", strerror(errno)); | |
system("pause"); | |
return 1; | |
} | |
/* no progress meter please */ | |
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); | |
/* send all data to this function */ | |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); | |
/* call this function to get a socket */ | |
curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket); | |
curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &sockfd); | |
/* call this function to close sockets */ | |
curl_easy_setopt(curl, CURLOPT_CLOSESOCKETFUNCTION, closecb); | |
curl_easy_setopt(curl, CURLOPT_CLOSESOCKETDATA, &sockfd); | |
/* call this function to set options for the socket */ | |
curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback); | |
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); | |
res = curl_easy_perform(curl); | |
const int max_packet_size = 65'536; // 64 KB | |
char* recv_buff = new char[max_packet_size]; | |
int actual_len = 0; | |
int i_res = 1; | |
do { | |
i_res = recv(sockfd, recv_buff, max_packet_size, 0); | |
if (i_res > 0) { | |
printf("Bytes received: %d\n", i_res); | |
std::cout << "Buffer: " << recv_buff << std::endl << "Size: " << i_res << std::endl; | |
break; | |
} | |
else if (i_res == 0) { | |
printf("Connection closed\n"); | |
break; | |
} | |
else { | |
printf("recv failed: %d\n", WSAGetLastError()); | |
} | |
} while (true); | |
curl_easy_cleanup(curl); | |
close(sockfd); | |
if (res) { | |
printf("libcurl error: %d\n", res); | |
return 4; | |
} | |
} | |
#ifdef WIN32 | |
WSACleanup(); | |
#endif | |
system("pause"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm sorry, if that's bad code :)