Skip to content

A non-Blocking Telnet Server for Character Mode

February 19, 2016

So I have a framework now that accommodates multiple simultaneous socket connections that can either be browsers on port 80 or telnet on port 23. I can easily run a doughnuts game for a single telnet user but that’s at the expense of implementing a blocking read so the server effectively spins its wheels waiting for input and any other users would be locked out. I had visions of making a state machine version of the game that would be dispatched every time a line was received and pick up where it left off – somewhat what the web server does. The latest wrinkle is that telnet servers commonly operate in character mode so I would need to accumulate the lines myself maintaining an accumulating line buffer for each socket. Add to that the need to deal with three-byte negotiation sequences and timeouts and it just seems a bit much.

Sometimes when I start to code something I think “hell yes, this will work”. This is not one of those times!

So then I had another idea: The W5500 is already maintaining an accessible buffer for each active socket. My program only gets tricky when i’m trying to accumulate from those buffers and build a line in a separate socket buffers. So…don’t do that! I figure I can look at the buffer in the wiznet, throw it away if it’s negotiation sequences, but otherwise leave it alone until i have a complete line. It’s a lot of extra data shuffling but the cpu is spinning its wheels anyway.

I coded this up with no game play on the telnet side, just a series of simple queries and responses. I only ran it with a single browser session and a single telnet session but it seems to be fine. the handleTelnetSession() gets called every pass through the main loop if the socket is in session. It calls the welcomeTelnet() routine the first time through and calls processTelnet() if there’s data in the receive buffer.

processTelnet() gets called any time there’s data available. It looks at the buffer to see if it starts with 0xFF meaning a negotiation sequence – if it is, it flushes the buffer. If not, it slurps in the whole buffer contents and checks to see if there’s a carriage return in it – if not, it returns without disturbing the buffer; the routine could get called dozens or hundreds of times between keystrokes, pull the same data in from the wiznet, and fail the CR search, requiring more passes. If there is a CR, it falls into a sequence of simple tests and responses including quitting the server, telling a joke, or displaying a table of gamer IP addresses – those responses all include flushing the buffer so processTelnet won’t be called again until there’s something to do. There’s no entry for the game there but when there is, I’ll just keep a flag that the socket is playing the game and interpret the input accordingly – easy peasy with the input issue dealt with.

void handleTelnetSession(unsigned char sock){	//handle a telnet session 
	if (sockPrevStat[sock]==SOCK_LISTEN){// first entry
		welcomeTelnet(sock);
		sockPrevStat[sock]=SOCK_ESTABLISHED;
	}
	if (wizGetCtl16(SnRX_RSR,sock)){//check for data available
		processTelnet(sock);
	}
}
void welcomeTelnet(unsigned char sock){
	sendConst("Oh Hello\r\n",sock);
	printf("S %d telnet session begins\n",sock);
}

void processTelnet(unsigned char sock){
	unsigned int rsize=wizGetCtl16(SnRX_RSR,sock);
	recv(buf,min(MAX_BUF-1,rsize),sock);//read what's there
	buf[rsize]=0;//make sure it's terminated
	printf("%d %02X %02X %02X\n",rsize,buf[0],buf[1],buf[2]);
	if (buf[0]==0xff){//looks like negotiation sequence
		flush(rsize,sock); //dump it
		return;
	}
	if (!strchr(buf,0x0d)){//if no cr
		return;//just keep trucking
	}
	if (buf[0]=='q'){//quit
		sendConst("\n\rbye\n\r",sock);
		printf("disconnecting socket %d\n",sock);
		flush(rsize,sock);
		wizCmd(CR_DISCON,sock);// Disconnect the connection- we're done here
		sockPrevStat[sock]=SOCK_CLOSED;
		return;
	}
	if(buf[0]=='j'){
		sendConst("An 1802 walks into a bar..\n\r",sock);
		sendConst(">",sock);
		flush(rsize,sock);
		return;
	}
	if(buf[0]=='t'){
		tSendTable(sock);
		sendConst(">",sock);
		flush(rsize,sock);
		return;
	}
	sendConst("?\r\n>",sock);
	flush(rsize,sock);
	return;
}

xx

Advertisements

From → telnet, web server

2 Comments
  1. Matthew Millman permalink

    Good lord. I implemented a telnet server on an 8-bit microcontroller a number of years ago, in the end it ended up being easier to port a small RTOS to it which had cooperative multi threading, that made things a whole lot easier.

  2. yeah, In my defense I got dragged down this rathole a bit at a time: first I thought telnet would improve gameplay and simplify my code; then i needed multi-socket support; then it turns out most telnet clients are character based; and so on. Now I can see zawinski’s law peeking over the horizon http://www.catb.org/jargon/html/Z/Zawinskis-Law.html

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: