Skip to content

Instantly share code, notes, and snippets.

@Crafting1i
Created January 31, 2023 09:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Crafting1i/fedbb3555c7d7a980a2c04ca435b815b to your computer and use it in GitHub Desktop.
Save Crafting1i/fedbb3555c7d7a980a2c04ca435b815b to your computer and use it in GitHub Desktop.
My tries to make websocket with libcurl
#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;
}
@Crafting1i
Copy link
Author

I'm sorry, if that's bad code :)

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