Archived

This topic is now archived and is closed to further replies.

shadowstryker

problem with asynchronous sockets

Recommended Posts

I''ve been trying to write some code so that I can use asynchronous sockets for a computer game. The only problem is that with my "network test" code that I have written so far I cannot get it to work. Here is my code:
Cal::Cal() {
	// wrapper to include only if compiler is Win32 based
	#ifdef WIN32
		WSADATA wsd;
		sockStatus = WSAStartup(0x0202, &wsd);
		if (wsd.wVersion != 0x0202) // check if winsock is version 2
			std::cout << "Incorrect version of winsock detected!\n";
	#else
		//Linux definitions
	#endif
}//ctor


Cal::~Cal() {
	CloseHandle(&receiveHandler);
	disconnect();
}//dtor


bool Cal::sockBind(int p) {
	sockaddr_in sAddr;
	tSock = socket(AF_INET, SOCK_STREAM, 0); //define socket
	// check is socket created
	if (tSock == INVALID_SOCKET)
		return false; // bind failed
	//set first sizeof(sockaddr_in) bytes pointed by
	//serverAddr to 0
	memset(&sAddr, 0, sizeof(sockaddr_in));
	//
	sAddr.sin_family = AF_INET;
	//
	sAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	//set port to p
	sAddr.sin_port = htons(p);
	// attempt to peform binding to socket
	if (bind(tSock,(sockaddr*) &sAddr, sizeof(sockaddr)) == SOCKET_ERROR) {
		disconnect();
		return false; //bind failed
	}
	return true; // bind successfull
}//sockBind


int Cal::receive(char* retBuffer) {
	packHead pHead; // header of packet (Structure defined in packets.h)
	int rByte, wByte; // bytes received, bytes waiting

	// check if there is more to be read
	// (will equal 0 if read as far as it has written)
	if(sRecv.writeLoc - sRecv.readLoc) {
		wByte = sRecv.writeLoc - sRecv.readLoc; // number of bytes to read
		// check if bytes to read are not enough for receiving a header
		if(wByte < sizeof(pHead)) {
			return 0;
		}
		// copy sizeof(pHead) bytes from packet buffer of sRecv into
		// the memory location of pHead
		memcpy(&pHead, &sRecv.packBuffer[sRecv.readLoc],sizeof(pHead));
		// check if bytes waiting are sufficient
		if((wByte - sizeof(pHead)) < (unsigned) pHead.pLength)
			return 0;
		// copy sizof(pHead) bytes from pHead to memory location
		// retBuffer for later processing
		memcpy(retBuffer, &pHead, sizeof(pHead));
		// copy (length of packet) bytes from packet buffer in sRecv from
		// current (read position + header size) into return buffer
		memcpy(&retBuffer[sizeof(pHead)],
			&sRecv.packBuffer[sRecv.readLoc + sizeof(pHead)], pHead.pLength);
		// move read location to current position
		sRecv.readLoc += pHead.pLength + sizeof(pHead);
		// move read bytes counter to current position
		rByte = pHead.pLength + sizeof(pHead);
		// check if too much has been read
		if(sRecv.readLoc >= 64000) {
			sRecv.readLoc = 0;
		}
	}
	return rByte;	//return bytes read,
					//if 0 then none were waiting or not enough
}


int Cal::sendPacket(char* buffer, int bLen) {
	// return result of sending buffer of length bLen
	// via tSock with no flags set
	return send(tSock,buffer,bLen,0);
}


bool Cal::sConnect(char* ipAddress, int port) {
	sockaddr_in sAddr;
	LPHOSTENT lphost;
	memset(&sAddr,0,sizeof(sockaddr_in));

	sAddr.sin_family = AF_INET;
	sAddr.sin_addr.s_addr = inet_addr(ipAddress);

	if(sAddr.sin_addr.s_addr == INADDR_NONE) {
		lphost = gethostbyname(ipAddress);
		if (lphost != NULL) {
			sAddr.sin_addr.s_addr = ((LPIN_ADDR) lphost->h_addr_list)->s_addr;
		}
		else {
			#ifdef WIN32
			WSASetLastError(WSAEINVAL);
			#endif
			return false;
		}
	}
	sAddr.sin_port = htons(port);
	tSock = socket(AF_INET, SOCK_STREAM, 0);
	
	if(tSock == INVALID_SOCKET)
		return false;
	int err = connect(tSock, (struct sockaddr*)&sAddr, sizeof(sockaddr));
	if(err == SOCKET_ERROR) {
		disconnect();
		return false;
	}
	sRecv.transSocket = tSock;
	sRecv.packBuffer[0] = NULL;
	sRecv.readLoc = 0;
	sRecv.writeLoc = 0;
	sRecv.endRequest = 0;
	return true;
}


void Cal::disconnect() {
	//check if socket was connected
	if(tSock != INVALID_SOCKET) {
		closesocket(tSock); //close socket
		tSock = INVALID_SOCKET; //marker to show socket closed
	}
} 
tSock is a SOCKET variable that I have defined in the class definition. when trying to use the socket for a simple 2-way chat test I get junk returned as a packet when I haven''t actually sent anything to be received by the other peer. Any suggestions? (I''m new to socket programming)

Share this post


Link to post
Share on other sites
while "I can''t get it to work" isn''t particularly descriptive of what or where the problem is - I notice that there is never a call to recv() to actually get data from the socket... could that be the problem?

If not, say where the problem is and what error code you get...



"Absorb what is useful, reject what is useless, and add what is specifically your own." - Lee Jun Fan

Share this post


Link to post
Share on other sites
hi,

sorry I should have been more descriptive. basically I have created a class called Cal which holds all the functions for my connections and creates two sockets tSock and uSock, which are the server and client sockets respectively. Since posting my original post I noticed that I forgot to get the computer creating the session to listen for and accept a connection.

I don''t use receive as I didn''t want to use any blocking commands. These functions are called from my main method which I never listed as I''m certain the code in it isn''t causing the error.

I have noticed that using the listen(socket,backlog) always seems to return 0 for me (it thinks there''s an incoming socket) and so it tries to accept the connection and then fails - this happens even when there are no inbound connection requests.

(Note: The Class code has changed since it was posted, but has the same basic idea)


Due to the failed attempt at accepting the connection when it get''s to use receive() to examine the incoming packet(s) - which there should be none of - it gives a negative number as the bytes received and then prints garbage.

Share this post


Link to post
Share on other sites
The recv() function is only blocking if your socket is blocking. By using ioctlsocket you can make the socket non-blocking.

I personally much prefer non-blocking sockets as I find them easier to work with and I can implement my own timeouts etc.

As for listen() - it will return 0 if the socket is successfully put into a listening state. Returning 0 does not mean that it thinks there is an incoming connection pending. To find out if an incoming connection is pending you need to use accept or select.

If you are listening on a non-blocking socket you can use accept. It will return INVALID_SOCKET (-1) and the error will be WSAEWOULDBLOCK if there is no connection pending. If another value is returned then that is the socket handle of the incoming connection.



"Absorb what is useful, reject what is useless, and add what is specifically your own." - Lee Jun Fan

Share this post


Link to post
Share on other sites
my little chat prototype is now working

except there''s one little snag - it won''t work between computers behind routers, though it works on local network.

any suggestions on how to deal with this? I thought the router usually routes the connection on a port to a computer thats expecting something on that port.

Share this post


Link to post
Share on other sites
quote:
Original post by shadowstryker
my little chat prototype is now working


Congrats!

quote:

except there''s one little snag - it won''t work between computers behind routers, though it works on local network.


Ah, and now you''ve been initiated into the world of network programming

In dealing with routers and firewalls, there are a couple of rules that will help you out. They won''t work in 100% of all cases, but if you follow them, you can be confident you''ve pretty much done all you can to allow your clients to connect to your server (we are talking about a client/server setup, if I read your code right, yes?).

1) Server uses a fixed port. You need this so that the firewall behind which the server resides can forward unannounced traffic to the server (and you *better* have a firewall up between your server and the internet).
2) Client uses a dynamic port assignment, which you can do simply by specifying 0 as the port number. Winsock will select an available port for you. The server needs to pay attention to the port number along with the IP address for the purpose of tracking each client, but doing this will allow you to run the server and client on the same computer for debugging purposes, as well as work reasonably well with most NAT router implementations.
3) The client must send first.
4) The server must reply using the same socket as it received on, to the address and port the client used.
5) The server must treat server socket, client address, and client port as a single unit for client identification. (Or use a seperate server socket for each client.)
6) Because of 4 and 5, the client port is irrelevant, as it should be. In any case, with a NAT router inbetween, the port the server sees may be different to what the client thinks it is using (as with the address).
7) An idle "connection" _will_ stop working sooner or later. "Idle" may only mean idle in the client to server direction (outgoing, from the point of view of the router/firewall). To keep an open route from server to client, the client ought to send on that route no less than every couple of minutes, or you risk the router shutting it down.
8) The client MUST send first .

I didn''t make up that list, it was advice given to me when I was dealing with the same problem. By following it, I have a UDP server/client setup that works perfectly, and since I upgraded my networking architecture, I''ve had exactly *zero* reports of people unable to connect, even behind hefty firewall and router equipment.

HTH, and good luck with your project.

Ron

Share this post


Link to post
Share on other sites