Author Topic: Using TCP in the uTasker project  (Read 82122 times)

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3232
    • View Profile
    • uTasker
Using TCP in the uTasker project
« on: July 20, 2007, 10:18:51 PM »
Hi All

The uTasker demo project uses TCP for several services. These include HTTP, SMTP, FTP, TIME SERVER and TELNET, to name the ones which initially spring to mind.

The demo project enables these services to be easily studied and own services to be written (usually termed clients and servers) based on these example. There is, at the time of writing, no 'official' document which contains a definitive guide.

This is logically the reason why the use of TCP is a frequent question and frequent discussion topic in private mails. It is also a reason why this document, which is long overdue, should soon be made available.

Therefore this thread is designed to be an introduction to the subject to answer the first burning questions and to give first practical examples, as well as more detailed insight into how it works, how it can be used and why it all works like that. If all goes well, the thread comntent can be moved to a more structured document at a later point in time.

TCP is in fact a very simple protocol - in its simpest form. But in order to allow it to perform with the speed and reliability which has enabled it to become originally the standard in the Internet and eventually the standard in almost all networks it does need a few additional frills which can then make it quite complicated indeed.

If you would like to discuss standard TCP details please feel free to create your own topic to get a discussion going. I have always found it very difficult to find any really meaningful discussions about the practical aspects in the Internet so it would be nice to be able to find some good ones here!


A simple TCP server example. (The HTTP service is a good source for reference).

First we get a socket from the TCP socket pool. The size of the socket pool is defined in config.h, where the define USER_TCP_SOCKETS can be incremented for every additional socket which user code (as opposed to standard services like HTTP) will need.
The following configures a socket with the defined characteristic (minimum delay as opposed to maximum throughput or maximum reliability - these are router parameters), a timeout of 10 seconds (after this length of time with no activity it will close the connection. A value 0xffff means no timeout!), and a callback routine called fnTestListener() which will be doing the work later.

Code: [Select]
static USOCKET Test_socket = -1;                                          // declare a static socket variable
...
    Test_socket = fnGetTCP_Socket(TOS_MINIMISE_DELAY, (unsigned short)10, fnTestListener); // from task or sub-routine code

The socket is not in the listening state (which is needed for it to act as a server) so we start it in this state by calling
Code: [Select]
static unsigned short usTestPort = 0x9999;                               // declare a static socket variable (the value could also be fixed)
....
    fnTCP_Listen(test_socket, usTestPort, 0);                             // after getting the socket, bind it to a listening port

Now the socket is listening on port 0x9999, which means if you have a test client (eg. Telnet on this port) send a connection request to it it will start a connection process.
Note that the parameter 0 when setting the listening state is the maximum windows size which the connection can receive - a zero defaults to the standard LAN TCP pay load size of 1460 bytes. For some applications with special buffer restrictions this can be used to improve flow control in the rx sense (CONTROL_WINDOW_SIZE needs to be enabled in config.h) but this won't be described here just yet.

The simple server listener looks something like this.

Code: [Select]
#define TEST_BUFFER_LENGTH 10
typedef struct stTCP_MESSAGE
{
    TCP_HEADER     tTCP_Header;     // reserve header space
    unsigned char   ucTCP_Message[TEST_BUFFER_LENGTH];
} TCP_MESSAGE;

static int fnTestListener(USOCKET Socket, unsigned char ucEvent, unsigned char *ucIp_Data, unsigned short usPortLen) {
   TCP_MESSAGE test_message;

    switch (ucEvent) {
    case TCP_EVENT_CONREQ:                                             
    case TCP_EVENT_CONNECTED:
    case TCP_EVENT_CLOSE:
    case TCP_EVENT_ACK:
    case TCP_EVENT_ARP_RESOLUTION_FAILED:
    case TCP_EVENT_PARTIAL_ACK:
        break;
    case TCP_EVENT_REGENERATE:
    case TCP_EVENT_DATA:
        if ((ucEvent == TCP_EVENT_REGENERATE) || (!uMemcmp((CHAR*)ucIp_Data, "TEST" , 4))) {
            uStrcpy((CHAR*)test_message.ucTCP_Message, "Hi!!");
            if (fnSendTCP(Socket, (unsigned char *)&test_message.tTCP_Header, 4, TCP_FLAG_PUSH) > 0) {
                return APP_SENT_DATA;
            }
        }
        break;
    case TCP_EVENT_ABORT:
    case TCP_EVENT_CLOSED:
        fnTCP_Listen(Socket, ++usTestPort, 0);                    // go back to listening state on next port number
        break;
    }
    return APP_ACCEPT;
}

As you can see it receives events from the TCP layer, some with other details such as a pointer to data.

This is a list of the events which can arrive:

  • TCP_EVENT_ABORT               TCP connection was closed (due to error or a reset from peer)
  • TCP_EVENT_CONNECTED        TCP connection has been successfully established
  • TCP_EVENT_ACK                   Data which we sent have been successfully acknowledged (for all sent data, with outstanding acks)
  • TCP_EVENT_DATA                 Data frame received with data content for us to treat
  • TCP_EVENT_CLOSE                Peer is starting a close of the present TCP connection
  • TCP_EVENT_CLOSED              The TCP connection has been fully closed
  • TCP_EVENT_REGENERATE       The last data frame was lost and must be resent!!!
  • TCP_EVENT_CONREQ              A peer wants to establish a TCP connection (SYN received)
  • TCP_EVENT_ARP_RESOLUTION_FAILED  Generally only when working as client and sending a connection request or data after a longer period of no activity. It means that the the SYN or the data could not be sent by the IP layer since the address of the destination could not be resolved (destination powered down or network problem, or just doesn’t exist). In this cause the application is informed that the connection is either not possible of the connection has broken down due to this.
  • TCP_EVENT_PARTIAL_ACK       This is only used when Windowing is in operation. Presently only TELNET uses it. It means that an ACK has been received for a part of the data which has been sent (rather that all data that has been sent). This is used to acknowledge that only a part of the data has been successfully delivered rather than all outstanding data. This has to be treated differently to a normal ACK event since the ack number has to be used to calculate which outstanding data acks are still open and expect their own ack later.

Now if you are new to TCP, enter in the above test program - best in the simulator - and you can see your first simple TCP server in operation. It won't do a lot but it will at least do the following.

1. When the client requests a connection to port 0x9999 the event TCP_EVENT_CONREQ will be received. Here it would be possible to deny the connection to a black-listed IP for example but our server always allows connections.
2. The TCP connection will be established and the event TCP_EVENT_CONNECTED is received.
3. If the client sends any data it will arrive with the event TCP_EVENT_DATA. The server will only respond to the message "TEST" in which case it will send the string "Hi!!" as answer.
4. After 10s with no activity, the server will close the connection (it is actually TCP that does this after the idle timer fires but the server receives the event TCP_EVENT_CLOSED when the connection has successfully closed).
5. The lister socket is not active after the connection has been closed so the code sets it up as listener again, this time with port number 0x999a.
6. If the connection is repeated, the port number is always incremented each time and the connection always times out after 10s.

Note that when the server sends data it returns APP_SENT_DATA. This informs TCP that the application has just successfully sent something and TCP doesn't have to acknowledge the received data - the ack has already been piggy-backed with the transmitted data.

If the transmission were to fail and need repeating, the server has to handle the event TCP_EVENT_REGENERATE. Since we only ever send one message it is obviouse that this has failed and so it is simply resent. The event TCP_EVENT_ACK will be received when the transmitted message actually has arrived safely.
It is in fact a great shame that messages can and do get lost (but it is not a perfect world and so we have to be ready for it) because regeneration of previously sent messages is probably the most complicated and/or RAM consuming parts of TCP implementations. This example has it real easy, but this is also a real simple example!

Here is a list of all possible return codes from the listener.

  • APP_ACCEPT             This is normal return when no data has been sent. The TCP level will then acknowledge the received frame and the connection will continue
  • APP_SENT_DATA       The listener is informing TCP that it has sent data (be sure that data was really sent when returning this, it should not be returned if a transmission attempt failed)
    The TCP level will therefore not send an ACK of its own since the data will have had a piggy-back ack of its own. If there is a mistake and data is sent and APP_ACCEPT is returned it is not serious – there will simply be an unnecessary ACK sent which doesn’t actually hurt anything (just not so efficient)
  • APP_REJECT             This is used to reject a connection (for example if the IP address is on a black list…) It causes TCP level to return a RESET rather than perform the TCP connection sequence
  • APP_REQUEST_CLOSE    The application informs TCP that the application has just actively started the close of the present TCP connection.
    It is returned after the application calls fnTCP_close(socket); 
  • APP_REJECT_DATA       This is a special case where the application doesn’t want to accept data at the moment. It doesn’t cause the connection to be broken down but TCP will not ACK the data. The peer will then be forced to repeat the data – resulting in a delay (using the peer as a queue, possibly until data can be received).
  • APP_WAIT                Same as previous but delay on TCP connection establishment.

Our example has made use of very few features up to now and there is much more to be explained. However this should allow first steps to be taken and also the servers in the project (HTTP, FTP etc.) to already make a lot more sense.

I will continue the topic later with a first simple client example. If you already have confusions or questions, or corrections etc. please feel free to comment.

Regards

Mark
« Last Edit: March 08, 2009, 01:54:54 PM by mark »

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3232
    • View Profile
    • uTasker
Re: Using TCP in the uTasker project
« Reply #1 on: July 20, 2007, 11:22:34 PM »
Hi

Here is some general information about TCP use in the uTasker project. It is an overview rather than a tutorial and is possibly a little vague in some respects. I hope that it doesn't confuse more that it helps, but it may well contain something of interest so here it is:



Here are the main user calls for working with TCP (see tcpip.h). You will find that the uTasker project is not based on Berkley conventions – it was designed as an alternative solution in small footprint projects where a mix between performance, reliability, simplicity and user comfort was defined, based on typical embedded project use. Therefore it doesn’t not try to copy any existing solution but goes its own way.


extern USOCKET fnTCP_Listen(USOCKET TCP_socket, unsigned short usPort, unsigned short usMaxWindow);

extern unsigned short fnGetFreeTCP_Port(void);

extern USOCKET fnGetTCP_Socket(unsigned char ucTos, unsigned short usIdleTimeout, int (*listener)(USOCKET, unsigned char, unsigned char *, unsigned short) );

extern USOCKET fnReleaseTCP_Socket(USOCKET TCPSocket);

extern USOCKET fnTCP_Connect(USOCKET TCP_socket, unsigned char *RemoteIP, unsigned short usRemotePort, unsigned short usOurPort, unsigned short usMaxWindow );

extern USOCKET fnTCP_close(USOCKET TCP_Socket);

extern signed short fnSendTCP(USOCKET TCP_socket, unsigned char *ptrBuf, unsigned short usDataLen, unsigned char ucTempFlags);

extern QUEUE_TRANSFER fnSendBufTCP(USOCKET TCP_socket, unsigned char *ptrBuf, unsigned short usDataLen, unsigned char ucCommand);

extern void fnModifyTCPWindow(USOCKET Socket, unsigned short usSafetyWindow);

extern void fnReportWindow(USOCKET Socket, unsigned short usBufferSpace);

 

The TCP task handles ARP so ARP is fully transparent to the user. However there are three types of TCP sockets which can be used.

The first is a RAW socket where the user supplied call back handles all events (see for example the http call back) which means that also repetitions must be handled there.

The second is a buffered socket. TELNET is an example of a protocol which makes use of this. The TCP socket has an inbuild data buffer for backing up data. It can send data of 'random' nature  rather than 'regeneratable' data at higher rates because it uses windowing and the buffered for retransmissions if necessary, as well as grouping single data transissions into larger transmission packets.

The third is a TELNET like socket where a TELNET socket is used instead of a TCP buffered socket – the TELNET socket can be in RAW mode so that it is otherwise transparent and doesn’t do negotiation or byte stuffing etc. The advantage of the TELNET like socket is that the TELNET layer provides an intermediate handler which handles repetitions and also TCP windowing so that high through put can be easily achieved. The down side is that it requires some buffer space (it works with fnSendBufTCP() rather than fnSendTCP()) to allow back ups of transmitted frames where the user simply writes to the socket as if it were any data port like a serial interface – the TELNET layer gets rid of the buffered data in the most efficient manor possible and ensures reliable delivery. There is an example of this in the demo project in debug.c (Telnet_socket and fnTELNETListener). The advantage to a application using a TELNET like socket rather than directly a buffered TCP socket is that the TELNET layer handles all detail - the application interface profits from the intermediate TELNET layer to do the "dirty work".

 

These are the TELNET specific routines:

extern USOCKET fnStartTelnet(unsigned short usTelnetPortNumber, unsigned short usIdleTimeout, unsigned short usMaxWindow, UTASK_TASK wakeOnAck, int (*listener)(USOCKET, unsigned char, unsigned char *, unsigned short) );

extern void fnStopTelnet(USOCKET TelnetSocket);

extern int  fnTelnet(USOCKET Telnet_socket, int iCommand);

extern int  fnCheckTelnetBinaryTx(USOCKET Socket);



The sockets in the uTasker project are always 'non-blocking' sockets (it is not possible to really compare them with sockets used with Windows etc.). Reception frames result in call back events and transmitted frames are either successful (an ACK call back event is received at some time in the future) or fail (a REGENERATE call back event is received at some time in the future – note that the REGENERATE event also results when a frame couldn’t be sent until ARP was resolved). These events will be handled by a TELNET layer if a TELNET like socket is used or else it is up to the application behind the TCP socket to handle them. Again the way this is performed is easy to see in the various protocols. If something is not clear, simply run the demo project in the simulator and set a break point in any code which is of interest – the code will break when it is used and the rest is usually self-explanatory.




I expect that various points here will be elaborated in further posts!

Regards

Mark


« Last Edit: July 20, 2007, 11:24:21 PM by mark »

Offline neil

  • Sr. Member
  • ****
  • Posts: 438
    • View Profile
Re: Using TCP in the uTasker project
« Reply #2 on: July 21, 2007, 09:21:42 AM »
Hi Mark,
  In the above call back function 'static int fnTestListener(...)' , would it be best to leave as it is in most applications ? Simply call a task when data comes in, and leave all the rest as it is?

 Also..
 'fnGetTCP_Socket(TOS_MINIMISE_DELAY, (unsigned short)10, fnTestListener); '

Our application will have a permanent TCP connection to our server, and sends a byte of information to the server every 2 minutes, so is it feesable to have a timeout of 130 seconds?

I also assume that for creating a client connection application, it will be the same as your server description, but instead of listning, simply make a connection, then all else is the same?

Many Thanks
Neil

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3232
    • View Profile
    • uTasker
Re: Using TCP in the uTasker project
« Reply #3 on: July 21, 2007, 01:21:44 PM »
Hi Neil

The framework of the call back is basically the same in every use. In some cases unused cases can be grouped to a default handling, although the compiler probably optimises to this any way. Data can be either handled in the TCP_EVENT_DATA case directly or else call further subroutines for readability.
Since the data is passed via pointer to the callback (it is still in the LAN input buffer) it should either be fully processed or copied to a permenent buffer if the processing were to be delayed for later. As soon as the call back returns the LAN input buffer will be unblocked so that the LAN controller can re-use it.
Note also that handling the data in another task has no real benefits since it is already being handled in the Ethernet task and so is not blocking interrupts. The LAN Rx interrupt simply schedules the LAN task which is doing the work 'off-line'. It has checked IP and determined that it is a valid TCP frame and the TCP module is a subroutine to the Ethernet task. TCP does have its own task but this is only used for monitoring connection timers and retransmissions in case of timeouts or ARP resolution. The TCP task doesn't handle rx data.


If you set fnGetTCP_Socket(TOS_MINIMISE_DELAY, 130, fnTestListener) the socket will have an idle time out of 130s and so will do what you require. As soon as your 'Keep-Alive' message stops retriggering the connection it will timeout and close.
The range of the idle timer is 1...0xfffe (1s to 65534s). 0 should not be used and 0xffff means NO IDLE TIMEOUT or infinite.

A client doesn't need to listen and so after actively starting a connection the rest is basically the same, so your assumption is correct. Also a listener can establish a client type connection by actively establishing it itself (when no connection already exists on the socket), so the only real difference is that it is not possible to establish a TCP connection with a client socket from a remote peer since there is no listener on the port.

Note that several listeners (servers) are possible on a single port (each needs a seperate socket) but only one client is possible. A client will generally get a "random" free port number for each connection - the function which does this is called:
extern unsigned short fnGetFreeTCP_Port(void);
and it is handled by TCP itself when a connection is to be established.

If you have multiple servers on the port they can share the call back by adding a loops as follows. This example if from the HTTP code where each HTTP socket has a structure which saves the socket index of each connection. Since the socket belonging to the TCP event is passed as a parameter it is then quite easy to search which session the connection belongs to.

Code: [Select]
static int fnHTTPListener(USOCKET Socket, unsigned char ucEvent, unsigned char *ucIp_Data, unsigned short usPortLen)
    HTTP *HTTP_session = ptrHTTP;
    int iSessionNumber;

    for (iSessionNumber = 0; iSessionNumber < NO_OF_HTTP_SESSIONS; iSessionNumber++) {
        if (HTTP_session->OwnerTCPSocket == Socket) {                    // search for the session to handle this event
            switch (ucEvent) {
            case TCP_EVENT_CONREQ:                                       // session requested
                return APP_ACCEPT;                                            // accept connection request
...etc.
            }
        }
        HTTP_session++;
    }
    return APP_REJECT;                                                        // owner socket not found!
}


Regards

Mark

Offline neil

  • Sr. Member
  • ****
  • Posts: 438
    • View Profile
Re: Using TCP in the uTasker project
« Reply #4 on: July 21, 2007, 02:19:40 PM »
Hi Mark,
In the previous reply you mentioned:

>>Note that several listeners (servers) are possible on a single port (each needs a seperate socket) >>but only one client is possible.

Does this mean its not possible to have 2 or more clients connecting to a server?

Neil

Offline neil

  • Sr. Member
  • ****
  • Posts: 438
    • View Profile
Re: Using TCP in the uTasker project
« Reply #5 on: July 21, 2007, 02:49:44 PM »
Hi Mark,
 I quickly tried to add a client, and have it connecting to my server application, but without any luck.  Could you let me know what I am doing wrong? Here is the small code.. (It just simply connects, no sends/receives)

USOCKET Test_socket;

static int fnTestSocket(USOCKET Socket, unsigned char ucEvent, unsigned char *ucIp_Data, unsigned short usPortLen)
{

    switch (ucEvent)
   {
      case TCP_EVENT_CONREQ:                                             
      case TCP_EVENT_CONNECTED:
      case TCP_EVENT_CLOSE:
      case TCP_EVENT_ACK:
      case TCP_EVENT_ARP_RESOLUTION_FAILED:
      case TCP_EVENT_PARTIAL_ACK:
         break;
      case TCP_EVENT_REGENERATE:
      case TCP_EVENT_DATA:
         if ((ucEvent == TCP_EVENT_REGENERATE) || (!uMemcmp((CHAR*)ucIp_Data, "TEST" , 4)))
         {
         }            }
         break;
      case TCP_EVENT_ABORT:
      case TCP_EVENT_CLOSED:
         break;
   }
    return APP_ACCEPT;
}


Within the 'fnApplication(..)', making it the last bit of code within 'if (!iAppState)':

Test_socket = fnGetTCP_Socket(TOS_MINIMISE_DELAY, (unsigned short)20, fnTestSocket);
fnTCP_Connect(Test_socket, "192.168.0.184", 10000, 0, 0); //my server address and port...

The connection is never made (all done within simulator), and the call back function always gets called with switch value of '9'.

I have my server application running in debug, so I can tell when a connection is made.
Regards
Neil

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3232
    • View Profile
    • uTasker
Re: Using TCP in the uTasker project
« Reply #6 on: July 21, 2007, 04:31:35 PM »
Neil

Quote
Does this mean its not possible to have 2 or more clients connecting to a server?

What I meant was that it is no two clients from the same IP address may have the same port number when connected to the same server port number. This is also the same restriction for the clients at the other end of the connection when we are acting as server.
So there is no problem with having multiple clients connected to a server.

I should be more precise. This is not an implementation restriction but a simple TCP connection rule:
A TCP connection is determined by two parameters; IP addresses at the two ends and Port numbers at the two ends.
For example Device A is at 192.168.0.3 and device B is at 192.168.0.4 in a local network
Device A and B have servers running on Port 80 (HTTP port).

Now Device A and Device B both have clients which connect to the server.
Let's say there are 2 connections from each. The clients will use a 'random' port number when connecting for the first (the uTasker uses a dynamic port range between 49152 and 65535) and each subsequent connection then uses the next port number.

Therefore the following 4 connection may exist, which are defined by the unique connection pairs (IP and Port):
Connection 1 (Client at A connected to server at B)
    192.168.0.3:50672 <-> 192.168.0.4:80
Connection 2 (Client at A connected to server at B)
    192.168.0.3:50673 <-> 192.168.0.4:80
Connection 3 (Client at B connected to server at A)
    192.168.0.3:80 <-> 192.168.0.4:52819
Connection 4 (Client at B connected to server at A)
    192.168.0.3:80 <-> 192.168.0.4:52820

As you can see. The server uses the same port but there is always a different client port number. Therefore each TCP connection is unique since the port pairs are never the same.

Regards

Mark


Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3232
    • View Profile
    • uTasker
Re: Using TCP in the uTasker project
« Reply #7 on: July 21, 2007, 04:36:56 PM »
Neil

Code: [Select]
fnTCP_Connect(Test_socket, "192.168.0.184", 10000, 0, 0); //my server address and port...
The first problem that I see is in the use of the IP address.
The uTasker project doesn't store IP addresses as strings but rather as arrays.

Therefore you need to spefify your IP address like this :

Code: [Select]
unsigned char ucMyIp[IPV4_LENGTH] = {192,168,0,198};
fnTCP_Connect(Test_socket, ucMyIp, 10000, 0, 0); //my server address and port...

This may then work.
What the previous version probably did was send the connection request to 31.39.32.31 which would then be directed to your default gateway and sent out onto the internet.
If you are receiving TCP_EVENT_ARP_RESOLUTION_FAILED (this will occur after about 4..5 seconds) it would imply that you don't have a local gateway.

Note that you can also activate "USE_TIME_SERVER" in config.h to see how it program operates. It is a client example and there are a number of timer servers which it attempts to contact until one if successful. The time server will send one message (with the time - surprise surprise) and close the connection.

On thing to note is a difference here between when working with the simulator and working on the target. When the client tries to connect in the initialisation of application.c the simulator can send immediately. On the target it generally takes a few seconds for the Ethernet link to become ready and so it takes a little longer before it all works.

Another point to note is that a client connection attempt often causes an ARP resolve to be initialted. This is because the address of the destination is not yet in the local ARP table. A server will invariably have the IP address already in the table since it was refreshed there as it received the connection attempt (SYN) from the remote client. For the user this is in fact totally transparent since TCP handles it itself when connecting. It is however possible that during an active connection the ARP entry times out (due to non activity or a network with high activity causing it to be flushed). In this case the ARP resolve will be automatically started but the listener will need to be able to resend the last message via the TCP_EVENT_REGENERATE.

fnSendTCP() will return the number of bytes transmitted. If it has to start an ARP resolution it will however return NO_ARP_ENTRY. In this case the user can also prepare for the regeneration event which is expected. Should instead TCP_EVENT_ARP_RESOLUTION_FAILED be received, it means that the connection partner is no longer reachable - it has maybe turned off in the meantime and so the connection could be terminated.

Regards

Mark





Offline neil

  • Sr. Member
  • ****
  • Posts: 438
    • View Profile
Re: Using TCP in the uTasker project
« Reply #8 on: July 21, 2007, 05:09:12 PM »
Hi Mark,
  The above worked fine, thanks, I can now connect to the server application.  I will soon start looking at sending/receiving side.

Many Thanks
Neil

Offline neil

  • Sr. Member
  • ****
  • Posts: 438
    • View Profile
Re: Using TCP in the uTasker project
« Reply #9 on: July 22, 2007, 01:08:13 PM »
Hi mark,
  After succesfully connecting to my server application (Windows based), I thought I would send some data. I want to send the following block of data, which my server application understands as a command:

After a connection is made, within the call back function I insert the code to send to my server, as follows:

unsigned char Buff[10];
//call back function
static int fnTestSocket(USOCKET Socket, unsigned char ucEvent, unsigned char *ucIp_Data, unsigned short usPortLen)
{
   TCP_MESSAGE test_message;

    switch (ucEvent)
   {
         case TCP_EVENT_CONNECTED:
                Buff[0]=0;
                Buff[1]=5;
                Buff[2]=233;
                 Buff[3]=5;
                Buff[4]=32;
                          if (fnSendTCP(Socket, &Buff[0], 5, TCP_FLAG_PUSH) > 0)
                   return APP_SENT_DATA;
                         break;

                    ....
             }


The above gets sent, but when I debug my server application, the above values are not received, there are 5 characters sent, but not the above.

Can you point me to where I am going wrong?

Regards
Neil

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3232
    • View Profile
    • uTasker
Re: Using TCP in the uTasker project
« Reply #10 on: July 23, 2007, 02:17:13 PM »
Hi Neil

Take a look at the first example. It uses:

if (fnSendTCP(Socket, (unsigned char *)&test_message->tTCP_Header, 4, TCP_FLAG_PUSH) > 0)

when sending.

Note that the buffer should also supply space for the TCP layer to add its header. This is so that TCP doesnt have to copy data contents to merge it with its header and is a rather specific solution - however once you have the buffer set up accordingly it should be easy to use.

Your copy is effectively giving  a pointer to your test message which TCP will overwrite with the header and it will then send 5 extra bytes beyond the header length, which will be a bit random since this is beyond the end of your buffer.

If you use a structure as in the example and build your test message up in the data part of the buffer it should then send the content that you are expecting.

Good luck

Regards

Mark
« Last Edit: November 19, 2007, 12:10:21 PM by mark »

Offline neil

  • Sr. Member
  • ****
  • Posts: 438
    • View Profile
Re: Using TCP in the uTasker project
« Reply #11 on: July 23, 2007, 03:06:16 PM »
Hi Mark,
  Thanks for the reply.
It worked fine as you mentioned, I just never realised that the header had to be included, just thought need to provide a buffer,but worked fine.

It strange, as I tried it , and all worked fine, I then closed my server and simulator. Then re-ran them the same way. The simulator tried to connect, but a connections didnt make a connection. Ive tried it several times, but no luck. Ive managed 2 connections from about 10 attempts. If I run my windows client application we use, we can do the exactly the same thing I am trying with the simulator, and everytime it connects tot he server.

Regards
Neil

Offline neil

  • Sr. Member
  • ****
  • Posts: 438
    • View Profile
Re: Using TCP in the uTasker project
« Reply #12 on: July 23, 2007, 04:08:16 PM »
Hi mark,
  Regarding the previous problem of not being able to connect.

With the problem above, not connecting, I was trying it on the sumilator. I now copied the code into the 52233 flash. And it worked fine, without modifying any code.

Within the call back routine above 'static int fnTestListener(USOCKET Socket, unsigned char ucEvent, unsigned char *ucIp_Data, unsigned short usPortLen)' is 'ucIp_Data' a pointer to the data read in, and 'usPortLen', The number of characters?

Regards
Neil
« Last Edit: July 23, 2007, 07:30:06 PM by neil »

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3232
    • View Profile
    • uTasker
Re: Using TCP in the uTasker project
« Reply #13 on: July 24, 2007, 09:46:39 PM »
Neil

I cant explain why it is not working every time with the simulator. If you send me an Ethereal recording I may be able to see what is happening.

ucIp_Data is a pointer to either the data in the input buffer or the IP address of the connection partner (in the same format as all local IP addresses). usPortLen is either a port number or a length, according to the event type.

Here is a complete list:

TCP_EVENT_ARP_RESOLUTION_FAILED. ucIp_Data is a pointer to the remote IP address. usPortLen is the port number.
TCP_EVENT_CONREQ. ucIp_Data is a pointer to the remote IP address. usPortLen is the port number.
TCP_EVENT_CONNECTED. ucIp_Data is a pointer to the remote IP address. usPortLen is the port number.
TCP_EVENT_ACK. ucIp_Data is a pointer to the remote IP address. usPortLen is the port number.
TCP_EVENT_CLOSE. ucIp_Data is a pointer to the remote IP address. usPortLen is the port number.
TCP_EVENT_ABORT. ucIp_Data is a pointer to the remote IP address. usPortLen is the port number.
TCP_EVENT_CLOSED. ucIp_Data is a pointer to the remote IP address. usPortLen is the port number. Exception: if REUSE_TIME_WAIT_SOCKETS option is enabled it is possible that a connection in the TCP_STATE_FIN_WAIT_2 state will be prematurely terminated rather than rejecting a connection due to no sockets available in the TCP socket pool. In this case the port number is 0.

TCP_EVENT_REGENERATE. ucIp_Data is not used and is 0. usPortLen is a length - how many bytes need regenerating.

TCP_EVENT_PARTIAL_ACK. ucIp_Data is a pointer to the remote IP address. usPortLen is a length - the number of bytes not yet acked.

TCP_EVENT_DATA. ucIp_Data is a pointer to the received data content. usPortLen is a length - the number of bytes of data.

Regards

Mark

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3232
    • View Profile
    • uTasker
Re: Using TCP in the uTasker project
« Reply #14 on: July 24, 2007, 09:47:11 PM »
Here is a simple TCP client example:

Earlier I gave a simple example of creating a TCP server socket which could be connected to from a remote client. In that case it was necessary to set the socket to listening state so that it could respond to connection attempts.

In the case of the client it is not necessary to set the socket to any special state, however it is also possible to set the socket to listening state but subsequently use it to connect as a client to a remote server. This means that any socket can be used as a client socket - but it can only establish a client connection if it has not already a connection from a remote client. (Put another way, a socket can only handle one connection; either the connection is established as a server from the listening state or as a client from any state).

The following code wil thus create a socket for client use and then attempt to establish a connection to a remote server.

USOCKET test_client_socket = fnGetTCP_Socket(TOS_MINIMISE_DELAY, TCP_DEFAULT_TIMEOUT, fnTestListener);

As in the case of the server, there must be a free socket available and the user must ensure that there are enough sockets for a particular application in the socket pool. USER_TCP_SOCKETS defines the number of sockets reserved for use by the application.

If no socket is available for use, the routine fnGetTCP_Socket() will return a negative error code.

static unsigned char ucRemoteIP[IPV4_LENGTH] = {192,168,0,101};
#define TEST_TCP_PORT 0x1234
if (test_client_socket >= 0) {
    fnTCP_Connect(test_client_socket, ucRemoteIP, TEST_TCP_PORT, 0, 0);
}

This call only attempts to establish a connection when the socket is valid. It causes TCP to send a connection request (SYN) to the IP address 192.168.0.101 on port 0x1234. Note that all IP addresses in the uTasker project are defined as arrays and not as strings which is another common method ("192.168.0.101" is therefore not a valid IP address definition).
In the example, the last 2 parameters of the fnTCP_Connect() call are 0. The first can be used to define the local port number but typically a client uses a more or less random port number when connecting and so a 0 informs TCP that it should define the port using its standard algorithm. The last parameter allows a maximum TCP window size to be specified. This will not be discussed further here since it will be used in a later topic. A zero informs TCP to use the standard rx window size, which is equivalent to the maximum TCP payload of an Ethernet frame (1460 bytes).


The client connection attempt may cause the connection frame to be sent out onto a local LAN (if the IP address belongs to the local subnet) or else to the default gateway address if it is outside of the local subnet. In either case, if the destination IP address does not yet exist in the ARP table this will first be resolved by the ARP protocol.

The way that ARP operates is transparent to the user when connecting. Either the connection is sent (either immediately or after ARP has resolved the IP address) or it can not be sent due to the fact that ARP - after several attempts, and a delay of about 5s - still can not resolve. The the callback is un this case called with event TCP_EVENT_ARP_RESOLUTION_FAILED.

Assuming that the connection is successful, TCP will call the listener with the event TCP_EVENT_CONNECTED.

Once connected, the rules for the connection are the same. Whether client-server or server-client the connection is effectively peer-peer.
As with the listener case, either party can close the TCP connection.


Note that the Time Server demo in the uTasker demo project shows how a client connects to a remote server, which automatically sends back the time before immediately closing the TCP connection. To activate this, set the define USE_TIME_SERVER in config.h.




Regards

Mark