I am using windows sockets with c++. In the following call I am trying to reply a message to the socket that just connected.
I tried connecting using a dummy client in c++. It would connect but the recv() would not receive anything.
Then I tried using telnet, it worked instantly, just as i wanted.
SOCKET s = accept(ls, (sockaddr*)&clientSin, &s_len);
if (s == INVALID_SOCKET) {
cerr << "Error in accept call: " << WSAGetLastError();
}
else {
cout << "Connection accepted at , socket no. :" << s << endl;
//adding to list of incoming sockets
inactiveList.push_back(s);
//send message to client requesting credentials
char buff[10];
// the character 'x' is a code to the client to provide the server with the credentials
buff[0] = 'x';
buff[1] = '\0';
//send(s, buff, 2, 0);
if (send(s, "From Vic: ", 10, 0) == INVALID_SOCKET)
{
int errorcode = WSAGetLastError();
cerr << "send to client failed: " << errorcode << endl;
closesocket(s);
continue;
}
Sleep(1000);
if (send(s, "From Vic: ", 10, 0) == INVALID_SOCKET)
{
int errorcode = WSAGetLastError();
cerr << "send to client failed: " << errorcode << endl;
closesocket(s);
continue;
}
}
the recv code is:
tnb = 0;
while ((nb = recv(s, &buff[tnb], LINESZ - tnb, 0)) > 0)
{
tnb += nb;
}
/* If there was an error on the read, report it. */
if (nb < 0)
{
printf("recv failed: %d\n", WSAGetLastError());
return 1;
}
if (tnb == 0)
{
printf("Disconnect on recv");
}
/* Make the response NULL terminated and display it. Using C output */
printf("tnb = %d\n", tnb);
buff[tnb] = '\0';
puts(buff);
Taking all my comments and turning it into an answer.
I suspect your recv loop is continuing forever because you haven't sent enough data to make it break out of the loop.
Change this:
while ((nb = recv(s, &buff[tnb], LINESZ - tnb, 0)) > 0)
{
tnb += nb;
}
To this: (notice that I'm allocating +1 for the array buff)
char buff[LINESZ+1]; // +1 for null terminator
buff[0] = '\0';
tnb = 0;
while (tnb < LINESZ)
{
nb = recv(s, &buff[tnb], LINESZ-tnb, 0);
if (nb < 0)
{
printf("Error on socket: %d\n", (int)WSAGetLastError());
break;
}
else if (nb == 0)
{
printf("Remote socket closed\n");
break;
}
printf("Received: %d bytes\n", (int)nb);
tnb += nb;
buff[tnb] = '\0'; // null terminate the end of the buffer so it will print reliably
}
Related
I am new in Socket Programming. I am trying to create a Application that uses Sockets for Communication.
I have doubt in the Receive function because sometime it just hangs in recvfrom function.
I am using select function for polling. It works when Camera is connected but If I remove Camera it doesn't show the Error Message.
My Code for Polling:
FD_ZERO(&m_readFds);
FD_SET(Sock, &m_readFds);
m_timeInterval.tv_usec = 30; //30 Microseconds for Polling
m_socketLength = sizeof(m_cameraInfo);
m_lastBlockId = -1;
while (m_acquiringThreadStatus)
{
FD_CLR(Sock, &m_readFds);
FD_SET(Sock, &m_readFds);
m_receivingStatus = select(Sock + 1, &m_readFds, NULL, NULL, &m_timeInterval);
if (m_receivingStatus < 0)
{
std::cout << "No Data to Receive"<<std::endl;
}
else
{
if ((m_receivedBytes = recvfrom(Sock, m_packetBuffer, RECEIVING_BUFFER_SIZE, 0, (struct sockaddr*)&m_cameraInfo, &m_socketLength)) == SOCKET_ERROR)
{
std::cout << "ERROR" << std::endl;
}
else
{
std::cout<<"Data Received"<<std::endl;
}
}
}
An one more question is that when I continously printing the Data Received Statement after sometime It stop. So how can I increase the size of Socket Receiving Buffer.
Thanks in Advance
Edit
SOCKET m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(m_sock == INVALID_SOCKET)
{
// Error
}
else
{
//In the else part bind the socket
}
If you read select()'s documentation, you would see that select() returns -1 on error, 0 on timeout, and > 0 on requested event(s). However, that is not how you are treating the return value. You are treating -1 as timeout and >= 0 as data events. So, you end up calling recvfrom() when there is nothing available to read. If the socket is in blocking mode (its default mode), recvfrom() will block the calling thread until data is actually available.
Try this instead:
m_lastBlockId = -1;
while (m_acquiringThreadStatus)
{
FD_ZERO(&m_readFds);
FD_SET(Sock, &m_readFds);
m_timeInterval.tv_sec = 0;
m_timeInterval.tv_usec = 30; //30 Microseconds for Polling
m_receivingStatus = select(Sock + 1, &m_readFds, NULL, NULL, &m_timeInterval);
if (m_receivingStatus == SOCKET_ERROR)
{
std::cout << "ERROR" << std::endl;
break;
}
if (m_receivingStatus == 0)
{
std::cout << "No Data to Receive" << std::endl;
continue;
}
m_socketLength = sizeof(m_cameraInfo);
if ((m_receivedBytes = recvfrom(Sock, m_packetBuffer, RECEIVING_BUFFER_SIZE, 0, (struct sockaddr*)&m_cameraInfo, &m_socketLength)) == SOCKET_ERROR)
{
std::cout << "ERROR" << std::endl;
break;
}
std::cout << "Data Received" << std::endl;
}
As for the size of the socket's receive buffer, you can set it via setsockopt() using the SO_RCVBUF option, eg:
int bufsize = ...;
setsockopt(Sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, sizeof(bufsize));
The following is the client-side code for a UDP client-server echo program :
ret_val = sendmmsg(socket_id, msgs, no_of_packets, 0);
//I send message to the server
if(ret_val == -1)
std::cerr << "Message sending failed.\n";
else{
cout << ret_val << " messages sent\n";
/************************************************************************/
char buffers[no_of_packets][packet_size + 1];
msgs = new struct mmsghdr[no_of_packets];
iovecs = new struct iovec[no_of_packets];
memset(msgs, 0, sizeof(msgs));
memset(iovecs, 0, sizeof(iovecs));
for(int i = 0;i < no_of_packets;i++){
iovecs[i].iov_base = buffers[i];
iovecs[i].iov_len = packet_size;
msgs[i].msg_hdr.msg_iov = &iovecs[i];
msgs[i].msg_hdr.msg_iovlen = 1;
}
//and receive the packet here, but the program hangs here
ret_val = recvmmsg(socket_id, msgs, no_of_packets, 0, NULL);
My program hangs here, any idea why it's happening ? The following is the server-side code which first receives and then sends successfully, but after the server sends for the first time, my client isn't able to read it as it hangs.
ret_val = recvmmsg(socket_id, msgs, no_of_packets, 0, NULL);
if(ret_val < 0){
break;
}
else{
cout << ret_val << " messages received\n";
for(int i = 0;i < ret_val;i++){
buffers[i][msgs[i].msg_len] = 0;
printf("Trip %d : %s\n", trip, buffers[i]);
}
/************************************************************************/
if(connect(socket_id, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1){
perror("connect()");
exit(EXIT_FAILURE);
}
ret_val = sendmmsg(socket_id, msgs, no_of_packets, 0);
//This send is successful, but since my client hangs,
//the server hangs as well since the 'recvmmsg' at the top gets nothing from the client
if(ret_val == -1)
std::cerr << "Message sending failed.\n";
else
cout << ret_val << " messages sent\n";
This line in the server code looks suspicious:
if(connect(socket_id, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1)
What is server_addr? It's certainly not the source address of any of the packets return from the prior call to recvmmsg.
Just remove the connect call.
I could write more, but is there any particular reason you are using recvmmsg and sendmmsg instead of recvfrom and sendto ?
Below is a much simpler way of implementing an echo server with a udp socket:
const int MAX_UDP_MESSAGE_SIZE = 65535
unsigned char message[MAX_UDP_MESSAGE_SIZE+1];
int rcvReslt, sndResult;
sockaddr_in addr = {};
socklen_t addrLength = sizeof(addr);
rcvResult = recvfrom(socket_id, message, MAX_UDP_MESSAGE_SIZE, 0, (sockaddr*)&addr, &addrLength);
if (rcvResult > 0)
{
message[rcvResult] = 0; // null terminate the message
printf("Trip %d : %s\n", trip, message);
// echo back
sndResult = sendto(socket_id, message, rcvResult, 0, (sockaddr*)&addr, addrLength);
}
else
{
int error_code = errno;
printf("Error: %d\n", error_code);
}
Clearly you're connected to the wrong target. You don't need to connect at all. recvfrommsg() both return the source IP:port. Just send back to the same place.
I'm having an issue with winsock on windows 8.1 where recv keeps returning 0 randomly. I'm running both client and server on the same machine (thus all traffic is pointed at the loopback address) and i have breakpoints on any statement on both client and server which would shut down the sockets. But when the issue occurs the server is still operating normally, and hasn't shutdown anything, while the client has hit a breakpoint that only triggers on recv returning 0 or less.
The client keeps returning 0 from recv randomly (although always at the same point in my code) when the server VS 2013 project is set to compile as a windows program (rather than a console, in order to make it produce no window, it's supposed to be silent running). The bug doesn't seem to occur when i compile the server as a console application, as I've been debugging the program in that mode and only come across this issue when i switched compilation settings.
Is there any way to launch a console window when compiling as a windows application so i can see any debug statements?
Does winsock behave differently when compiling for the console vs compiling for a windows application?
Why would the client's recv return 0, when I've not sent a shutdown signal from the server?
My code, ask if there's any more you need:
Client
void recvLoop()
{
int recievedBytes = 1;
while (running && recievedBytes > 0)
{
WORD dataSize;
WORD dataType;
int recievedBytesA = ConnectSock.Recieve(&dataSize, sizeof(WORD));
if (recievedBytesA <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
int recievedBytesB = ConnectSock.Recieve(&dataType, sizeof(WORD));
if (recievedBytesB <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
unique_ptr<char[]> data(new char[dataSize]);
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true; //breakpoint set here - Always triggers here
attemptKillThreads();
continue;
}
//use the received data.....
}
}
When this breaks recievedBytesA = 2, recievedBytesB = 2, recievedBytesC = 0, dataType = 0, dataSize = 0
ConnectSock is a global of type ConnectSocket. here is its Recieve()
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock != INVALID_SOCKET)
{
int i = recv(sock, (char *)recvbuf, recvbuflen, 0);
if ((i == SOCKET_ERROR))
{
int err = 0;
err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
}
return i;
}
return -2;
}
Server:
void sendLoop()
{
int bytessent = 0;
QueuePack tosend;
while (running)
{
tosend = sendQueue.Dequeue();
if (tosend.packType == QueuePack::EXIT || tosend.packType == 0 || tosend.dSize == 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.dSize, sizeof(WORD));
//cout used to see what exactly is being sent, even if it is garbage when converted to text
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.packType, sizeof(WORD));
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(tosend.bufferPtr(), tosend.dSize);
cout << tosend.bufferPtr() << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
}
}
if (Connection->shutdownSock(SD_SEND) == SOCKET_ERROR)
{
Connection->closeSock();
}
}
SendData is literally a wrapper for send that uses a reinterpret_cast
int SendData(void * writeBuffer, int bufferSize)
{
return send(SocketManager.clientSocket, reinterpret_cast<char *>(writeBuffer), bufferSize, 0);
}
sendQueue is a Bounded blocking queue that holds QueuePacks
QueuePacks are used to transfer data, it's size and what kind of data it is between threads. both Client and server use this as it allows me to make sure data gets to the right thread on the client
Queuepack has 2 public variables packType and dSize of type WORD.
QueuePack::QueuePack() : packType(UND), dSize(0)
{
int h = 0; //debug statement to break on - never gets hit after initial collection construction occurs
}
QueuePack::QueuePack(const WORD type, WORD size, char * data) : packType(type), dSize(size)
{
//debug test and statement to break on
if (size == 0 || type == 0)
{
int h = 0; //breakpoint - never gets hit
}
dSize = (dSize < 1 ? 1 : dSize);
_buffer = make_unique<char[]>(dSize);
memcpy(_buffer.get(), data, dSize);
}
QueuePack::QueuePack(QueuePack &other) : packType(other.packType), dSize(other.dSize)
{
//debug test and statement to break on
if (other.dSize == 0 || other.packType == 0)
{
int h = 0; //breakpoint - never gets hit
}
if (&other == this)
{
return;
}
_buffer = make_unique<char[]>(dSize);
other.buffer(_buffer.get());
}
QueuePack QueuePack::operator= (QueuePack &other)
{
// check for self-assignment
if (&other == this)
{
return *this;
}
// reuse storage when possible
if (dSize != other.dSize)
{
_buffer.reset(new char[other.dSize]);
dSize = other.dSize;
}
packType = other.packType;
other.buffer(_buffer.get());
return *this;
}
QueuePack::~QueuePack()
{
}
HRESULT QueuePack::buffer(void* container)
{
try
{
memcpy(container, _buffer.get(), dSize);
}
catch (...)
{
return E_FAIL;
}
return S_OK;
}
char * QueuePack::bufferPtr()
{
return _buffer.get();
}
When this breaks recievedBytesA = 2, recievedBytesB = 2, recievedBytesC = 0, dataType = 0, dataSize = 0
You are calling ConnectSock.Recieve() when dataSize is 0. There is nothing to read, so Receive() reports that 0 bytes were read.
You need to add a check for that condition:
unique_ptr<char[]> data(new char[dataSize]);
if (dataSize != 0) // <-- add this
{
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true;
attemptKillThreads();
continue;
}
}
Your code is also assuming that Recieve() reads all bytes that are requested, it is not handling the possibility that recv() may return fewer bytes. So you need to make Recieve() call recv() in a loop to guarantee that everything requested is actually read:
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(recvbuf);
int total = 0;
while (recvbuflen > 0)
{
int i = recv(sock, buf, recvbuflen, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
if (i == 0)
{
cout << "disconnected" << endl;
return 0;
}
buf += i;
recvbuflen -= i;
total += i;
}
return total;
}
Same with SendData(), as send() may return fewer bytes than requested:
int SendData(void * writeBuffer, int bufferSize)
{
if (SocketManager.clientSocket == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(writeBuffer);
int total = 0;
while (bufferSize > 0)
{
int i = send(SocketManager.clientSocket, buf, bufferSize, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
buf += i;
bufferSize -= i;
total += i;
}
return total;
}
I'm trying to create a server that talks with 2 clients, 1 in each time. After the talking with one client, the server sends a message to both clients.
I found a basic code of a server, and I tried to upgrade it to accept multiple number of connections, and I saw 2 ways of it : threads, or doing array of sockets, but I couldn't understand it.
Can someone explain me how to use threads and give examples please?
This is the code :
int main()
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != 0)
{
std::cout << "WSA Initialization failed!\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
SOCKET Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (Socket == INVALID_SOCKET)
{
std::cout << "Socket creation failed.\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
SOCKADDR_IN serverInf;
serverInf.sin_family = AF_INET;
serverInf.sin_addr.s_addr = INADDR_ANY;
serverInf.sin_port = htons(8888);
if (bind(Socket, (SOCKADDR*)(&serverInf), sizeof(serverInf)) == SOCKET_ERROR)
{
std::cout << "Unable to bind socket!\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
listen(Socket, 1);
SOCKET TempSock = SOCKET_ERROR;
while (TempSock == SOCKET_ERROR)
{
std::cout << "Waiting for incoming connections...\r\n";
TempSock = accept(Socket, NULL, NULL);
}
// If iMode!=0, non-blocking mode is enabled.
u_long iMode = 1;
ioctlsocket(Socket, FIONBIO, &iMode);
Socket = TempSock;
std::cout << "Client connected!\r\n\r\n";
// Main loop
for (;;)
{
char *szMessage = "Welcome to the server!\r\n";
send(Socket, szMessage, strlen(szMessage), 0);
int nError = WSAGetLastError();
if (nError != WSAEWOULDBLOCK&&nError != 0)
{
std::cout << "Winsock error code: " << nError << "\r\n";
std::cout << "Client disconnected!\r\n";
// Shutdown our socket
shutdown(Socket, SD_SEND);
// Close our socket entirely
closesocket(Socket);
break;
}
Sleep(1000);
}
WSACleanup();
system("PAUSE");
return 0;
}
To do so you need one server socket and a clientsocket array like this:
SERVER:
ACCEPT:
int clientsock[2];
minsocks = 0;
numsocks = 2;
while(minsock < numsocks)
{
clientsock[minsock] = accept(serversock,
(struct sockaddr *) &clientaddr,
(socklen_t *)&clientaddrlen);
minsock++;
}
RECIEVE:
char message[6];
int data;
int limit = 6;
for(int i = 0; i < NUMSOCK; i++)
{
int in = recv(clientsock[i], &message[index], limit, 0);
if(in > 0)
{
index += in;
limit -= in;
}
else if ( in == 0 )
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
}
This should be a good beginning for you to start with.
Threads - C version
pthread_t sniffer_thread;
if( pthread_create( &sniffer_thread , NULL , connection_handler , (void*) new_sock) < 0)
{
perror("could not create thread");
return 1;
}
//Now join the thread , so that we dont terminate before the thread
//pthread_join( sniffer_thread , NULL);
puts("Handler assigned");
}
/*
* This will handle connection for each client
* */
void *connection_handler(void *socket_desc)
{
//Get the socket descriptor
int sock = *(int*)socket_desc;
int read_size;
char *message , client_message[2000];
while(in != 0)
{
int in = recv(socket_desc, &client_message[index], limit, 0);
if(in > 0)
{
index += in;
limit -= in;
}
else
printf("recv failed: %d\n", WSAGetLastError());
}
//Free the socket pointer
free(socket_desc);
return 0;
}
when I want to use the same socket connected to the host to send() and recv() for the second time, recv() will return 0 without anything in buffer
basically Im doing:
connect to the website
send packets
receive packets
send packets again (I think this one is working since it's not giving me SOCKET_ERROR)
receive packets again (this one is returning 0 so as "connection closed")
source code: http://pastebin.com/sm5k5GAe
as you can see, I have sock2, which when I use for second send/recv it's working fine, however I will do more communication, and having sockets for all of that and connecting them would be kinda stupid I guess
The only 2 conditions that cause recv() to return 0 are:
if you provided it with a 0-length buffer.
if the other party has gracefully closed the connection on its end. You can use a packet sniffer, such as Wireshark, to verify this.
You are sending HTTP 1.1 requests that include a Connection: close header in them. That tells an HTTP server to close its end of the connection after sending the response. That is perfectly normal behavior. Simply read the response, close your end of the connection, and reconnect before sending the next request.
If you want to keep the connection open so you can send multiple requests over a single connection, send a Connection: keep-alive header instead, or just omit the Connection header altogether since you are sending HTTP 1.1 requests and keep-alive is the default behavior for HTTP 1.1.
Either way, you must look at the server's actual response Connection header to know if it is going to close its end of the connection or not. For an HTTP 0.9 or 1.0 response, if there is no Connection: keep-alive header present then you must assume the connection is going to be closed. For an HTTP 1.1 response, if there is no Connection: close header then you must assume the connection is being left open. But you do have to be prepared to handle the possibility that the connection may still be closed, such as by a intermediate router/firewall, so simply reconnect if the next request fails with a connection error.
With that said, you are better off not implementing HTTP manually, but instead using a pre-made library that handles these details for you, such as libcurl.
Update:
Try something more like this instead:
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
using namespace std;
SOCKET sock;
string currentHost;
bool checkConnection(string Hostname);
int readFromSock(char *data, int datalen, bool disconnectOK = false);
int readData(string &workBuffer, char *data, int datalen, bool disconnectOK = false);
bool readLine(string &workBuffer, string &line);
bool doRequest(string Hostname, string Request, string &Reply);
bool GetReply(string Host, string &Reply);
bool GetLogin(string Reply, string MyStr, string &Login);
bool GetSession(string Reply, string MyStr, string &Session);
bool GetLoginReply(string Host, string Username, string Password, string Login, string Session, string &Reply);
bool Login(string Host, string Resource, string Username, string Password);
bool IsConnected(string Reply, string MyStr);
#pragma comment (lib, "ws2_32.lib")
#pragma warning(disable:4996)
int main()
{
string sLoginSite, sUsername, sPassword;
char buffer[32];
//Initialize Winsock
WSADATA WsaData;
if(WSAStartup(MAKEWORD(2,2), &WsaData) != 0)
{
cout << "WinSock Startup Failed" << endl;
system("pause");
return 1;
}
sock = INVALID_SOCKET;
//Get Login Site
cout << "Travian login site e.g. ts1.travian.com: ";
cin >> buffer;
sLoginSite = buffer;
cout << endl;
//Get Username
cout << "Username: ";
cin >> buffer;
sUsername = buffer;
//Get Password
cout << "Password: ";
cin >> buffer;
sPassword = buffer;
cout << endl;
// Perform Login
if (!Login(sLoginSite, sUsername, sPassword))
{
cout << "Error while Logging in" << endl;
system("pause");
return 1;
}
cout << "Successfully connected to the account \"" << sUsername << "\" at " << sLoginSite << endl;
system("pause");
if (sock != INVALID_SOCKET)
closesocket(sock);
WSACleanup();
return 0;
}
// ensure connection to Hostname
bool checkConnection(string Hostname)
{
// Switching to a different hostname? If so, disconnect...
if (currentHost != Hostname)
{
if (sock != INVALID_SOCKET)
{
closesocket(sock);
sock = INVALID_SOCKET;
}
currentHost = Host;
}
// TODO: make sure the socket is actually still connected. If not, disconnect...
if (sock != INVALID_SOCKET)
{
/*
if (no longer connected)
{
closesocket(sock);
sock = INVALID_SOCKET;
}
*/
}
// Create a new connection?
if (sock == INVALID_SOCKET)
{
// resolve the Hostname...
struct hostent *host = gethostbyname(Hostname.c_str());
if(!host)
{
cout << "Can't Resolve Hostname" << endl;
return false;
}
// Connect to the Hostname...
SOCKET newSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (newSock == INVALID_SOCKET)
{
cout << "Error creating Socket" << endl;
return false;
}
SOCKADDR_IN SockAddr;
ZeroMemory(&SockAddr, sizeof(SockAddr)));
SockAddr.sin_port = htons(80);
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
cout << "Connecting" << endl;
if(connect(newSock, (SOCKADDR*) &SockAddr, sizeof(SockAddr)) != 0)
{
closesocket(newSock);
cout << "Can't Connect to Hostname" << endl;
return false;
}
sock = newSock;
cout << "Successfully connected to " << Hostname << "!" << endl;
}
// Ready
return true;
}
// read raw data from the socket directly
// returns how many bytes were actually read, -1 on error, or 0 on disconnect
int readFromSock(char *data, int datalen, bool disconnectOK)
{
int read = 0;
while (datalen > 0)
{
// more data is expected...
int ret = recv(sock, data, datalen, 0);
if (ret == SOCKET_ERROR)
{
cout << "recv failed: " << WSAGetLastError() << endl;
closesocket(sock);
sock = INVALID_SOCKET;
return -1;
}
if (ret == 0)
{
cout << "server disconnected" << endl;
closesocket(sock);
sock = INVALID_SOCKET;
// if the caller is OK with a disconnect occurring, exit without error...
if (disconnectOK)
break;
return -1;
}
// move forward in the output buffer
data += ret;
datalen -= ret;
// increment the result value
read += ret;
}
// done
return read;
}
// read raw data from an in-memory buffer, reading from the socket directly only when the buffer is empty.
// returns how many bytes were actually read, -1 on error, or 0 on disconnect
int readData(string &workBuffer, char *data, int datalen, bool disconnectOK)
{
int read = 0;
int len;
char buffer[512];
while (datalen > 0)
{
// more data is expected...
len = workBuffer.length();
if (len > 0)
{
// the work buffer has cached data, move to the output buffer...
if (len > datalen) len = datalen;
workBuffer.copy(data, len);
workBuffer.erase(len);
// move forward in the output buffer
data += len;
datalen -= len;
// increment the return value
read += len;
}
else
{
// the work buffer is empty, read from the socket and cache it...
len = readFromSock(buffer, sizeof(buffer), disconnectOK);
if (ret == -1)
return -1;
// disconnected?
if (ret == 0)
break;
// append new data to the work buffer...
workBuffer += string(buffer, ret);
}
}
// done
return read;
}
// reads a LF-delimited line of text from an in-memory buffer, reading from the socket directly only when the buffer is empty.
// returns whether a full line was actually read.
bool readLine(string &workBuffer, string &line)
{
// clear the output...
line = "";
int found, len, start = 0;
char buffer[512];
do
{
// check if a LF is already cached. Ignore previously searched text..
found = workBuffer.find("\n", start);
if (found != string::npos)
{
len = found;
// is the LF preceded by a CR? If so, do include it in the output...
if (len > 0)
{
if (workBuffer[len-1] == '\r')
--len;
}
// output the line, up to but not including the CR/LF...
line = workBuffer.substr(0, len);
workBuffer.erase(found);
break;
}
// the work buffer does not contain a LF, read from the socket and cache it...
len = readFromSock(buffer, sizeof(buffer));
if (len <= 0)
{
closesocket(sock);
sock = INVALID_SOCKET;
return false;
}
// append new data to the work buffer and search again...
start = workBuffer.length();
workBuffer += string(buffer, len);
}
while (true);
// done
return true;
}
// perform an HTTP request and read the reply
// returns whether the reply was actually read
bool doRequest(string Hostname, string Request, string &Reply)
{
// clear the output
Reply = "";
char buffer[512];
string str, workBuffer;
string sContentLength, sTransferEncoding, sConnection;
bool doClose;
// make sure there is a connection, reconnecting if needed...
if (!checkConnection(Hostname))
return false;
// send the request...
char *data = Request.c_str();
int len = Request.length();
int ret;
do
{
ret = send(sock, data, len, 0);
if (ret == SOCKET_ERROR)
{
cout << "Send Error" << endl;
closesocket(sock);
sock = INVALID_SOCKET;
return false;
}
// move forward in the input buffer...
data += ret;
len -= ret;
}
while (len > 0);
// read the response's status line...
if (!readLine(workBuffer, str))
return false;
// TODO: parse out the line's values, ie: "200 OK HTTP/1.1"
int ResponseNum = ...;
int VersionMajor = ...;
int VersionMinor = ...;
// only HTTP 1.0 responses have headers...
if (VersionMajor >= 1)
{
// read the headers until a blank line is reached...
do
{
// read a header
if (!readLine(workBuffer, str))
return false;
// headers finished?
if (str.length() == 0)
break;
// TODO: do case-insensitive comparisons
if (str.compare(0, 15, "Content-Length:") == 0)
sContentLength = str.substr(15);
else if (str.compare(0, 18, "Transfer-Encoding:") == 0)
sTransferEncoding = str.substr(18);
else if (str.compare(0, 18, "Connection:") == 0)
sConnection = str.substr(11);
}
while (true);
// If HTTP 1.0, the connection must closed if the "Connection" header is not "keep-alive"
// If HTTP 1.1+, the connection must be left open the "Connection" header is not "close"
// TODO: do case-insensitive comparisons
if ((VersionMajor == 1) && (VersionMinor == 0))
doClose = (sConnection.compare"keep-alive") != 0);
else
doClose = (sConnection.compare("close") == 0);
}
else
{
// HTTP 0.9 or earlier, no support for headers or keep-alives
doClose = true;
}
// TODO: do case-insensitive comparisons
if (sTransferEncoding.compare(sTransferEncoding.length()-7, 7, "chunked") == 0)
{
// the response is chunked, read the chunks until a blank chunk is reached...
do
{
// read the chunk header...
if (!readLine(workBuffer, str))
return false;
// ignore any extensions for now...
int found = str.find(";");
if (found != string::npos)
str.resize(found);
// convert the chunk size from hex to decimal...
size = strtol(str.c_str(), NULL, 16);
// chunks finished?
if (size == 0)
break;
// read the chunk's data...
do
{
len = size;
if (len > sizeof(buffer)) len = sizeof(buffer);
if (!readData(workBuffer, buffer, len))
return false;
// copy the data to the output
Reply += string(buffer, len);
size -= len;
}
while (size > 0);
// the data is followed by a CRLF, skip it...
if (!readLine(workBuffer, str))
return false;
}
while (true);
// read trailing headers...
do
{
// read a header...
if (!readLine(workBuffer, str))
return false;
// headers finished?
if (str.length() == 0)
break;
// process header as needed, overwriting HTTP header if needed...
}
while (true);
}
else if (sContentLength.length() != 0)
{
// the response has a length, read only as many bytes as are specified...
// convert the length to decimal...
len = strtol(sContentLength.c_str(), NULL, 10);
if (len > 0)
{
// read the data...
do
{
ret = len;
if (ret > sizeof(buffer)) ret = sizeof(buffer);
ret = readData(workBuffer, buffer, ret);
if (ret <= 0)
return false;
// copy the data to the output
Reply += string(buffer, ret);
len -= ret;
}
while (len > 0);
}
}
else
{
// response is terminated by a disconnect...
do
{
len = readData(workBuffer, buffer, sizeof(buffer), true);
if (len == -1)
return false;
// disconnected?
if (len == 0)
break;
// copy the data to the output
Reply += string(buffer, len);
}
while (true);
doClose = true;
}
// close the socket now?
if (doClose)
{
closesocket(sock);
sock = INVALID_SOCKET;
}
// TODO: handle other responses, like 3xx redirects...
return ((ResponseNum / 100) == 2);
}
// Login to Hostname with Username and Password
// returns whether login was successful or not
bool Login(string Hostname, string Username, string Password)
{
string sLoginForm, sReply, sSessionID, sLoginID, sLoginReply;
//Get Login Form HTML
if (!GetReply(Hostname, sReply))
{
cout << "Reply Error" << endl;
return false;
}
//Save Reply
ofstream Data;
Data.open("Reply.txt");
Data << sReply;
Data.close();
//Get Session ID from HTML
if (!GetSession(sReply, "sess_id", sSessionID))
{
cout << "Session ID Error" << endl;
return false;
}
//Get Login ID from HTML
if (!GetLogin(sReply, "<input type=\"hidden\" name=\"login\" value=", sLoginID))
{
cout << "Login ID Error" << endl;
return false;
}
// perform final Login
if (!GetLoginReply(Hostname, Username, Password, sLoginID, sSessionID, sLoginReply))
{
cout << "Login Reply Error" << endl;
return false;
}
/*
if(!IsConnected(sLoginReply, "HTTP/1.1 303 See Other"))
{
cout << "Invalid Username or Password" << endl;
return false;
}
*/
//Save Reply
Data.open("login.txt");
Data << sLoginReply;
Data.close();
// done
return true;
}
bool GetReply(string Hostname, string &Reply)
{
string str;
str = "GET / HTTP/1.1\r\n";
str += "Host: " + Hostname + "\r\n"
str += "Connection: keep-alive\r\n"
str += "\r\n";
return doRequest(Hostname, str, Reply);
}
bool GetSession(string Reply, string MyStr, string &Session)
{
int found = Reply.find(MyStr);
if(found == string::npos)
return false;
Session = Reply.substr(found+MyStr.Length(), 32);
return true;
}
bool GetLogin(string Reply, string MyStr, string &Login)
{
int found = Reply.find(MyStr);
if(found == string::npos)
return false;
Login = Reply.substr(found+MyStr.length(), 10)
return true;
}
bool GetLoginReply(string Hostname, string Username, string Password, string Login, string Session, string &Reply)
{
string str, special;
special = "name=" + Username + "&password=" + Password + "&s1=Login&w=1600%3A900&login=" + Login;
string temp;
stringstream ss;
ss << special.length();
ss >> temp;
str = "POST /dorf1.php HTTP/1.1\r\n";
str += "Host: " + Hostname + "\r\n";
str += "Cookie: sess_id=" + Session + "; highlightsToggle=false; WMBlueprints=%5B%5D\r\n";
str += "Content-Type: application/x-www-form-urlencoded\r\n";
str += "Content-Length: " + temp + "\r\n"
str += "\r\n";
str += special;
return doRequest(Hostname, str, Reply);
}
bool IsConnected(string Reply, string MyStr)
{
return (Reply.find(MyStr) != string::npos);
}