Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

ServerSocket issue

Moderators: kris

Posted: 04/21/08 10:34:23

Hi, I am using ThreadPool? to handle incoming connections to a server, and have been fighting with a bug in ServerSocket?.accept and SocketConduit?.detach for while which causes my server to crash when accepting a connection or sometimes when the handler I'm using is freed, finally think I have found the problem, as I haven't been able to crash the server after my changes. My handler class was deleting the SocketConduit? that the ServerSocket? had created, causing the accept function to segfault, I believe due to SocketConduit? freelist. There needs to be some documentation of that in the SocketConduit? Docs I think. Also when The handler closes it closes any connections it has opened, this was sometimes causing a segfault in SocketConduit?.detach, I fixed it by just not calling SocketConduit?.close.

What is the correct way to use SocketConduit? and ServerSocket??

Am I causing memory leeks by not calling close, (I would like to call close, because the remote end will not allow me to connect if the number of connections gets to high)

Because I don't close the sockets the seem to be staying in CLOSE_WAIT state, is this a Tango bug, or am I doing some thing wrong?

-Rory

Author Message

Posted: 04/21/08 17:41:56

You can't delete a socketConduit given to you by accept(), since you don't own it. Unfortunately, D doesn't provide a way to handle this very well, thought the doc should be certainly be better. Would you like to create a patch with better documentation?

I'll get back to you regarding close/detach ...

Posted: 04/21/08 17:57:04

Hi Kris, thanks for the reply.

I got it working, so I can close the sockets, I used :

conduit.socket.detach();

which doesn't ever seem to be called if the thread exists due to an exception so I've had to make all sorts of ugly handlers, wouldn't it be better if there was a destructor that closed the sockets that are no longer being used?

I will try to put some time into the Doc.

-Rory

Posted: 04/21/08 19:13:52

sorry, premature victory, I have not killed my bug yet.

This is where I am now getting the SIGSEGV, in Connection.close(). I have attached my wrapper class. Please excuse the hackish use of Log.getLogger. I am using it as a way to log stuff in my libraries, and then if I want more info I just setup a appender in the main program. I create a Connection instance in one class and then create a class which runs on the Connection and then closes it, before the thread exits it runs Connection.close on all Connections it has opened.

/**
	Stores information about a TCP/IP Connection
**/
class Connection {
	char[] protoname;	// the name of the protocol used on this connecion
	IPv4Address localAddress;		// the local network address and port to use or being used for this connection
	IPv4Address remoteAddress;		// what network address to connect to
	SocketConduit conduit;
	private bool established_;	// is this connection established?
	bool established() {
		return established_;
	}
	char[] domain; // the domain that this has been established to
	char[][] domains; // the domains that were used to get this connection, because of the way MX records work
	
	
	this(char[] domain) {
		this.domain = domain;
	}
	/* ~this() {
		this.close();
	} */
	/**
		get the MX records for 'domain', will use A records if not found
	**/
	Connection getMX() {
		try {
			//domains = DNS.getMX(domain);
			auto res = DNS.Record(domain, DNS.T_MX);
			if (res.records.length > 0) {
				foreach (record; res.records) {
					domains ~= record.result;
				}
			} else
				domains ~= domain;
		} catch (DNSException e) {
			domains ~= domain; // just use the domain
		}
		
		return this;
	}
	
	Connection connect() {
		auto ret = new SocketConduit();
		if (remoteAddress is null) { // there is no remoteAddress override present
			foreach (domain; domains) {
				try {
					if (localAddress !is null) { // there is a localAddress override present use it to connect through
						ret.bind(localAddress);
						ret.connect(new InternetAddress(domain, 25));
					} else {
						ret.connect(new InternetAddress(domain, 25));
					}
					if (ret.isAlive) {
						if (localAddress is localAddress.init) localAddress = cast(IPv4Address)ret.socket.localAddress;
						remoteAddress = cast(IPv4Address)ret.socket.remoteAddress;
						conduit = ret;
						established_ = true;
						return this; // success
					}
				} catch (AddressException e) {
					Log.getLogger("Connections").warn("Caught AddressException: "~ e.toString);
					continue;
				}
			}
		} else { // there is a remote address override so don't do any lookups
			Log.getLogger("Connections").info("Using remoteAddress override for Connection to "~ domain);
			if (localAddress !is null) { // there is a localAddress override present use it to connect through
				ret.bind(localAddress);
				ret.connect(remoteAddress);
			} else {
				ret.connect(remoteAddress);
			}
			conduit = ret;
			established_ = true;
			return this; // success
		}
		throw new ConnectionException("Unable to connect to recipient server");
	}
	
	void close() {
		synchronized {
			Log.getLogger("Connections").info("Closing Connection");
			if (established_ || (conduit != conduit.init && conduit.isAlive)) {
				conduit.socket.detach();
				conduit = conduit.init;
				established_ = false;
			}
		}
	}
}

Posted: 04/29/08 20:32:51

Okay, that was embaressing I had fixed the "socket close" crash by using "conduit.socket.detach();" instead of "conduit.detach()" the bug in the above code is because of the comparison "conduit != conduit.init". changed it to:

(conduit !is null && (conduit != conduit.init && conduit.isAlive))

I have put this note here just for completeness.

-Rory