• Please review our updated Terms and Rules here

TCP/IP for DOS - Looking for C programmers

mbbrutman

Associate Cat Herder
Staff member
Joined
May 3, 2003
Messages
6,420
I haven't posted about this in ages because I haven't worked on it in ages, but it's probably time to look for some help.

I've written a TCP/IP library using Borland C/C++ 3.0 for DOS. It has the following features:

  • Runs on any 8088 or better (PCjrs, XTs, 386s, etc.)
  • Interfaces with a packet driver; use any Ethernet card that has a packet driver
  • Full ARP support
  • IP Gateway
  • DNS resolver (just added recently)
  • TCP Sockets and UDP packets
  • High performance .. seriously high performance
  • Extreme tracing capability for all levels of debug

I've written some test apps for it:

  • 'netcat' - send and receive data from STDIN/STDOUT to any machine on the internet using TCP/IP sockets. (I use this now as a poor man's FTP)
  • 'mini bbs' - some code that allows multiple users to make incoming connections and play around in a crude menu system. It doesn't speak the telnet protocol yet, but it is a good demonstration of multiple sockets and it also shows the code can run for days at a time without crashing. (Some of you have helped me test this in the past.)
  • 'dns resolver' - just finds IP addresses for hostnames .. nothing fancy, but the coding was fairly complex.

(I'm willing to make netcat and the dns resolver available for download just in case anybody wants to play. Netcat might be useful, but the dns resolver is more of a toy.)

The closest thing one can compare this library too is WATTCP. WATTCP is far more mature, but Erick has been working on it much longer. (We traded some emails recently, and it looks like I managed to rediscover a lot of the lessons he learned 20 years ago.) A few years earlier I might have just started with WATTCP and built more apps, but building the library from scratch has been a great experience and if there are any bugs I know I can fix them because I have the source code. ;-0

What I'm looking for is help in writing the next level of applications and feedback on the APIs I've written so far. I think that what I've written is usable, but other professional programmers might think otherwise. ;-0

Requirements: Some free time, a strong knowledge of C/C++, knowledge of TCP/IP and DOS.

If I get help things go faster. Eventually I can write my own FTP server, telnet client, telnet BBS, etc., but some help in getting the library mature would be great and I can't boil the ocean myself. Part of the mission of the project is to bring the joy of programming and TCP/IP on DOS to other people. The eventual goal is a telnet BBS running directly off of a PCjr. I'll probably start with an IRC server instead, as that's a much easier APP to write.

Heck, I'll even write some documentation before I throw the library and header files at you! :)
 
very nice. as you know, i'm a lowly BASIC programmer so i'm not really going to be able to help you. i still wouldn't mind looking at the source.

back in like mid 2006 i used WATTCP for some TCP routines, but for some reason i was never able to find any real detailed info on it so i wasn't able to do a few things i wanted like open a socket for incoming connections. pretty soon i gave up, started a new .BAS from scratch and switched to TCPDRV because i found a nice big reference page for it.

there is still one problem i seem to have when using the my TCPDRV API routines.. if i try to write any sort of multi-connection server with it, it will take a connection if somebody connects to it but if another person tries to connect while the first guy is still on, it usually kicks the other person off. i'm not sure if it's a bug in TCPDRV (i doubt it is) or if it's something in my code. i've looked through the code over and over again to find maybe a mistake somewhere, i've experimented and i just can't find the problem.

that's actually why i never put out another update for my webserv prog. the whole thing just drives me nuts, and i've pretty much given up. i should probably just sit down and rewrite it all from scratch and see what happens. i've also been thinking about maybe just interfacing with the packet driver only and make the whole TCP stack myself. i don't know if i want to mess with that or not, it's probably more trouble than it's worth.

at one point, i started writing an IRC server with my routines. somehow i was able to mostly get around that multi-connection problem, i didn't really do anything different so i'm not sure what it was... but it still occasionally dropped connections.

atari2600a connected to it, and so did ahm. the three of us were on it at the same time chatting, and it seemed to be working okay. i really wish i knew what was causing that problem.

maybe if i got your source for the TCP stack you made, it'd motivate me enough to sit down and really start learning C. it can't be THAT hard. i'd just have to try to forget everything i know about BASIC.

so does your code work like WATTCP and TCPDRV, in the sense that it can load as a TSR, and have other software use it with interrupt calls?

Code:
DO
   IF Motivation% > 0 THEN EXIT DO ELSE GOSUB GiveUp
LOOP
 
One of the reasons I never bothered writing an application for Trumpet (TCPDRV) is that without support from the author and without source code there would be no way to get around bugs. When you use somebody else's code you are also subject to their design decisions, which may or may not suit your needs.

In my case I am targetting some very old machines ... speed and memory are important, but not as important as they would be on a C64 or Apple ][. There is a really small TCP/IP stack that is the basis of Contiki and some other projects which is very small, but very very minimal and very low performance. A PC or PCjr has enough memory to make some performance tradeoffs in favor of performance.

For example:

  • Multiple input buffers for incoming data from the packet driver (user adjustable)
  • ARP caching
  • A fairly large receive buffer for incoming socket data (user defined)
  • Automatic retransmission of outgoing TCP/IP packets (optional)

A lot of the library can be controlled by compile time options. For a TCP application I can strip out the UDP support and save some space. (Or vice-versa - I can have UDP support only.) I can also strip out all of the tracing code or adjust other parameters to suit the application.

My code definitely handles multiple connections .. I've seen up to 5 at a time. The last time I did an extensive test it stayed running for 3 days or so with connections coming and going.

I'll post some source code here a little later to give you a flavor for what it looks like.


Mike
 
Here is an example of a simple program loop. (The program lets you connect to another machine, or listen for a connection from another machine, and then once the connection is established you can type back and forth at each other.)

Code:
  uint8_t done = 0;
  uint8_t remoteDone = 0;

  while ( !done && !remoteDone ) { 

    PACKET_PROCESS_SINGLE;
    Arp::driveArp( );
    Tcp::drivePackets( );

    // Process incoming data first.   
    if ( remoteDone == 0 ) {

      uint16_t recvRc = mySocket->recv( incomingData, MY_RCV_BUF_SIZE );
      if ( recvRc ) {
        TotalBytesReceived += recvRc;
        write( 1, incomingData, recvRc );
      }
  
      remoteDone = mySocket->isRemoteClosed( );
    }
    
    if ( kbhit( ) ) {

      char c = getche( );
      if ( c == 26 ) { // The user hit Ctrl-Z - time to end
        done = 1;
        mySocket->shutdown( TCP_SHUT_WR );
        break; 
      }
    
      int rc = mySocket->send( &c, 1 );
      if ( rc < 0 ) {
        fprintf( stderr, "\nTcp: Send failed\n" );
      }
    
    }
  }
 
  mySocket->close( );
  TcpSocket::freeSocket( mySocket );

And here is the code that either connects to the foreign system (active open) or listens for an incoming connection (passive open):

Code:
  TcpSocket *mySocket;

  int8_t rc;
  if ( Listening == 0 ) {
    mySocket = TcpSocket::getSocket( );
    mySocket->setRecvBuffer( MY_RCV_BUF_SIZE );
    rc = mySocket->connect( SrcPort, serverAddr, serverPort );
  } 
  else {
  
    TcpSocket *listeningSocket = TcpSocket::getSocket( );
    listeningSocket->listen( SrcPort, MY_RCV_BUF_SIZE );
    
    // Listen is non-blocking.  Need to wait
    while ( 1 ) {
      PACKET_PROCESS_SINGLE;
      Arp::driveArp( );
      Tcp::drivePackets( );

      if ( TcpSocket::getPendingAcceptCount( ) ) {
        mySocket = TcpSocket::accept( );
        listeningSocket->close( );
        TcpSocket::freeSocket( listeningSocket );
        rc = 0;
        break;
      }
    }
    
  }

  if ( rc != 0 ) {
    fprintf( stderr, "Socket open failed\n" );
    shutdown( -1 );
  }

Too scary? :-0

When you see:
Code:
PACKET_PROCESS_SINGLE;
Arp::driveArp( );
Tcp::drivePackets( );

You are looking at the 'busy' loop. Those three function calls check for incoming packets that the packet driver left for us, and make sure that ARP and TCP keep moving. You don't need to know anything about what they actually do - just call them once in a while and when you are sitting around in your code waiting for something to happen.

Trumpet TCP (TCPDRV) is a TSR that hooks the timer interrupt, so they do this for you automatically. I chose not to go the TSR route to keep thing simple. I might do it one day, but it will probably result in reduced performance.
 
Back
Top