Client-server Sockets 1
How to Write a Network Based Program (Using Microchip's TCP/IP Stack) A network based program that uses the Client-server architecture must create at least two separate programs, which are intended to run on different hosts. One of these programs plays the role of the server, and the other plays the role of the client. For the server program, initially, a software structure, called a Server Socket must be created. Once a server socket has been created and started, then the server can listen for client connections and exchange data with a connected client. The type of the socket must be a TCP server socket, because, as discussed previously, we are using TCP. 2
How to Write a Network Based Program (Using Microchip's TCP/IP Stack) For the client program, initially, a software structure, called a Client Socket must be created. Once a client socket has been created, then the client may connect to a running server and exchange data with it. Each program is said to bind a software socket to its end of the connection. To communicate, the client and the server each reads from and writes to the socket bound to the connection. 3
We will now explain how to create a client socket and a server socket on the MX7cK board, using the C language and Microchip s TCP/IP Stack API. We focus on the server socket on the MX7cK board, because, in our ultimate application for this course, the MX7cK board will play the role of a server to make available its sensor readings to any client host that may connect to it. Project 1 will use a telnet client to create a TCP client socket to connect with the TCP server socket on the MX7cK board. Later on in the course, when Project 2 is being done, we will explain how to create a client socket and a server socket using the Java language and its Networking API and the Socket class. With a server socket running on the MX7cK board, we can then use the Java client on a remote host to make connections with the MX7cK board and communicate data. 4
Overview of Microchip s TCP/IP API (From [1]) 5
TCP_SOCKET TCPOpen ( DWORD dwremotehost, BYTE vremotehosttype, WORD wport, BYTE vsocketpurpose ); Function Prototype TCP_SOCKET TCPOpen(DWORD dwremotehost, BYTE vremotehosttype, WORD wport, BYTE vsocketpurpose); Duel purpose function, i.e., it can be used to create/open a client socket and a server socket. See \xc32\v1.33\pic32mx\include\generictypedefs.h for typedefs of DWORD, BYTE, WORD, etc. 6
Used by client Sockets only This makes sense because a client socket needs to specify to which server the socket should connect, while the server socket is the one being connected to. A server socket would use null here. If you are creating a client socket, then dwremotehost accepts: String of the remote host name, e.g.: www.google.ca String of the IP address of the remote host, e.g.: 192.168.1.123 Literal of IP address, e.g.: 0x7B01A8C0 (literal of 192.168.1.123 in Hex) Pointer to a NODE_INFO structure with the remote IP address specified. 7
Identifies the meaning of dwremotehost TCP_OPEN_SERVER Opens a server socket and ignores dwremotehost TCP_OPEN_RAM_HOST Open a client socket to Host, where the ID is specified by a string TCP_OPEN_ROM_HOST Same as above for RAM deficient systems TCP_OPENIP_ADDRESS Open a client socket to Host, where the ID is specified by a literal TCP_OPEN_NODE_INFO Open a client socket to Host, where the ID is specified by a node structure 8
TCP Port to listen to (for server socket) or connect to (client socket) For a Client Socket, This specifies the remote TCP port to which a connection should be made. Note: the local TCP port for client sockets will be automatically picked by the TCP API. For a Server Socket, This specifies the local TCP port on which the server should listen for connections. 9
vsocketpurpose Identification of the Socket Used to identify the pre-specified socket data structure. A data structure must be created, statically at compile time, for each socket the software will use at run time. Note: mainstream TCP/IP stacks create sockets dynamically at run time on demand. 10
// Open a server socket: listen to port 7777 MySocket = TCPOpen( 0, TCP_OPEN_SERVER, 7777, MY_PRE_SPECIFIED_SOCKET ); 11
You must set certain information for each socket you intend on using in your application. When the program first begins to run, the Stack Initialization routine uses your information to allocate memory for your socket(s). It does this once throughout the entire program. Note that the standard way of creating sockets in the C language is dynamically and at anytime when the program runs. In other words, when an application needs a socket, it creates one and uses the malloc() function to allocate memory dynamically. However, Microchip s TCP/IP stack API does not support dynamic memory allocation for the socket structures; The API expects you to set information about a socket before compile time, and then once that socket has been allocated memory by the Stack Initialization routine, then you can refer to that socket when you use TCPOpen() and other socket based operations, like reading and writing. 12
Four initialization parameters need to be specified in TCPIP ETH795.h for each of your sockets 1. Purpose of that socket (this is just an identifier) 2. Add your socket to the Socket Initializer Structure, and set the type of memory the socket should be stored in (RAM/ROM), and the sizes of the transmit FIFO and the receive FIFO. 3. Determine and set the total memory required for all sockets, including your addition. During initialization of the application, the stack software will create a TCP Control Block (TCB) with the above information for each socket. The TCB is used to control a socket s operations. 13
To be Written in TCPIP ETH795.h #define TCP_SOCKET_TYPES #define TCP_PURPOSE_GENERIC_TCP_CLIENT 0 #define TCP_PURPOSE_GENERIC_TCP_SERVER 1 #define TCP_PURPOSE_TELNET 2 #define TCP_PURPOSE_TCP_PERFORMANCE_TX 3 #define TCP_PURPOSE_UART_2_TCP_BRIDGE 4 #define TCP_PURPOSE_HTTP_SERVER 5 #define TCP_PURPOSE_HTTP_SERVER 6 #define TCP_PURPOSE_DEFAULT 7 #define TCP_PURPOSE_BERKELEY_SERVER 8 #define TCP_PURPOSE_LED_SERVER 9 #define END_OF_TCP_SOCKET_TYPES Choose any name you want. Assign it the next number. These are the sockets the Stack already uses. 14
2. Add TCP Socket to Initializer Structure To be Written in TCPIP ETH795.h Initially, there were 9 TCP Sockets in the Initializer array: } TCPSocketInitializer[] = { {TCP_PURPOSE_GENERIC_TCP_CLIENT, TCP_PIC_RAM, 125, 1200}, {TCP_PURPOSE_GENERIC_TCP_SERVER, TCP_PIC_RAM, 20, 20}, {TCP_PURPOSE_TELNET, TCP_PIC_RAM, 200, 150}, {TCP_PURPOSE_TCP_PERFORMANCE_TX, TCP_PIC_RAM, 2000, 1}, {TCP_PURPOSE_UART_2_TCP_BRIDGE, TCP_PIC_RAM, 256, 256}, {TCP_PURPOSE_HTTP_SERVER, TCP_PIC_RAM, 1500, 1500}, {TCP_PURPOSE_HTTP_SERVER, TCP_PIC_RAM, 1500, 1500}, {TCP_PURPOSE_DEFAULT, TCP_PIC_RAM, 1000, 1000}, {TCP_PURPOSE_BERKELEY_SERVER, TCP_PIC_RAM, 25, 20}, {TCP_PURPOSE_LED_SERVER, TCP_PIC_RAM, 200, 20} //My Socket }; Identifier chosen in previous slide. Use RAM, since the board has plenty. Determine how large your transmit buffer for your application should be. This is the size of the TxFIFO. Likewise determine how large your receive buffer for your application should be. This is the size of the RxFIFO. 15
3. Allocate memory for your Socket To be Written in TCPIP ETH795.h To add memory for the TCP_PURPOSE_LED_SERVER socket: #define TCP_ETH_RAM_SIZE (0ul) #define TCP_SPI_RAM_SIZE (0ul) #define TCP_SPI_RAM_BASE_ADDRESS (0x00) #define TCP_PIC_RAM_SIZE (12669 + 10*2 + 44 + 200 + 20) Initial size Memory needed for your socket. TxFIFO and RxFIFO each need an extra byte Sizeof TCB Control Block TxFIFO Size RxFIFO Size 16
Note: Calculation of Initial Size Initially, there were nine TCP Sockets in the Initializer array: TCPSocketInitializer[] = { {TCP_PURPOSE_GENERIC_TCP_CLIENT, TCP_PIC_RAM, 125, 1200}, {TCP_PURPOSE_GENERIC_TCP_SERVER, TCP_PIC_RAM, 20, 20}, {TCP_PURPOSE_TELNET, TCP_PIC_RAM, 200, 150}, {TCP_PURPOSE_TCP_PERFORMANCE_TX, TCP_PIC_RAM, 2000, 1}, {TCP_PURPOSE_UART_2_TCP_BRIDGE, TCP_PIC_RAM, 256, 256}, {TCP_PURPOSE_HTTP_SERVER, TCP_PIC_RAM, 1500, 1500}, {TCP_PURPOSE_HTTP_SERVER, TCP_PIC_RAM, 1500, 1500}, {TCP_PURPOSE_DEFAULT, TCP_PIC_RAM, 1000, 1000}, {TCP_PURPOSE_BERKELEY_SERVER, TCP_PIC_RAM, 25, 20}, }; Initial Size = 9 x sizeof(tcb) + SUM(TxFIFO + RxFIFO) = 9 x 44 + (125+1200) + (20+20) + + (25+20) = 12669 17
Note: Initially, there are actually 13 socket identifiers in file TCPIP ETH795.h. However, not all of these are used in the ini alizer array (commented out) (Look ). Also, I have added the LED_SERVER socket already. #define TCP_SOCKET_TYPES #define TCP_PURPOSE_GENERIC_TCP_CLIENT 0 #define TCP_PURPOSE_GENERIC_TCP_SERVER 1 #define TCP_PURPOSE_TELNET 2 #define TCP_PURPOSE_FTP_COMMAND 3 #define TCP_PURPOSE_FTP_DATA 4 #define TCP_PURPOSE_TCP_PERFORMANCE_TX 5 #define TCP_PURPOSE_TCP_PERFORMANCE_RX 6 #define TCP_PURPOSE_UART_2_TCP_BRIDGE 7 #define TCP_PURPOSE_HTTP_SERVER 8 #define TCP_PURPOSE_DEFAULT 9 #define TCP_PURPOSE_BERKELEY_SERVER 10 #define TCP_PURPOSE_BERKELEY_CLIENT 11 #define TCP_PURPOSE_LED_SERVER 12 #define END_OF_TCP_SOCKET_TYPES TCPSocketInitializer[] = { {TCP_PURPOSE_GENERIC_TCP_CLIENT, TCP_PIC_RAM, 125, 1200}, {TCP_PURPOSE_GENERIC_TCP_SERVER, TCP_PIC_RAM, 20, 20}, {TCP_PURPOSE_TELNET, TCP_PIC_RAM, 200, 150}, {TCP_PURPOSE_TCP_PERFORMANCE_TX, TCP_PIC_RAM, 2000, 1}, {TCP_PURPOSE_UART_2_TCP_BRIDGE, TCP_PIC_RAM, 256, 256}, {TCP_PURPOSE_HTTP_SERVER, TCP_PIC_RAM, 1500, 1500}, {TCP_PURPOSE_HTTP_SERVER, TCP_PIC_RAM, 1500, 1500}, {TCP_PURPOSE_DEFAULT, TCP_PIC_RAM, 1000, 1000}, {TCP_PURPOSE_BERKELEY_SERVER, TCP_PIC_RAM, 25, 20}, //{TCP_PURPOSE_TELNET, TCP_PIC_RAM, 200, 150}, //{TCP_PURPOSE_TELNET, TCP_PIC_RAM, 200, 150}, //{TCP_PURPOSE_FTP_COMMAND, TCP_PIC_RAM, 100, 40}, //{TCP_PURPOSE_FTP_DATA, TCP_PIC_RAM, 0, 128}, //{TCP_PURPOSE_TCP_PERFORMANCE_RX, TCP_PIC_RAM, 40, 2000}, //{TCP_PURPOSE_BERKELEY_SERVER, TCP_PIC_RAM, 25, 20}, //{TCP_PURPOSE_BERKELEY_SERVER, TCP_PIC_RAM, 25, 20}, //{TCP_PURPOSE_BERKELEY_CLIENT, TCP_PIC_RAM, 125, 100}, {TCP_PURPOSE_LED_SERVER, TCP_PIC_RAM, 200, 20}, }; #define TCP_PIC_RAM_SIZE (12669 + 10*2 + 44 + 200 + 20) 18
You will use the following three types of functions when writing data to a socket: TCPIsPutReady() TCPPut() Family of functions TCPFlush() 19
You will use the following two types of functions when reading data from a socket: TCPIsGetReady() TCPGet() Family of functions 20
Once you have set your information about a socket, then compile, download, and run your application. When the program starts, the Stack Initialization routine runs, and attempts to allocate memory for your socket(s). If you have correctly specified your socket information, then the LED1 will be blinking. This is a notification that the program and stack are running successfully. If you have incorrectly specified the socket information, then the program will enter an infinite loop. You will observe this because the LED will not be blinking. 21
[1] Microchip, Microchip Libraries for Applications Help Files, 14 September 2012. [Online]. Available: http://www.microchip.com/stellent/idcplg?idcservice=ss_get_page&nodeid=2680&ddo cname=en547784. [Accessed 23 September 2013]. 22