Exiting blocked threads (socket.accept)

Tim tim at unknownMailAddress.com
Thu Mar 28 12:33:18 PDT 2013


On Thursday, 28 March 2013 at 17:57:47 UTC, Tim wrote:
> 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.

I should better double check the documentation... the reason for 
the problem is the if-then-elseif... I've changed this to:

if (ss.isSet(pSocket)) {
    // ... do stuff here
}

if (ss.isSet(readSock)) {
    break mainloop;
}

As I already mentioned... thanks to Sean, Ali and Martin!


More information about the Digitalmars-d-learn mailing list