====== Client library ====== ===== Functions ===== ==== client_init ==== The ''client_init'' function should be called before any other client related functions and connects to ''serverAddr'' (probably ''localhost'') on port ''port''. Both of these values should be #defined in ''config.h'' as SERVER and PORT respectively. The function is prototyped as: int client_init (char *serverAddr, unsigned short port); in client.h On success ''client_init'' function returns the socket file descriptor of the server that it has connected to which should be passed to all client-related functions. On failure the function returns -1 and makes a note in the client error log. ==== client_recv_packet ==== The ''client_recv_packet'' function is used to read packets of data from the server. It reads a single packet of data from ''serverFD'' (the servers network file descriptor returned by ''client_init'') and then places the packet into ''packet'' which should be a pointer to a ''client_packet'' structure. It is prototyped as: int client_recv_packet (unsigned short serverFD, struct client_packet *packet); in client.h The ''client_recv_packet'' function will automatically perform byte-conversions on ''packet->command'' and ''packet->len'' using the ''ntohs'' function. First of all the function will read 4 bytes of data from the ''serverFD'', convert them to the host byte order and then use the ''packet->len'' variable to work out the length of the data segment of the packet. Next, ''malloc'' is used to allocate enough memory to store the data segment of the packet and the null byte (''packet->len + 1''), before going onto reading the data segment into the newly allocated string and then setting the last byte to ''\0''. **Please note the the data segment of the packet (''packet->data'') is made up of memory allocated off of the heap, and so needs to be ''free()'''ed when no longer needed to prevent a memory leak!** On success this function will return 0 while on failure this function will return -1 and will make a note in the error log about exactly what went wrong. ==== client_recv_packets ==== The ''client_recv_packets'' function is very similar to the ''client_recv_packet'' function, except that it will return all of the packets in the buffer and will allocate memory as required. Unlike ''client_recv_packet'' the ''client_recv_packets'' function is non-blocking and will return if there is nothing in the buffer. It is prototyped as: struct client_packet *client_recv_packets(unsigned short serverFD, int *npackets); in client.h Since this function makes calls to the ''client_recv_packet'' function to actually read the data from the buffer it also performs byte-conversions on ''command'' and ''len'' member variables. On success this function returns a pointer to a ''client_packet'' structure and places the number of items read from the server into ''npackes''. On failure, ''packet'' is returned (which is by default initialised to ''NULL'') and ''npackets'' is set to 0. ==== client_send_packet ==== The ''client_send_packet'' function should be used to send a packet of information to the server (aka the 'party line'). It sends ''packet'' to ''serverFD'' (which is the servers file descriptor) and is prototyped as: int client_send_packet (unsigned short serverFD, struct client_packet *packet); in client.h Like the ''client_recv_packet'' function the ''client_send_packet'' function will automatically perform byte-conversions on both ''packet->command'' and ''packet->len'' to convert them to the network byte order using ''htons'' function. On success the function will return 0 otherwise, on failure, will return -1 and make a note in the error log about what went wrong. ==== client_close ==== The ''client_close'' function simply closes ''serverFD'' by calling the ''close'' function (which it is no more than a wrapper over) and is prototyped as: void client_close (unsigned short serverFD) in client.h ''client_close'' should be called when the connection to the server ('party line') is no longer needed. ===== Example ===== Here is a simple application that demonstrates the use of these functions. The program basically forks (creates a copy of) itself. Both of the processes then connect to the server, once a connection has been established the child process will then wait for 0.25 seconds, giving the parent process time to send two packets of data to the party line at which point the parent process then exits. After the child processes 0.25 second wait has elapsed it will call the ''client_recv_packets'' function to read all of the packets from the server (which should be the two that the parent sent) and then prints them out to the terminal before ''free()''ing all of the memory allocated by the ''client_recv_packets'' function and exiting. /*************************************************************************** * Copyright (C) 2006 the `High Altitude Slug Project' * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to * * the following conditions: * * * * The above copyright notice and this permission notice shall be * * included in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.* * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * * OTHER DEALINGS IN THE SOFTWARE. * ***************************************************************************/ /* * Slug * test_client.c * By: Freddie Witherden * A simple test application designed to test both the server and client * libraries */ /* Includes */ #include #include #include #include #include #include #include "config.h" #include "client.h" /* Messages and command words to be sent */ #define CMD_1 100 #define MSG_1 "foo is to bar as spam is to eggs" #define CMD_2 7000 #define MSG_2 "this statement is false!" int main (void) { int serverFD; /* Server network file descriptor */ pid_t pid; /* Process ID, for fork */ /* Fork */ if ((pid = fork()) == -1) { /* Error */ fputs("Error fork()ing the process\n", stderr); exit(EXIT_FAILURE); } else if (pid == 0) { /* Child */ struct client_packet *message; /* For pointing to the recieved message */ int npackets; /* For holding the number of packets read */ int i; /* Connect */ serverFD = client_init(SERVER_HOST, SERVER_PORT); if (serverFD == -1) { fputs("Error connecting to the server (are you sure it is running?)\n", stderr); exit(EXIT_FAILURE); } /* Sleep for 0.25 secs */ usleep(250000); /* Read the data from the server that the parent has sent */ message = client_recv_packets(serverFD, &npackets); if (message == NULL) { fputs("Error reading packets from the server\n", stderr); exit(EXIT_FAILURE); } /* Print out the packets */ for (i = 0; i < npackets; i++) { printf("Comamnd word is: %i\n", message[i].command); printf("Length of data segment is: %i\n", message[i].len); printf("Data segment is: \"%s\"\n", message[i].data); } /* Free the allocated memory */ for (i = 0; i < npackets; i++) free(message[i].data); free(message); } else { /* Parent */ struct client_packet messages[2] = { { CMD_1, MSG_1, strlen(MSG_1) }, { CMD_2, MSG_2, strlen(MSG_2) } }; /* Connect */ serverFD = client_init(SERVER_HOST, SERVER_PORT); if (serverFD == -1) { fputs("Error connecting to the server (are you sure it is running?)\n", stderr); exit(EXIT_FAILURE); } /* Send the messages */ if (client_send_packet(serverFD, &messages[0]) == -1 || client_send_packet(serverFD, &messages[1]) == -1) { fputs("Error sending packets to the server\n", stderr); exit(EXIT_FAILURE); } } /* Close the connection */ client_close(serverFD); return 0; }