Exiting blocked threads (socket.accept)

Tim tim at unknownMailAddress.com
Thu Mar 28 10:57:45 PDT 2013


On Thursday, 28 March 2013 at 12:28:05 UTC, Martin Drasar wrote:
> On 28.3.2013 11:23, Tim wrote:
>> Thanks Martin and Ali. Your solution works as long as I use the
>> receive()-method, but what about using SocketStreams? I 
>> replaced
>> socket.receive() with socketStream.readLine() which isn't 
>> broken by the
>> solution above...
>
> If you check the documentation, you will see that the 
> SocketStream is a
> stream for blocking socket. You can't easily make it work with
> nonblocking select() calls.
>
> However, if you want to use the stream interface, you can write 
> the
> non-blocking stream yourself. If you check the SocketStream at 
> github
> (https://github.com/D-Programming-Language/phobos/blob/master/std/socketstream.d),
> you will see that it is pretty small and easy. You can transfer 
> the
> interrupt code inside the readBlock() method.
>
> However, it will probably be for the best to just write your 
> own method
> to read for socket until the end of the line.
>
> Martin

Thanks for everything, but I'm still having some problems with 
this solution... I implemented a simple ftp server which looks as 
follows:

import std.socket;
import core.thread;
import core.stdc.signal;
import std.stdio;
import std.string;
import std.conv : to;

__gshared Socket readSock;
__gshared Socket writeSock;
__gshared bool stopServer = false;

class DataChannel {
	Socket datasocket;
	this() {
		datasocket = new TcpSocket();
		datasocket.bind(new InternetAddress(0));
		datasocket.listen(0);
	}

}

class Connection : Thread {
   private Socket pSocket;
   void run() {
int i = 0x00;
DataChannel datachannel;
     ptrdiff_t received;
     ubyte[0x20] buffer;

     SocketSet ss = new SocketSet();

	pSocket.send("220 FTP ready.\r\n");
datachannel = new DataChannel();
   mainloop:
     while(1) {
i++;
       ss.reset();
       ss.add(pSocket);
       ss.add(readSock);

       if (Socket.select(ss, null, null) > 0) {

         if (ss.isSet(pSocket))
         {
           received = pSocket.receive(buffer);

	if (i == 1)
	  pSocket.send("331 Password required for anyUser\r\n");
	else if (i == 2)
		pSocket.send("230 User anyUser logged in\r\n");
	else if (i == 3)
		pSocket.send("257 / is the current directory.\r\n");
	else if (i == 4)
		pSocket.send("215 UNIX Type: L8\r\n");
	else if (i == 5)
		pSocket.send("200 Type set to I\r\n");
	else if (i == 6) {
		string[] lip = split(pSocket.localAddress.toAddrString, ".");
		auto port = (cast(InternetAddress) 
datachannel.datasocket.localAddress).port;
		pSocket.send("227 Entering Passive Mode(" ~ lip[0x00] ~ "," ~ 
lip[0x01] ~ "," ~ lip[0x02] ~ "," ~ lip[0x03] ~ "," ~ 
to!(string)(port / 256) ~ "," ~ to!(string)(port % 256) ~ 
")\r\n");
		}
	else if (i == 7)
		pSocket.send("150 Here comes the directory listing.\r\n226 
Directory listing complete\r\n");
	else if (i == 8)
		pSocket.send("250 CWD command successful.\r\n");
	else if (i == 9)
		pSocket.send("257 / is the current directory.\r\n");
	}

           // process data
         }
         else if (ss.isSet(readSock))
         {
           writeln("Received interrupt");
           break mainloop;
         }
       }

     pSocket.close();
   }
   this(Socket s) {
     super(&run);
     pSocket = s;
   }
}

extern (C) void terminateServer(int s) nothrow {
   stopServer = true;
}

void main() {

   signal(SIGINT, &terminateServer);

   TcpSocket s = new TcpSocket();
   s.bind(new InternetAddress(2100));
   s.listen(0);

   auto pair = socketPair();

   readSock  = pair[0];
   writeSock = pair[1];

   SocketSet ss = new SocketSet();

   while (!stopServer)
   {
     ss.reset();
     ss.add(s);

     if (Socket.select(ss, null, null, dur!"msecs"(20)) > 0)
     {
       writeln("Received new connection");
       (new Connection(s.accept)).start();
     }
   }

   writeSock.send([1]);

   s.shutdown(SocketShutdown.BOTH);
   s.close();

   writeln("Finished");
}

I know... that's not only a dirty solution, but a terrible (and 
wrong) solution, but it's for demonstration purposes only. By 
connecting to this server using a ftp client (let's say FileZilla 
or gFTP), I can connect until I get an error (which comes because 
of the dirty solution... but that's not the point here). When I 
press CTRL+C to terminate the server, this doesn't work anymore. 
I don't know why (probably because of the datachannel? But there 
is no loop or something else in the datachannel-class - no 
accept() or similar)... I've a rfc959-based implemented version 
of the ftp-server, but the result is the same... pressing CTRL+C 
doesn't work in some cases.


More information about the Digitalmars-d-learn mailing list