Thread overview
Sockets won't block
Aug 31, 2006
%u
Aug 31, 2006
Graeme Defty
Aug 31, 2006
Regan Heath
Re: Sockets won't block - thank you
Sep 02, 2006
Graeme Defty
Sep 03, 2006
Regan Heath
August 31, 2006
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
August 31, 2006
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
August 31, 2006
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

September 02, 2006
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:
     mySock.blocking = true;
> 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:
     writefln("Sent (", len , ") '", buf , "' and received '", rcvbuf , "'.");
> 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
September 03, 2006
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