Where to begin? The beginning, I suppose.
I'm new to socket programming. I wanted to give it a shot. After research, I found that it seems that there is no standard C++ oop socket library (there were some third party libraries that might have been oop, but I decided to make my own anyway because, well, it's more fun that way since it's just a personal project anyway), so I decided to make my own.
I decided that my new library would be integrated with iostream. Sure, this probably isn't a good idea usually, but I figured 1) I had a decent reason to and 2) it was just as an academic exercise anyway. It couldn't hurt to at least learn how to do it.
Thus, I did a little digging and came up with this design:
class NetworkStream : public std::iostream {
public:
// class network streambuff
// should handle the actual writing
class NetworkBuff : public std::streambuf {
private:
SOCKET socket; // I'm working with winsock2, btw
public:
NetworkBuff(SOCKET s);
protected:
// out
virtual std::streamsize xsputn(const char* buffer, std::streamsize size);
virtual std::streamsize overflow(char buffer);
virtual int underflow();
virtual std::streamsize xsgetn(char* buffer, std::streamsize size);
};
// constructors
NetworkStream(SOCKET socket); // sets up the socket in NetworkBuff
virtual ~NetworkStream();
}; // end network stream
This NetworkStream object is supposed to handle the actual reading and writing on the network. The connection is maintained by higher level code (and by that, I mean an object that inherits from NetworkStream). That code is working (as far as I can tell, anyway) and is irrelevant to my question, so I've omitted it. I'm just mentioning it so you understand my design.
Anyway, the actual implementation for my new stream object looks something like this:
// class network streambuff
// should handle the actual writing
//constructor
NetworkStream::NetworkBuff::NetworkBuff(SOCKET s) {
socket = s;
}
// out
std::streamsize NetworkStream::NetworkBuff::xsputn(const char* buffer, std::streamsize size) {
// well, let's send the data
int result = send(socket,buffer,static_cast<int>(size),0);
// if that didn't work, throw an error
if(result == SOCKET_ERROR) throw("Send Failure: " + WSAGetLastError());
// NOTE: I realized after I wrote this that this throw may be useless,
// since I think iostream catches any errors thrown at this level, but
// just in case
// and pass through to streambuff, because, well, it can't hurt
return std::streambuf::xsputn(buffer, size);
}
// basically do the same thing as before...
std::streamsize NetworkStream::NetworkBuff::overflow(char buffer) {
// well, let's send the data
int result = send(socket,buffer,sizeof(buffer),0);
// if that didn't work, throw an error
if(result == SOCKET_ERROR) throw("Send Failure: " + WSAGetLastError());
// and pass through to streambuff, because, well, it can't hurt
return std::streambuf::overflow(buffer);
}
As far as I can tell, this works like a charm. I step through my debugger, and my xsputn()
gets called. So I can do something like this:
std::string data = "Hello, world!";
networkstream << data;
And it gets called. I think it sent successfully. It doesn't stepping through my debugger, result doesn't have an error. However, I haven't tested it fully yet because it's my receiving function that doesn't work:
std::streamsize NetworkStream::NetworkBuff::xsgetn(char* buffer, std::streamsize size) {
// well, let's read the data
int result = recv(socket,buffer,static_cast<int>(size),0);
// if that didn't work, throw an error
if(result == SOCKET_ERROR) throw("Receive Failure: " + WSAGetLastError());
// Now this I think is wrong, specifically comparing against SOCKET_ERROR.
// That's not my problem, though. My problem is that this function seems to
// never get called, so a wrong comparison doesn't matter yet anyway
// and pass through to streambuff, because, well, it can't hurt
return std::streambuf::xsgetn(buffer, size);
}
// Now this guy, I'm pretty extra sure is probably wrong, but it's what I got. I
// couldn't find a good example of using underflow, so I did my best from the
// research I did find
int NetworkStream::NetworkBuff::underflow() {
// well, let's read the data
int result = recv(socket,gptr(),sizeof(*gptr()),0);
// if that didn't work, throw an error
if(result == SOCKET_ERROR) throw("Recieve Failure: " + WSAGetLastError());
// and pass through to streambuff, because, well, it can't hurt
return std::streambuf::underflow();
}
It compiles real nicely. However, when I try to use it:
std::string data = "";
networkstream >> data; // should wait for data on the network
It seems to pretend nothing happened. No, actually, according to my debugger, it seems to set a fail bit, ignore my virtually overloaded functions, and continue execution like nothing happened.
So, my question, in one paragraph, is: what exactly is wrong with my underflow/xsgetn function that is causing it to fail and essentially ignore my code? I'm sure I'm doing something wrong, but what exactly?