www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Sockets won't block

reply %u <gdefty attglobal.net> writes:
When I open sockets they don't seem to like blocking, and return immediately
when I do a 'receive' (with 0 bytes of data of course)

The routine (client and server in 1) is as follows (excuse all the tracing
'writefln's and a few artifacts from the original C++ version :-)

---- code ----
// $Header: /home/CVS/atm/ATMsock_t.cpp,v 1.1 1980/04/16 23:53:02 graeme Exp $
// $Header: /home/CVS/atm/ATMsock_t.cpp,v 1.1 1980/04/16 23:53:02 graeme Exp $

// Author:	Graeme Defty
// $Date: 1980/04/16 23:53:02 $
/*
	This module runs tests on the ATM Socket class.
*/

static const char pgmid[] =
	"$Id: ATMsock_t.cpp,v 1.1 1980/04/16 23:53:02 graeme Exp $";

// $Source: /home/CVS/atm/ATMsock_t.cpp,v $ //

//#include <fstream>
//#include <signal.h>
//#include <unistd.h>
import std.c.time;
import std.stdio;
import std.string;
import std.socket;

//#include "ATMsock.h"

//#include "assure.h"

// ============ Don't think we need these any more ==========
//static void
//sig_h(int signo) {
//	cout << "Caught signal " << signo << endl;
//	if (signo == SIGINT) exit(0);
//}
// ========================================================

int
main (char[][] args) {

	int portnum = 1234;
	char [] buf;
	char [] rcvbuf;
	Socket	mySock;
	InternetAddress myAddr, farAddr;
	int len;
	int count;
//	struct sigaction sigact;
//	struct sigaction oldsigact;

// first handle the arguments . . .
	if (args.length>2) {
		portnum = atoi(args[2]);
	}
	writefln("Using port number " , portnum , ".");

	// === Signal handling seems to be unneccessary now ====

// then set up signal handling . . .
//	sigact.sa_handler	= sig_h;
//	sigemptyset (&sigact.sa_mask);
//	sigact.sa_flags	= 0;
//	for (len=1; len<48; len++) {
//		if (sigaction(len, &sigact, &oldsigact) == -1) {
//			perror ("Catching Signal");
//		} else {
//			cout << "Signal " << len << " set." << endl;
//		}
//	}

	if (args.length>1 && args[1] == "s") {

		writefln("Test begins for server . . .");

		myAddr = new InternetAddress("127.0.0.1", portnum);
		mySock = new Socket(AddressFamily.INET, SocketType.STREAM);
		writefln("Server created . . .");
		mySock.bind(myAddr);
		writefln(". . . and bound . . .");
		mySock.listen(5);
		writefln(". . . and listening.");

		while (true) {
			Socket otherSock = mySock.accept();
			assert(otherSock.isAlive);
			writefln("Accepted a connection . . .");
			writefln("Far address is ", otherSock.remoteAddress);

			otherSock.blocking = true;
			assert(otherSock.isAlive);
			sleep(1);

			len = otherSock.receive(buf);
			assert(len != Socket.ERROR);
			assert(otherSock.isAlive);
			writefln("Received data (", len , ") '", buf , "'.");

			otherSock.send(buf);
			assert(otherSock.isAlive);
			writefln("Sent that data . . .");
//			mySock.close;
//			writefln(". . . and closed the socket.");
		}

	} else {
		writefln("Test begins for client . . .");

		myAddr = new InternetAddress("127.0.0.1", portnum);
		farAddr = new InternetAddress("127.0.0.1", 1234);

		for (count = 0; count < 12 ; count++) {
			if (count % 1000 == 0) {writefln("Done " , count);}

			mySock = new Socket(AddressFamily.INET, SocketType.STREAM);
			assert(mySock.isAlive);
			writefln("Client created . . .");

			mySock.bind(myAddr);
			assert(mySock.isAlive);
			writefln(". . . and bound . . .");

			mySock.connect(farAddr);
			assert(mySock.isAlive);
			writefln(". . . and connected.");

			writefln("Far address is ", mySock.remoteAddress);

			buf = format("This is data line ", count);
			len = mySock.send(buf);
			assert(mySock.isAlive);
			writefln("Sent (", len , ") '", buf , "' and received '", rcvbuf , "'.");

			len = mySock.receive(rcvbuf);
			assert(mySock.isAlive);
//			assert (buf == rcvbuf, "Sent and received data different!");
			if (buf != rcvbuf) {
				writefln("ERROR: Sent '", buf , "' and received '", rcvbuf , "'.");
			}
			mySock.close();
			sleep(10);
		}
	}
	writefln("Test Done!");
	return 0;
}

---- end code ----

I would appreciate any guidance.

Thanks,

graeme
Aug 31 2006
next sibling parent Graeme Defty <gdefty attglobal.net> writes:
Sorry, guys,

I forgot to add that I am on Windows XP

The output is as follows:
=== Client:
C:>atmsock_tt c 4321
Using port number 4321.
Test begins for client . . .
Done 0
Client created . . .
. . . and bound . . .
. . . and connected.
Far address is 127.0.0.1:1234
Sent (19) 'This is data line 0' and received ''.
ERROR: Sent 'This is data line 0' and received ''.
Client created . . .
etc.
=== end

=== Server:
C:\Documents and Settings\Administrator\My Documents\Projects\atm.d>atmsock_tt s

Using port number 1234.
Test begins for server . . .
Server created . . .
. . . and bound . . .
. . . and listening.
Accepted a connection . . .
Far address is 127.0.0.1:4321
Received data (0) ''.
Sent that data . . .
=== end

Thanks again,

g
Aug 31 2006
prev sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 31 Aug 2006 16:37:02 +0000 (UTC), %u <gdefty attglobal.net> wrote:
 When I open sockets they don't seem to like blocking, and return  
 immediately
 when I do a 'receive' (with 0 bytes of data of course)

This is due to the receive function's shortcut, see std\socket.d: if(!buf.length) //return 0 and don't think the connection closed return 0; you're passing it a 0 length buffer. If you pass 0 for len to the 'recv' call it will return an error WSAEINVAL. The comment indicates a desire to avoid the error because people assume it means the socket has closed, which you could do, if you didn't check the actual error code perhaps. The solution. Your receive calls need to look something like this: buf.length = 100; len = otherSock.receive(buf); assert(len != Socket.ERROR); assert(otherSock.isAlive); buf.length = len; writefln("Received data (", buf.length , ") '", buf , "'."); In other words, allocate some space in buf, read data into buf, check for errors, set buf length to length of data read, log it to screen. I also made the client socket blocking with: mySock.blocking = true; after the connect call. Though, from memory sockets are blocking by default. One other thing I changed was your logging here: writefln("Sent (", len , ") '", buf , "' and received '", rcvbuf , "'."); I made this into 2 log lines, the 2nd _after_ the receive call :) FYI... the client does not need to call 'bind'. It's entirely optional for simple outgoing connections. If you do not call bind it will automatically bind the first random available local socket. The only time you need to call bind is when you want the remote connection to see you as a certain ip and/or port. Regan
 The routine (client and server in 1) is as follows (excuse all the  
 tracing
 'writefln's and a few artifacts from the original C++ version :-)

 ---- code ----
 // $Header: /home/CVS/atm/ATMsock_t.cpp,v 1.1 1980/04/16 23:53:02 graeme  
 Exp $
 // $Header: /home/CVS/atm/ATMsock_t.cpp,v 1.1 1980/04/16 23:53:02 graeme  
 Exp $

 // Author:	Graeme Defty
 // $Date: 1980/04/16 23:53:02 $
 /*
 	This module runs tests on the ATM Socket class.
 */

 static const char pgmid[] =
 	"$Id: ATMsock_t.cpp,v 1.1 1980/04/16 23:53:02 graeme Exp $";

 // $Source: /home/CVS/atm/ATMsock_t.cpp,v $ //

 //#include <fstream>
 //#include <signal.h>
 //#include <unistd.h>
 import std.c.time;
 import std.stdio;
 import std.string;
 import std.socket;

 //#include "ATMsock.h"

 //#include "assure.h"

 // ============ Don't think we need these any more ==========
 //static void
 //sig_h(int signo) {
 //	cout << "Caught signal " << signo << endl;
 //	if (signo == SIGINT) exit(0);
 //}
 // ========================================================

 int
 main (char[][] args) {

 	int portnum = 1234;
 	char [] buf;
 	char [] rcvbuf;
 	Socket	mySock;
 	InternetAddress myAddr, farAddr;
 	int len;
 	int count;
 //	struct sigaction sigact;
 //	struct sigaction oldsigact;

 // first handle the arguments . . .
 	if (args.length>2) {
 		portnum = atoi(args[2]);
 	}
 	writefln("Using port number " , portnum , ".");

 	// === Signal handling seems to be unneccessary now ====

 // then set up signal handling . . .
 //	sigact.sa_handler	= sig_h;
 //	sigemptyset (&sigact.sa_mask);
 //	sigact.sa_flags	= 0;
 //	for (len=1; len<48; len++) {
 //		if (sigaction(len, &sigact, &oldsigact) == -1) {
 //			perror ("Catching Signal");
 //		} else {
 //			cout << "Signal " << len << " set." << endl;
 //		}
 //	}

 	if (args.length>1 && args[1] == "s") {

 		writefln("Test begins for server . . .");

 		myAddr = new InternetAddress("127.0.0.1", portnum);
 		mySock = new Socket(AddressFamily.INET, SocketType.STREAM);
 		writefln("Server created . . .");
 		mySock.bind(myAddr);
 		writefln(". . . and bound . . .");
 		mySock.listen(5);
 		writefln(". . . and listening.");

 		while (true) {
 			Socket otherSock = mySock.accept();
 			assert(otherSock.isAlive);
 			writefln("Accepted a connection . . .");
 			writefln("Far address is ", otherSock.remoteAddress);

 			otherSock.blocking = true;
 			assert(otherSock.isAlive);
 			sleep(1);

 			len = otherSock.receive(buf);
 			assert(len != Socket.ERROR);
 			assert(otherSock.isAlive);
 			writefln("Received data (", len , ") '", buf , "'.");

 			otherSock.send(buf);
 			assert(otherSock.isAlive);
 			writefln("Sent that data . . .");
 //			mySock.close;
 //			writefln(". . . and closed the socket.");
 		}

 	} else {
 		writefln("Test begins for client . . .");

 		myAddr = new InternetAddress("127.0.0.1", portnum);
 		farAddr = new InternetAddress("127.0.0.1", 1234);

 		for (count = 0; count < 12 ; count++) {
 			if (count % 1000 == 0) {writefln("Done " , count);}

 			mySock = new Socket(AddressFamily.INET, SocketType.STREAM);
 			assert(mySock.isAlive);
 			writefln("Client created . . .");

 			mySock.bind(myAddr);
 			assert(mySock.isAlive);
 			writefln(". . . and bound . . .");

 			mySock.connect(farAddr);
 			assert(mySock.isAlive);
 			writefln(". . . and connected.");

 			writefln("Far address is ", mySock.remoteAddress);

 			buf = format("This is data line ", count);
 			len = mySock.send(buf);
 			assert(mySock.isAlive);
 			writefln("Sent (", len , ") '", buf , "' and received '", rcvbuf ,  
 "'.");

 			len = mySock.receive(rcvbuf);
 			assert(mySock.isAlive);
 //			assert (buf == rcvbuf, "Sent and received data different!");
 			if (buf != rcvbuf) {
 				writefln("ERROR: Sent '", buf , "' and received '", rcvbuf , "'.");
 			}
 			mySock.close();
 			sleep(10);
 		}
 	}
 	writefln("Test Done!");
 	return 0;
 }

 ---- end code ----

 I would appreciate any guidance.

 Thanks,

 graeme

Aug 31 2006
parent reply Graeme Defty <gdefty attglobal.net> writes:
Regan,

Can't thank you enough for the help. All is well now.

It is a bit disappointing that the 'wrapper' round the C call is so 'thin'. The
sad thing is, my original C++ code allocated a 1K buffer and did all the right
things. I changed it to be a dynamic array in the expectation that the buffer
would be expanded as necessary. Oh well. That'll teach me :-)

 I also made the client socket blocking with:

 after the connect call. Though, from memory sockets are blocking by
 default.

Yes, you are right. They are.
 One other thing I changed was your logging here:

 I made this into 2 log lines, the 2nd _after_ the receive call :)

Hmmm. Yes. <Sounds offstage of faces going red :-) >
 FYI... the client does not need to call 'bind'.

Yes. I think that was one of my 'stabs in the dark'. Anyway, Thanks again for your speedy and madly useful response. Cheers, graemeDeft
Sep 01 2006
parent "Regan Heath" <regan netwin.co.nz> writes:
On Sat, 2 Sep 2006 06:29:24 +0000 (UTC), Graeme Defty  
<gdefty attglobal.net> wrote:
 Can't thank you enough for the help. All is well now.

You're welcome.
 It is a bit disappointing that the 'wrapper' round the C call is so  
 'thin'. The sad thing is, my original C++ code allocated a 1K buffer and  
 did all the right things. I changed it to be a dynamic array in the  
 expectation that the buffer would be expanded as necessary. Oh well.  
 That'll teach me :-)

Have you seen/tried "std.socketstream"? It might be more to your liking. Regan
Sep 03 2006