Posts Tagged sockets
A Brief Linux Sockets Connect Illustration
For a little while I’ve been playing with sockets in C now and have come up with the following succinct example of connecting. Note that the connection is fairly flexible with regards to protocol and transport type. It really is simply there to make the connection to somewhere else with as few questions asked. It will involve DNS and service lookups if you provide names instead of the network address and port. If you want to catch exceptions you need to clear the standard error variable and check it yourself after receiving a fail return value. It won’t, or at least shouldn’t, report any exceptions because for my current purposes failures are not really exceptions just dead ends on some of many paths. I tried to keep it simple too, have fun.
/* connect_to_socket
*
* connect to a socket using an initialised addrinfo structure,
*
* info is an initialised addrinfo structure
* sock is a pointer to the location to store the socket descriptor when opened
*
* returns 0 if successful
*/
int connect_to_socket(const struct addrinfo *info, int* sock) {
/* open socket */
*sock = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
if (*sock < 1) return -1;
/* connect to the server*/
if (connect(*sock, info->ai_addr, info->ai_addrlen) == 0)
return 0;
/* clean up on failure */
close(*sock);
return -1;
}
/* connect_to_server
*
* connect to a server using the server name or adress and port or service name
*
* server is a string containing either the name or address of the server
* port is a string containing either the service name or port number to use
* sock is a pointer to the location to store the socket descriptor when opened
*
* returns 0 if successful
*/
int connect_to_server(const char* server, const char* port, int* sock) {
struct addrinfo hints, *info = NULL, *list = NULL;
int e = 0;
/* initialise the hints for retrieving the address details */
memset(&hints, 0, sizeof (hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
/* get the address if the server to connect to */
e = getaddrinfo(server, port, &hints, &list);
if (e != 0) return -1;
/* connect to the first socket in the list */
for (info = list; info != NULL; info = info->ai_next)
if (connect_to_socket(info, sock) == 0)
break;
/* clean up and return */
if (list != NULL) freeaddrinfo(list);
if (sock > 0) return 0;
else return -1;
}
When there’s Nothing in your Sock
Normally I do most of my network programming in Java; life is just easier that way. But some times, *very* rarely, you need to carve up a little C. So I’ve not touched socket networking for a while and all its foibles. Hence a few problems with reading from a socket.
When socket programming with the ‘read’ function it does not always return the length read into the buffer. In fact if you are talking to an HTTP server a ‘GET /‘ will nearly always end with read returning 0. This is because the socket will have been closed on your last read. This of course begs the question, how do you know how much was read in the last read? For me this was simple; I just zero filled the buffer prior to reading.
ssize_t r_read(int sock, void* buf, size_t size)
{
ssize_t ret;
bzero(buf, size);
while (ret = read(sock, buf, size - 1), ret == -1 && errno == EINTR);
return ret;
}
Which does work because now its a simple matter of counting until the first 0 shows up, but I’d still like to know how to tell how many characters were read without having to do the count. This becomes more important when transferring binary files as you won’t know if the 0 is validly part of the file or not.
Simple Berkeley Sockets C Server Example
About Apache and Bloat
Every now and then I look at Apache and wonder about its size and complexity. Its a huge beastie now but absolutely brilliant too. I have quite a respect for it as do many around the world. But then I don’t really use very much of its functionality.
Sure its been re-factored into a modular form so that you only need to use the bits that you want but it still presents a mental preponderance thatmany would consider bloat. But bloat is a term used to describe software that has vast wads of unnessecary code that could be accomplished more simply. However Idon’t think that Apache has that. All of its code seems very purposeful, and as I indicated earlier it is reasonably easy to include or exclude as you will.
Introducing the Example
So this example is not here to show you the right way of doing things, but rather how simple the solution to you particular problem could be. In this case the example server simply serves up the time on the server machine to port 6543 for any client and then closes the stream. It can do this for many concurrent connections at once given the only complexity that is added to the server is client handling by forked process.
Now to the Code
/* -----------------------------------------------------------------------------
simple example of a server in C using very simple Berkeley sockets
This is just an example for looking at. Not really how you would actually
implement a server. For a start it does not take care of signals and isn't
capable of being implemented as a service.
----------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <strings.h>
#include <arpa/inet.h>
/* we're not going to use arguments for this example */
#define SERV_UDP_PORT 6543
#define SERV_TCP_PORT 6543
#define SERV_HOST_ADDR "192.168.2.200"
int main (int argc, char ** argv, char ** env) {
int socket_server, socket_client, child_pid;
struct sockaddr_in address_client, address_server;
socklen_t client_address_size;
FILE *stream_client;
time_t now;
struct tm *tm;
/* open a tcp socket to listen on */
if ( (socket_server = socket(AF_INET, SOCK_STREAM, 0) ) < 0)
err_dump("server: can't open stream socket");
/* bind to our local address ans start listening so that clients can find us */
bzero((void *) &address_server, (size_t)sizeof(address_server));
address_server.sin_family = AF_INET;
address_server.sin_addr.s_addr = htonl(INADDR_ANY);
address_server.sin_port = htons(SERV_TCP_PORT);
if (bind(
socket_server,
(struct sockaddr *) &address_server,
sizeof(address_server))
< 0)
err_dump("server: can't bind local address");
listen(socket_server, 5);
for ( ; ; ) {
/* wait for client connection and accpt it when it comes */
client_address_size = sizeof(address_client);
socket_client = accept(
socket_server,
(struct sockaddr *) &address_client,
&client_address_size);
if (socket_client < 0)
err_dump("server: accept error");
if ( (child_pid = fork() ) < 0)
err_dump("server: fork error");
/* specific client process handling starts here */
else if (child_pid == 0) {
/* client process has no need of the server socket */
close(socket_server);
/* generate a file stream from the client socket */
if ((stream_client = fdopen(socket_client, "w")) == NULL) {
perror("daytimed fdopen");
return 5;
}
/* write to the client stream as per normal then close it */
if ((now = time(NULL)) < 0) {
perror("daytimed time");
return 6;
}
tm = gmtime(&now);
fprintf(stream_client, "%.4i-%.2i-%.2iT%.2i:%.2i:%.2iZ\n",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
fclose(stream_client);
exit(EXIT_SUCCESS);
/* client process has left the building */
}
/* the server has no need to keep the client socket open */
close(socket_client);
}
return 0;
}