I code a small client for IPv4 / IPv6 with a hostname resolver.
For IPv4 and resolver it's fine but not with IPv6 when connect() I have a problem WSAGetLastError() say WSAEAFNOSUPPORT.
I have a switch all structures (AF_INET -> AF_INET6, SOCKADDR_IN -> SOCKADDR_IN6) to IPv6 versions.
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
printf("Simple_Client IPv4 & IPv6\n\n");
// Initiates Winsock
WSADATA WSAData;
WSAStartup(MAKEWORD(2, 0), &WSAData);
// Get Parameters IP/PORT and request
std::string str_HOSTNAME = "mirror.neostrada.nl";
int PORT = 21;
// RESOLVE IP
BOOL is_IPv6 = FALSE;
std::string str_dest_ip = "";
addrinfo hints = { 0 };
hints.ai_flags = AI_ALL;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo * pResult;
getaddrinfo(str_HOSTNAME.c_str(), NULL, &hints, &pResult);
if (pResult == NULL)
{
printf("pResult error\n");
return -1;
}
if (pResult->ai_family == AF_INET)
{
printf("getaddrinfo = AF_INET (IPv4)\n");
is_IPv6 = FALSE;
}
if (pResult->ai_family == AF_INET6)
{
printf("getaddrinfo = AF_INET6 (IPv6)\n");
is_IPv6 = TRUE;
}
char str[128];
memset(str, 0, sizeof(str));
if (is_IPv6 == FALSE) // IPv4
{
if (inet_ntop(AF_INET, &(*((ULONG*)&(((sockaddr_in*)pResult->ai_addr)->sin_addr))), str, INET_ADDRSTRLEN))
str_dest_ip = char_to_string(str, strlen(str)); // Copy char in std::string
else
printf("inet_ntop error\n");
}
if (is_IPv6 == TRUE) // IPv6
{
if (inet_ntop(AF_INET6, &(*((ULONG*)&(((sockaddr_in6 *)pResult->ai_addr)->sin6_addr))), str, INET6_ADDRSTRLEN))
str_dest_ip = char_to_string(str, strlen(str)); // Copy char in std::string
}
printf("%s : %s | Port : %i\n", is_IPv6 ? "IPv6" : "IPv4", str_dest_ip.c_str(), PORT);
// Connect to the HOSTNAME
SOCKET sock;
if (is_IPv6 == TRUE)
{
SOCKADDR_IN6 sin;
sin.sin6_family = AF_INET6;
if(inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin) != 1)
printf("ERROR inet_pton %i\n", WSAGetLastError());
sin.sin6_port = htons(PORT);
sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
return -2;
if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) != SOCKET_ERROR)
{
printf("Connect Success to %s | PORT : %i\n", str_dest_ip.c_str(), PORT);
}
else
{
printf("ERROR connect to %s | PORT : %i : %i\n", str_dest_ip.c_str(), PORT, WSAGetLastError());
Sleep(10000);
return -2;
}
}
char buf[1024] = { 0 };
int size_recv = recv(sock, buf, sizeof(buf), 0);
printf("SIZE RECV = %i | DATA RECV = %s\n", size_recv, char_to_string(buf, size_recv).c_str());
WSACleanup();
getchar();
return 0;
}
If somebody have a idea, thanks for reading.
The problem is here:
inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin)
This writes the IPv6 address on top of the sin6_family field, damaging the whole structure.
It should be:
inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin.sin6_addr)
It's also a good idea to zero-initialize the whole sin structure in the beginning because it has more fields than you're filling in.
You are not using getaddrinfo() correctly.
For one thing, getaddrinfo() returns an error code that you are ignoring.
For another thing, getaddrinfo() returns a linked list that potentially contains multiple addresses in a mix of IPv4 and/or IPv6, due to your use of AF_UNSPEC. If you are only interested in IPv6, set hints.ai_family to AF_INET6 rather than AF_UNSPEC.
But either way, a given hostname may have multiple IPs associated with it, and they might not all be reachable from your location, so you should be connect()'ing to each address in the list, either one at a time or in parallel, until one of them succeeds.
Also, there is no need to use inet_pton() in this situation at all (which you are not using correctly, as explained by #rustyx's answer). getaddrinfo() returns fully populated sockaddr_in(6) structs that you can pass as-is to connect().
Try something more like this instead:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
#pragma comment(lib, "ws2_32.lib")
std::string addr_to_str(addrinfo *addr)
{
char str[128];
switch (addr->ai_family)
{
case AF_INET: // IPv4
{
if (inet_ntop(AF_INET, &(((sockaddr_in*)(addr->ai_addr))->sin_addr), str, INET_ADDRSTRLEN))
return str;
ret = WSAGetLastError();
break;
}
case AF_INET6: // IPv6
{
if (inet_ntop(AF_INET6, &(((sockaddr_in6*)(addr->ai_addr))->sin6_addr), str, INET6_ADDRSTRLEN))
return str;
ret = WSAGetLastError();
break;
}
default:
ret = WSAEAFNOSUPPORT;
break;
}
std::cerr << "inet_ntop error: " << ret << std::endl;
return "";
}
int main()
{
std::cout << "Simple_Client IPv4 & IPv6" << std::endl << std::endl;
// Initiates Winsock
WSADATA WSAData;
int ret = WSAStartup(MAKEWORD(2, 0), &WSAData);
if (ret != 0)
{
std::cerr << "WSAStartup error: " << ret << std::endl;
return -1;
}
// Get Parameters IP/PORT and request
std::string str_HOSTNAME = "mirror.neostrada.nl";
int PORT = 21;
// RESOLVE IP
addrinfo hints = { 0 };
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo *pResult = NULL;
ret = getaddrinfo(str_HOSTNAME.c_str(), std::to_string(PORT).c_str(), &hints, &pResult);
if (ret != 0)
{
std::cerr << "getaddrinfo error: " << ret << std::endl;
WSACleanup();
return -1;
}
// Log the IPs
bool has_IPv4 = false;
bool has_IPv6 = false;
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
switch (addr->ai_family)
{
case AF_INET: // IPv4
{
has_IPv4 = true;
std::cout << "IPv4 : " << addr_to_str(addr);
break;
}
case AF_INET6: // IPv6
{
has_IPv6 = true;
std::cout << "IPv6 : " << addr_to_str(addr);
break;
}
}
}
// Connect to the HOSTNAME
SOCKET sock = INVALID_SOCKET;
if (has_IPv6)
{
// try IPv6 first...
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
if (addr->ai_family != AF_INET6)
continue;
std::cout << "Connecting to IPv6 : " << addr_to_str(addr) << " | Port : " << PORT << std::endl;
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock == INVALID_SOCKET)
{
ret = WSAGetLastError();
std::cerr << "socket error: " << ret << std::endl;
continue;
}
if (connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "connect error: " << ret << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
break;
}
}
if ((sock == INVALID_SOCKET) && (has_IPv4))
{
// try IPv4 next...
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
if (addr->ai_family != AF_INET)
continue;
std::cout << "Connecting to IPv4 : " << addr_to_str(addr) << " | Port : " << PORT << std::endl;
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock == INVALID_SOCKET)
{
ret = WSAGetLastError();
std::cerr << "socket error: " << ret << std::endl;
continue;
}
if (connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "connect error: " << ret << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
break;
}
}
freeaddrinfo(pResult);
if (sock == INVALID_SOCKET)
{
WSACleanup();
return -2;
}
std::cout << "Connect Successful" << std::endl;
char buf[1024];
int size_recv = recv(sock, buf, sizeof(buf), 0);
if (size_recv == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "recv error: " << ret << std::endl;
}
else
{
std::cout << "SIZE RECV = " << size_recv;
if (size_recv > 0)
{
std::cout << " | DATA RECV = ";
std::cout.write(buf, size_recv);
}
std::cout << std::endl;
}
closesocket(sock);
WSACleanup();
std::cin.get();
return 0;
}
Related
So I've been creating a winsock server/client in UE4. I can get the client to connect to the server however once the client sends the first message it seems to close the socket, preventing any further messages to be sent to the server. It also seems like the server is doing the same thing when sending data.
client
// Convert IP & port to standard lib
const std::string IP = std::string(TCHAR_TO_UTF8((*GameInstance->GetIPAddress())));
const std::string PORT = std::string(TCHAR_TO_UTF8(*GameInstance->GetPort()));
// Set the version of WSA we are using
auto Version = MAKEWORD(2, 2);
WSAData WSData;
struct addrinfo* Result = nullptr, * ptr = nullptr, hints;
int iResult; // Store Initializing results
std::string message; // Define a message to send to the server
UE_LOG(LogTemp, Log, TEXT("Starting Client"));
// Initialize WinSock
iResult = WSAStartup(Version, &WSData); // Start winsock
if(iResult != 0)
{
UE_LOG(LogTemp, Error, TEXT("Failed to initialize winsock"));
return ECreateConnectionFlag::WINSOCK_FAILED;
}
UE_LOG(LogTemp, Log, TEXT("Initialized WinSock"));
// Setup hints
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
UE_LOG(LogTemp, Log, TEXT("Connecting"));
// Get the address details
iResult = getaddrinfo(IP.c_str(), PORT.c_str(), &hints, &Result);
if(iResult != 0)
{
UE_LOG(LogTemp, Error, TEXT("Error getting address info from the server"));
WSACleanup();
return 0;
}
// Connect the player
for(ptr = Result; ptr != nullptr; ptr->ai_next)
{
GameInstance->SetPlayerSocket(socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol));
if(GameInstance->GetLoggedInPlayer().PlayerSocket == INVALID_SOCKET)
{
UE_LOG(LogTemp, Error, TEXT("Failed to create socket"));
WSACleanup();
return 0;
}
iResult = connect(GameInstance->GetLoggedInPlayer().PlayerSocket, ptr->ai_addr, ptr->ai_addrlen);
if(iResult == SOCKET_ERROR)
{
closesocket(GameInstance->GetLoggedInPlayer().PlayerSocket);
GameInstance->SetPlayerSocket(INVALID_SOCKET);
continue;
}
break;
}
freeaddrinfo(Result); // Release Address information as it's no longer required
// Ensure the socket is valid
if(GameInstance->GetLoggedInPlayer().PlayerSocket == INVALID_SOCKET)
{
UE_LOG(LogTemp, Error, TEXT("Unable to connect to server..."));
WSACleanup();
return 0;
}
// WE ARE CONNECTED
/* CONNECT AND SEND USERNAME */
FString SignInMessage = FString("Username-" + GameInstance->GetLoggedInPlayer().Username);
std::string ConnectionMessage = std::string(TCHAR_TO_UTF8(*SignInMessage));
iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, ConnectionMessage.c_str(), (int)strlen(ConnectionMessage.c_str()), 0);
if(iResult <= 0)
{
int error = WSAGetLastError();
UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
return 0;
}
while(bRunThread)
{
UE_LOG(LogTemp, Log, TEXT("Receiving Data"));
/* DISCONNECT FROM SERVER */
const std::string msg = "Hello World";
iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, msg.c_str(), (int)strlen(msg.c_str()), 0);
if(iResult <= 0)
{
int error = WSAGetLastError();
UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
return 0;
}
FPlatformProcess::Sleep(1.0f);
}
/* DISCONNECT FROM SERVER */
const std::string DisconnectMsg = "Disconnect";
iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, DisconnectMsg.c_str(), (int)strlen(DisconnectMsg.c_str()), 0);
if(iResult <= 0)
{
int error = WSAGetLastError();
UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
return 0;
}
UE_LOG(LogTemp, Warning, TEXT("Client Disconnected"));
closesocket(GameInstance->GetLoggedInPlayer().PlayerSocket);
WSACleanup();
return 0;
}
server
WSAData wsa;
struct addrinfo hints; // Server Hint details
struct addrinfo* server = NULL; // Address info of the server
SOCKET serverSocket = INVALID_SOCKET; // Server Listening Socket
PlayerArray* Players = new PlayerArray(); // Reference to all the players in the server
LobbyArray* Lobbies = new LobbyArray(); // Reference to all the lobbies in the server
// Initialize the winsock library
std::cout << "Initializing WinSock..." << std::endl;
int WSA_Init = WSAStartup(MAKEWORD(2, 2), &wsa);
if (WSA_Init != 0)
{
std::cerr << "Error Initializing Winsock";
WSACleanup();
return;
}
else
{
std::cout << "Winsock Initialized" << std::endl;
}
// Setup Hints
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Setup the server
std::cout << "Setting up Server" << std::endl;
getaddrinfo(IP_ADDRESS, PORT, &hints, &server);
// Create the listening socket
std::cout << "Creating Listening socket" << std::endl;
serverSocket = socket(server->ai_family, server->ai_socktype, server->ai_protocol);
if (serverSocket == INVALID_SOCKET)
{
std::cerr << "Failed creating listening socket" << std::endl;
WSACleanup();
return;
}
else
{
std::cout << "Created listen socket" << std::endl;
}
// Set the socket to be TCP
setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY, &OPTION_VALUE, sizeof(int));
// Bind the socket
std::cout << "Binding Socket..." << std::endl;
bind(serverSocket, server->ai_addr, (int)server->ai_addrlen);
// Start the server
std::cout << "Server has started & is listening..." << std::endl;
listen(serverSocket, SOMAXCONN);
while (true)
{
std::cout << "Players Connected: " << Players->Count() << std::endl;
SOCKET Incoming = INVALID_SOCKET; // Define a socket for anything incoming
Incoming = accept(serverSocket, NULL, NULL); // Accept the incoming message from the socket
// If the socket is not valid than continue through the loop
if (Incoming == INVALID_SOCKET)
{
std::cout << "Invalid Socket" << std::endl;
continue;
}
else
{
std::cout << "Valid Socket" << std::endl;
}
char tempmsg[DFT_BUFLEN] = ""; // Define a temp msg to store the message from the client
int received = recv(Incoming, tempmsg, DFT_BUFLEN, 0); // Receive a message from the client
std::string convertedMessage = tempmsg;
// Check that the received message is from a valid socket
if (received != SOCKET_ERROR)
{
std::string message = tempmsg; // Assing the temp message to a string to split
if (convertedMessage == "Disconnect")
{
Players->RemovePlayer(Incoming);
std::cout << "Player Disconnected..." << std::endl;
continue;
}
else
{
std::cout << tempmsg << std::endl; // === DEBUG ===
// Split the string
char* next_split;
char* split_string = strtok_s(tempmsg, "-", &next_split);
std::string FirstMsg = split_string;
if (FirstMsg == "Username")
{
std::cout << next_split << " Has joined the server" << std::endl; // Server message
// Get the player that we want to set the username to
// Create the player and add it to the server list
Player* NewPlayer = new Player();
Players->AddPlayer(NewPlayer);
NewPlayer->SetUsername(next_split); // Set the usernames
continue;
}
else if (split_string == "Lobby")
{
if (next_split == "Create")
{
Lobby* NewLobby = Lobbies->CreateLobby(); // Create a new lobby
Player* SocketPlayer = Players->GetPlayerBySocket(Incoming); // Get the player creating it by socket
// ensure that the player is valid, if so add the player to the lobby
// Otherwise send an error message to the console.
if (SocketPlayer != nullptr)
{
NewLobby->AddPlayerToLobby(SocketPlayer);
}
else
{
std::cerr << "Failed Locate player to add to lobby" << std::endl;
}
}
else if (next_split == "Destroy")
{
// TODO: Destroy Specific lobby
}
continue;
}
else
{
std::cout << "Error Reading Message" << std::endl;
}
}
}
else
{
std::cerr << "Socket Error when recieving message" << std::endl;
}
}
// Clean up the server
delete Players;
delete Lobbies;
closesocket(serverSocket);
WSACleanup();
return;
Console output after disconnecting
You are making a fundamental TCP mistake. TCP is a stream protocol., its only gurantees are
the bytes you send will be received in the same order they were sent
they will be received only once
BUT there are no 'messages' or 'records' in TCP. You can send a 100 byte message and the other end can receive
one 100 byte message
25 4 byte messages
100 1 byte messages
one 25, one 12, one 3, and one 60 (hope my math is correct)
So in the receive logic you must do this
char buffer[1000]; // or whatever
int length = ????;
char* bptr = buffer;
while(length > 0){
int recvLen = recv(sock, bptr, length,0);
if (recvLen < 1){
// error - disconnect or other failure
break;
}
bptr += recvLen;
length -= recvLen;
}
Ie keep pulling data till you have the whole message
BUT this means you need to know the messages length in advance. So either
send a well known sized length first
send fixed length messages
Or you can have a recognizable termination sequence- ie 10 byte of FF means end of message (see crlfcrlf at end of HTTP get for example)
The first option is the most robust (send length then data)
I am currently trying to understand how HTTP requests work so I am using winsock2 for c++ to try and send some HTTP requests manually.
My code looks like this:
#include <iostream>
#include <SFML/Graphics.hpp>
#include "EasyWinSock.h"
char ip[] = "prnt.sc";
char port[] = "80";
int main() {
easy_win_sock ews(ip, port);
ews.init_win_sock();
ews.create_win_sock();
ews.connect_win_sock();
char sendbuf2[] = "GET /t/gh17d-1645175682/post HTTP/1.1\r\nUser-Agent: kekwtestkekw\r\nHost: ptsv2.com\r\n\r\n";
char sendbuf[] = "GET /111111 HTTP/1.1\r\nHost: www.prnt.sc\r\n\r\n";
ews.send_win_sock(sendbuf, (int)strlen(sendbuf));
DATA *result = ews.recieve_win_sock_text(512); // dynamically allocated
/*
for (int i = 0; i < result->content->size(); i++) {
std::cout << (*result->content)[i];
}
*/
ews.cleanup_win_sock();
return 0;
}
With the Winsocket tucked away in the struct:
#pragma once
#pragma comment(lib, "ws2_32.lib")
#include <iostream>
#include <vector>
#include <winsock2.h>
#include <WS2tcpip.h>
struct DATA {
std::vector<char*>* content;
int size;
};
struct easy_win_sock {
char *ip;
char *port;
WSADATA *wsaData;
addrinfo* result = nullptr;
addrinfo* ptr = nullptr;
SOCKET *sock;
easy_win_sock(char* ip, char* port) {
this->ip = ip;
this->port = port;
this->sock = new SOCKET;
this->wsaData = new WSADATA;
}
void init_win_sock() {
int i_result = WSAStartup(MAKEWORD(2, 2), wsaData);
if (i_result != 0) {
std::cout << "WASStartup failed: " << i_result << std::endl;
exit(1);
}
}
void create_win_sock() {
this->result = NULL;
this->ptr = NULL;
addrinfo hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// get ip adress of hostname
int i_result = getaddrinfo(this->ip, this->port, &hints, &this->result);
if (i_result != 0) {
std::cout << "getaddrinfo failed: " << i_result << std::endl;
WSACleanup();
exit(1);
}
// create socket
*this->sock = INVALID_SOCKET;
// Attempt to connect to the first adress returned by the call to getaddrinfo
this->ptr = this->result;
// create socket for connecting to server
*this->sock = socket(this->ptr->ai_family, this->ptr->ai_socktype, this->ptr->ai_protocol);
if (*this->sock == INVALID_SOCKET) {
std::cout << "Error at socket(): " << WSAGetLastError() << std::endl;
freeaddrinfo(this->result);
WSACleanup();
exit(1);
}
}
void connect_win_sock() {
// connect to server
int i_result = connect(*this->sock, this->ptr->ai_addr, (int)this->ptr->ai_addrlen);
if (i_result == SOCKET_ERROR) {
closesocket(*this->sock);
*this->sock = INVALID_SOCKET;
}
// if connection failed we just close everithing instead of trying other adresses from getadressinfo()
freeaddrinfo(result);
if (*this->sock == INVALID_SOCKET) {
std::cout << "Unable to connect to server!" << std::endl;
WSACleanup();
exit(1);
}
}
void send_win_sock(char* data, int buf_len) {
int i_result = send(*this->sock, data, buf_len, 0);
if (i_result == SOCKET_ERROR) {
std::cout << "Send failed: " << WSAGetLastError() << std::endl;
closesocket(*this->sock);
WSACleanup();
exit(1);
}
}
DATA* recieve_win_sock_text(int buf_size) {
std::vector<char*>* recvbufs = new std::vector<char*>;
DATA* data = new DATA;
// recieve data till server closes connection
int i_result = 1; // number of recieved bytes
while (i_result > 0) {
recvbufs->push_back(new char[buf_size]);
i_result = recv(*this->sock, (*recvbufs)[recvbufs->size() - 1], buf_size - 1, 0);
if (i_result >= 0) {
(*recvbufs)[recvbufs->size() - 1][i_result] = '\0';
std::cout << (*recvbufs)[recvbufs->size() - 1];
}
else {
std::cout << "recv failed: " << WSAGetLastError() << std::endl;
}
}
data->content = recvbufs;
return data;
}
void cleanup_win_sock() {
// shutdown the connection for sending
// still can recieve
int i_result = shutdown(*this->sock, SD_SEND);
if (i_result == SOCKET_ERROR) {
std::cout << "Shutdown failed: " << WSAGetLastError() << std::endl;
closesocket(*this->sock);
WSACleanup();
exit(0);
}
closesocket(*this->sock);
WSACleanup();
delete this->sock;
delete this->wsaData;
}
};
I am using http://ptsv2.com/ to try out my HTTP requests and on this site they work. When I however try to send a GET request to the site prnt.sc/111111 I get different Error codes depending on my GET request. When I just do a
GET /111111 HTTP/1.1
Host: prnt.sc
<empty line>
for example, I get a 403 Forbidden.
When I use the Website https://reqbin.com/ to test HTTP requests and i put in prnt.sc/111111 it generates the same request but the response it shows is 200 OK.
Can anyone help me? I'm seriously stuck here.
Thanks in advance.
I have a local server which has a REST API running at https://localhost:61000, to which I can send the following Curl request:
curl -X POST "https://localhost:61000/users/logout" -H "accept: */*" -H "Locale: en"
and it would return me the following response
In MSVC++ using the WinSock and OpenSSL library I can get the response code 200 from google.com at port 443, but when I try for localhost at port 61000 I always get a HTTP/1.1 400 Bad Request
// Client
int main(int argc, char* argv[])
{
WSADATA wsadata;
if ((WSAStartup(MAKEWORD(2, 0), &wsadata)) == 0) {
cout << "-WSAStartup Initialized." << endl;
struct addrinfo hints, * res;
int sockfd;
memset(&hints, 0, sizeof hints);
hints.ai_socktype = 0;// SOCK_STREAM;
hints.ai_protocol = 0;// IPPROTO_TCP;
hints.ai_family = AF_UNSPEC;
if (getaddrinfo("localhost", "61000", &hints, &res) != 0) {
cout << "-getaddrinfo unsuccessful." << WSAGetLastError() << endl;
}
if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == INVALID_SOCKET) {
cout << "-Unable to create socket." << WSAGetLastError() << endl;
}
if ((connect(sockfd, res->ai_addr, res->ai_addrlen)) != SOCKET_ERROR) {
cout << "-Connection Established." << endl;
}
cout << "-Client connected to: " << res->ai_addr << endl;
SSL_library_init();
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
const SSL_METHOD* meth = TLSv1_2_client_method();
SSL_CTX* ctx = SSL_CTX_new(meth);
ssl = SSL_new(ctx);
if (!ssl) {
printf("Error creating SSL.\n");
log_ssl();
return -1;
}
sock = SSL_get_fd(ssl);
SSL_set_fd(ssl, sockfd);
int err = SSL_connect(ssl);
if (err <= 0) {
printf("Error creating SSL connection. err=%x\n", err);
log_ssl();
fflush(stdout);
return -1;
}
printf("SSL connection using %s\n", SSL_get_cipher(ssl));
const char* request = "GET /users HTTPS/1.1\r\n\r\n";
//SendPacket
int len = SSL_write(ssl, request, strlen(request));
if (len < 0) {
int err = SSL_get_error(ssl, len);
switch (err) {
case SSL_ERROR_WANT_WRITE:
return 0;
case SSL_ERROR_WANT_READ:
return 0;
case SSL_ERROR_ZERO_RETURN:
case SSL_ERROR_SYSCALL:
case SSL_ERROR_SSL:
default:
return -1;
}
}
//RecvPacket
len = 100;
char buf[20000];
do {
len = SSL_read(ssl, buf, 100);
buf[len] = 0;
printf(buf);
} while (len > 0);
if (len < 0) {
int err = SSL_get_error(ssl, len);
if (err == SSL_ERROR_WANT_READ)
return 0;
if (err == SSL_ERROR_WANT_WRITE)
return 0;
if (err == SSL_ERROR_ZERO_RETURN || err == SSL_ERROR_SYSCALL || err == SSL_ERROR_SSL)
return -1;
}
}
else {
cout << "-WSAStartup Initialization failed." << endl;
if (WSACleanup() != 0) {
cout << "-WSACleanup Successful." << endl;
}
else {
cout << "-WSACleanup Failed." << endl;
}
}
return 0;
}
Response:
Assuming that the (HTTP) 400 Bad Request response status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error, but I can not figure out how to create the request in right way.
I'm trying to build a fast server & client which work in localhost. The idea is to send data blobs from another program, and be quick about it. only one client connects to the server at a time.
I first tried implementing this using boost::asio library. everything worked fine, except that the throughput was abysmally slow, 415megabytes/s.
Then I proceeded to create a testcase with winsock, it too had very similar throughput, 434megabytes/s. abysmally slow.
I was expecting more of at range of 40Gigabytes or at least several gigabytes per second.
Id appreaciate any suggestions, as I am way out of my element, with networks programming.
My current client function:
bool sendDataWin(size_t size, const size_t blocksize, size_t port)
{
int result;
struct addrinfo *addressinfo = nullptr, hints;
auto sport = std::to_string(port);
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the local address and port to be used by the server
result = getaddrinfo("localhost", sport.c_str(), &hints, &addressinfo);
if (result != 0) {
printf("Error at client socket(): %ld\n", WSAGetLastError());
return false;
}
SOCKET connection = socket(
addressinfo->ai_family,
addressinfo->ai_socktype,
addressinfo->ai_protocol);
if (connection == INVALID_SOCKET) {
printf("Error at client socket(): %ld\n", WSAGetLastError());
freeaddrinfo(addressinfo);
return false;
}
// Try to put loopback fast path on.
bool value;
DWORD dwBytesRet;
int status =
WSAIoctl(
connection,
SIO_LOOPBACK_FAST_PATH,
&value,
sizeof(bool),
NULL,
0,
&dwBytesRet,
0,
0);
if (status == SOCKET_ERROR) {
DWORD LastError = ::GetLastError();
if (LastError == WSAEOPNOTSUPP) {
printf("client call is not supported :: SIO_LOOPBACK_FAST_PATH\n");
}
}
// Connect to server.
result = connect(connection, addressinfo->ai_addr, (int)addressinfo->ai_addrlen);
if (result == SOCKET_ERROR) {
printf("Error at client socket(): %ld\n", WSAGetLastError());
closesocket(connection);
return false;
}
freeaddrinfo(addressinfo);
std::vector<uint8_t> buffer;
buffer.resize(blocksize);
size_t total = 0;
do {
size_t sendSize = blocksize;
size_t next = total + sendSize;
if (next > size)
{
sendSize -= next - size;
}
// Echo the buffer back to the sender
result = send(connection, (const char*)buffer.data(), (int)sendSize, 0);
if (result == SOCKET_ERROR) {
printf("client send failed: %d\n", WSAGetLastError());
closesocket(connection);
return false;
}
total += sendSize;
} while (total < size);
result = shutdown(connection, SD_SEND);
if (result == SOCKET_ERROR) {
printf("client shutdown failed: %d\n", WSAGetLastError());
closesocket(connection);
return false;
}
// cleanup
closesocket(connection);
return true;
}
server function:
bool serverReceiveDataWin(size_t size, const size_t blocksize, size_t port)
{
int result;
struct addrinfo *addressinfo = nullptr, *ptr = nullptr, hints;
auto sport = std::to_string(port);
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the local address and port to be used by the server
result = getaddrinfo(nullptr, sport.c_str(), &hints, &addressinfo);
if (result != 0) {
printf("Error at server socket(): %ld\n", WSAGetLastError());
return false;
}
SOCKET ListenSocket = socket(addressinfo->ai_family, addressinfo->ai_socktype, addressinfo->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at server socket(): %ld\n", WSAGetLastError());
freeaddrinfo(addressinfo);
return false;
}
// Try to put loopback fast path on.
bool value;
DWORD dwBytesRet;
int status =
WSAIoctl(
ListenSocket,
SIO_LOOPBACK_FAST_PATH,
&value,
sizeof(bool),
NULL,
0,
&dwBytesRet,
0,
0);
if (status == SOCKET_ERROR) {
DWORD LastError = ::GetLastError();
if (LastError == WSAEOPNOTSUPP) {
printf("server call is not supported :: SIO_LOOPBACK_FAST_PATH\n");
}
}
// Setup the TCP listening socket
result = bind(ListenSocket, addressinfo->ai_addr, (int)addressinfo->ai_addrlen);
if (result == SOCKET_ERROR) {
printf("server bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(addressinfo);
closesocket(ListenSocket);
return false;
}
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
printf("Listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
return false;
}
// Accept a client socket
SOCKET ClientSocket = accept(ListenSocket, nullptr, nullptr);
if (ClientSocket == INVALID_SOCKET) {
printf("server accept failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
return false;
}
std::vector<uint8_t> buffer;
buffer.resize(blocksize);
size_t total = 0;
// Receive until the peer shuts down the connection
do {
size_t currentSize = blocksize;
size_t next = total + currentSize;
if (next > size)
{
currentSize -= next - size;
}
result = recv(ClientSocket, (char*)buffer.data(), (int)currentSize, 0);
if (result > 0)
{
total += result;
}
else if (result == 0)
{
printf("server Connection closing...\n");
return false;
}
else {
printf("server recv failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
return false;
}
} while (total < size);
result = shutdown(ClientSocket, SD_SEND);
if (result == SOCKET_ERROR) {
printf("server shutdown failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
return false;
}
// cleanup
closesocket(ClientSocket);
return true;
}
and the test program itself is:
int main()
{
int width = 1920;
int height = 1080;
const size_t totalBpp = 3;
const size_t totalSize = width * height * totalBpp;
size_t port = 27140;
size_t times = 1000;
size_t expectedData = totalSize * times;
// Initialize Winsock
int iResult;
WSADATA wsaData;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
std::cout << "WSAStartup failed: " << iResult << std::endl;
return EXIT_FAILURE;
}
std::atomic_bool serverOk{ false };
std::atomic_bool clientOk{ false };
auto start = std::chrono::high_resolution_clock::now();
std::thread server = std::thread([&] {
serverOk = serverReceiveDataWin(expectedData, totalSize, port);
});
std::thread client = std::thread([&] {
clientOk = sendDataWin(expectedData, totalSize, port);
});
client.join();
server.join();
auto end = std::chrono::high_resolution_clock::now();
WSACleanup();
if (!(clientOk && serverOk))
{
if (!serverOk) std::cout << "Server was not OK." << std::endl;
if (!clientOk) std::cout << "Client was not OK." << std::endl;
return EXIT_FAILURE;
}
std::chrono::duration<double> diff = end - start;
double frameTime = diff.count() / times;
double fps = 1.0 / frameTime;
std::cout << "Sent: " << width << "x" << height << "_" << totalBpp << "(" << totalSize << "). times: " << times << std::endl;
std::cout << "frameTime: " << frameTime << "s." << std::endl;
std::cout << "fps: " << fps << "." << std::endl;
std::cout << "transfer rate : " << ((expectedData / diff.count()) / 1048576) << " mebibytes/s." << std::endl;
std::cout << "transfer rate : " << ((expectedData / diff.count()) / 1000000) << " megabytes/s." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds{ 60 });
return EXIT_SUCCESS;
}
on my machine I get these results:
Sent: 1920x1080_3(6220800).times : 1000
frameTime : 0.0138217s.
fps : 72.35.
transfer rate : 429.225 mebibytes / s.
transfer rate : 450.075 megabytes / s.
It seems that the issue was somewhat my firewall/anti-virus, I first unloaded F-secure, the speeds increased to 500megabytes/s.. after uninstall & reboot, the speeds increased to 4000megabytes/s.
A couple points.
the IOCTL is not called correctly.
bool value;
DWORD dwBytesRet;
int status =
WSAIoctl(
ListenSocket,
SIO_LOOPBACK_FAST_PATH,
&value,
sizeof(bool),
NULL,
0,
&dwBytesRet,
0,
0);
This should pass in a DWORD (32 bit integer) set to 1. The above code passes a C++ bool (probably only 1 byte) that was never initialized to anything.
Since this is only a single socket, this is going to be CPU limited, since it's effectively going to just be going from user/kernel and memcopying on a single thread. It's not likely that this will hit 40 Gbps over a single connection. Particularly since this is only sending 6GB worth of data.
You should turn off Nagle's Algorithm when sending large amounts of data like this. Set TCP_NODELAY on the sending socket.
I try to read site body from get request but I only get the status code and some of start from html text (This is what I got -"HTTP/1.1 200 OK Server: Apa!DOCTYPE html html lang=e"). I would appreciate if you could help me fix the problem. Thanks
Code -
#include <winsock2.h>
#include <WS2tcpip.h>
#include <windows.h>
#include <iostream>
#include <vector>
#include <ostream>
int main(){
// Initialize Dependencies to the Windows Socket.
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cout << "WSAStartup failed.\n";
system("pause");
}
struct addrinfo hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
/* connect and download the article */
static const char wiki_host[] = "en.wikipedia.org";
struct addrinfo* targetAdressInfo = NULL;
DWORD getAddrRes = getaddrinfo(wiki_host, NULL, &hints, &targetAdressInfo);
if (getAddrRes != 0 || targetAdressInfo == NULL)
{
cout << "Could not resolve the Host Name" << endl;
system("pause");
WSACleanup();
return -1;
}
SOCKADDR_IN sockAddr;
sockAddr.sin_addr = ((struct sockaddr_in*) targetAdressInfo->ai_addr)->sin_addr;
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(80);
freeaddrinfo(targetAdressInfo);
SOCKET webSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (webSocket == INVALID_SOCKET)
{
cout << "Creation of the Socket Failed" << endl;
system("pause");
WSACleanup();
return -1;
}
if (connect(webSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr)) != 0)
{
cout << "Could not connect";
system("pause");
closesocket(webSocket);
WSACleanup();
return -1;
}
string http_query = "GET / https://en.wikipedia.org/w/api.php?titles=StackOverflow&action=query&prop=extracts&format=json\r\nConnection: close\r\n\r\n";
if (send(webSocket, http_query.c_str(), http_query.length(), 0) == -1) {
cout << "Could not send the request to the Server" << endl;
system("pause");
closesocket(webSocket);
WSACleanup();
return -1;
}
/* prepare to fetch the wiki article */
string response = "";
while (true) {
static char recv_buffer[4096];
const int bytes_read = recv(webSocket, recv_buffer, sizeof(recv_buffer) - 1, 0);
if (!bytes_read) {
break;
}
if (bytes_read == -1) {
closesocket(webSocket);
WSACleanup();
}
recv_buffer[bytes_read] = '\0';
response += recv_buffer;
};
/* finished with the socket */
closesocket(webSocket);
WSACleanup();
/* parse the http response headers */
size_t cursor = 0;
string response_content;
vector<std::string> response_headers;
const size_t headers_end = response.find("\r\n\r\n");
while (true) {
const size_t line_end = response.find("\r\n", cursor);
if (line_end == std::string::npos) { /* probably due to http error */
break;
}
response_headers.push_back(response.substr(cursor, line_end - cursor));
if (line_end == headers_end) { /* found content */
response_content = response.substr(headers_end + 4); /* skip \r\n\r\n */
break;
}
cursor = line_end + 2; /* skip \r\n */
}
// print the respone
for (int i = 0; i < sizeof(response); i++){
cout << response[i];
}
//print response_content
for (int i = 0; i < sizeof(response); i++){
cout << response_content[i];
}
system("pause");
return 0;
}
This doesn't work:
for (int i = 0; i < sizeof(response); i++){
cout << response[i];
}
Because sizeof(response) is the size of the string object, not the length of the string. You should simply do
cout << response;
If you really want to iterate through the string (which is slower and not recommended) you'd have to use response.size() instead of sizeof(response).
There are a few other issue with your code as well, but this should solve the problem at hand.