-
When recv() can't
Just a quick question about recv() - sorry, I swear I found it on google earlier but I can't remember exactly what the search string was.
Since you can't (afaik) know the length of the data to be received from the server I was going to use an STL vector so I can push_back() single bytes as I read them, but I don't know what recv() returns (if at all) when there's nothing left to return from the server so I can break the loop I plan to use.
Tips?
-
That depends on weather you're using blocking or non-blocking sockets. If you're using blocking sockets then recv won't return untill there is some data, the connection is closed or there was an error. If it's non-blocking recv will return SOCKET_ERROR and a call to WSAGetLastError will return WASWOULDBLOCK
-
I'm using blocking sockets, so what could I do?
-
You can use the ioctlsocket function with the FIONREAD argument to get the amount of data waiting to be received and only call recv if their is some data waiting. This isn't the amount of data the server is sending it's how much has been received so far.
-
Could somebody have a loot at this (a tad long ~140 lines). As far as I can tell the use of ioctlsocket as a counter is making the receival lag behind... or something.
Code:
#include "stdafx.h"
using namespace std;
#pragma warning (disable :4267) // conversion from 'size_t' to 'unsigned int', possible loss of data
// contains connection information to be used elsewhere
// after server_connect() call
typedef struct {
LPHOSTENT host_info;
SOCKADDR_IN serv_info;
SOCKET sock;
} serv_host_t;
// server_connect
//
// returns: 0 on error, filled out serv_host_t otherwise
serv_host_t *server_connect (char *name)
{
WORD sock_ver;
WSADATA wsa_data;
int ret;
LPHOSTENT host;
SOCKET sock;
SOCKADDR_IN server_info;
serv_host_t *sh_info = new serv_host_t;
sock_ver = MAKEWORD (1, 1);
WSAStartup (sock_ver, &wsa_data);
host = gethostbyname (name);
if (! host) {
char err[128];
memset (err, 0, 128);
sprintf (err, "Could not resolve host name '%s'\n", name);
cout << "\nERROR: " << err << "\n";
WSACleanup ();
return 0;
}
cout << "Connecting to " << name << "...\n";
sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
cout << "\nERROR: Could not create socket\n";
WSACleanup ();
return 0;
}
cout << "Created socket...\n";
server_info.sin_family = AF_INET;
server_info.sin_addr = *((LPIN_ADDR) *host->h_addr_list);
server_info.sin_port = htons (21);
ret = connect (sock, (LPSOCKADDR) &server_info, sizeof (sockaddr));
if (ret == SOCKET_ERROR) {
cout << "\nERROR: Could not connect to host\n";
WSACleanup ();
return 0;
}
cout << "Connection complete.\n";
sh_info->host_info = host;
sh_info->serv_info = server_info;
sh_info->sock = sock;
return sh_info;
}
// server_send
// Send data to the server
//
// returns: 0 if successfull, 1 otherwise
int server_send (char *data, SOCKET sock)
{
unsigned len = 0;
int ret;
char *c = data;
if (sock == INVALID_SOCKET) {
cout << "\nERROR: Invalid socket passed to server_send()\n";
return 1;
}
//cout << "Sending data...\n >> " << data << "\n";
// append \r\n\r\n
c += strlen (data);
*c++ = '\r';
*c++ = '\n';
*c++ = '\r';
*c++ = '\n';
len = strlen (data);
ret = send (sock, data, len, 0);
if (ret == SOCKET_ERROR) {
ret = WSAGetLastError ();
cout << "\nERROR: Unabled to send data to server [" << ret << "]\n";
return 1;
}
cout << "Sent.\n";
return 0;
}
// server_recv
// Receive data from the server
//
// returns: the data received
char *server_recv (SOCKET sock)
{
unsigned long len = 0;
unsigned i = 0;
vector<char> v;
char ch = 0;
int ret;
char *buffer = 0;
ioctlsocket (sock, FIONREAD, &len);
for (; i < len; i++) {
ret = recv (sock, &ch, 1, 0);
if (ret == SOCKET_ERROR) {
ret = WSAGetLastError ();
cout << "\nERROR: Socket error [" << ret << "]\n";
return 0;
} else if (ret == 0) {
cout << "\nERROR: Connection 'gracefully closed'\n";
return 0;
}
v.push_back (ch);
}
v.push_back ('\0');
buffer = new char[v.size ()];
for (i = 0; i < v.size (); i++) buffer[i] = v[i];
return buffer;
}
int main (int argc, char *argv[])
{
serv_host_t *sh_info = 0;
char *serv_name = new char[256];
char *command = new char[64];
int ret;
char *received = 0;
memset (serv_name, 0, 256);
memset (command, 0, 64);
cout << "Basic FTP Client\n(C) Lee Thomas\n\n";
cout << "server name\n ] ";
cin.get (serv_name, 256);
sh_info = server_connect (serv_name);
cout << "\n";
sprintf (command, "USER ahlukalt");
server_send (command, sh_info->sock);
received = server_recv (sh_info->sock);
cout << received << "\n";
closesocket (sh_info->sock);
WSACleanup ();
return 0;
}
-
When you say lag behind I'm assuming you mean you don't get all the data. Thats likely because ioctlsocket returns the amount of data that is available when it is called, by the time that data is recieved there could be some more waiting. The next time you call ioctlsocket it will report the next lot of data.
What you need to do is keep recieving data untill you reach a deliminator (\r\n or whatever it is) and then deal with that line (display it, take action, etc)
the program logic would be like this
Code:
while(connected)
{
getuserinput
checkfordata
if you have a complete line display it, take some action
}
you are probably safe assuming that no line will be longer then a given length say 1024 bytes so you could just store bytes in a buffer untill you get a complete line then deal with it.
-
I send a 'packet', a self defined structure.
The packet contains the size of the following/attached data and details for processing it ie type of data.
Can send an int first and then keep recv() until you get that much data.
ie
recv() int (4 bytes) == size
alloc buffer
recv() size
process
free buffer
recv() int (4 bytes) == size
ect