Table of Contents
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 <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/time.h> #include <stdint.h> #include <unistd.h> #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; }