Cogs and Levers A blog full of technical stuff

Unix Networking: Client

The following snippet outlines the skeleton of a client socket application. It’s the bare bones of what is needed to establish the client connection.

int sockfd, numbytes;
char buf[MAXDATASIZE];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];

/* fill out the address info */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

/* create the actual server address */
getaddrinfo("www.remoteplace.com", PORT, &hints, &servinfo);

/* create the socket file descriptor */
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);

/* perform the network connect */
connect(sockfd, p->ai_addr, p->ai_addrlen);

/* free up the addres structure */
freeaddrinfo(servinfo);

/* receive some data on the socket */
recv(sockfd, buf, MAXDATASIZE-1, 0));

/* close the socket off */
close(sockfd);

###Further reading The getaddrinfo system call The socket system call The connect system call The recv system call The send system call

Using the GNU Debugger

Bundled with the GNU toolset is a full featured debugger in GDB. This blog post aims to be a cheatsheet to get yourself around this tool quickly.

The example program

int main(int argc, char *argv[]) {
  /* some local variables */
  int x = 1, y = 2, z = 0;

  /* something that will go bad */
  int ans = x / z;

  return 0;
}

Compile your code with debug info

You need to use the -g switch with GCC in order to compile debug symbols. This helps the feedback that GDB will give you a great deal.

$ gcc -g test.c -o test

Loading your program into the debugger

So, in our example above the name of our executable would be “test”. So, we just tell gdb to load test by passing it in.

$ gdb test

You can even start gdb with a text user interface, so I’ve just discovered.

$ gdb test -tui

It looks like this - not bad, eh?

GDB Screenshot

Now we instruct gdb that we’d like to run the application

(gdb) run
Starting program: /home/michael/Development/gdb-tute/test 
Program received signal SIGFPE, Arithmetic exception.
0x00000000004004c3 in main (argc=1, argv=0x7fffffffe4d8) at test.c:9
9    ans = x / z;

So you can see here that dividing by zero wasn’t the best career move for this program. But It nicely let us know that there was an arithmetic exception on line 9!

Examining data

To get just the call stack information, we issue the “backtrace” instruction

(gdb) backtrace
#0  0x00000000004004c3 in main (argc=1, argv=0x7fffffffe4d8) at test.c:9

Examining data with print. We can view the value of variables by passing them to the print statement.

(gdb) print x
$1 = 1
(gdb) print y
$2 = 2
(gdb) print z
$3 = 0

Print also supports printf style specifiers. Examining data at an address. We can view the value of data at a memory location by using the x command.

(gdb) print &z
$4 = (int *) 0x7fffffffe3e8
(gdb) x 0x7fffffffe3e8
0x7fffffffe3e8: 0x00000000

Setting variables while attached is done with set. We can stop the error in the program at runtime using this mixed with a breakpoint.

Breakpoint 1, main (argc=1, argv=0x7fffffffe4d8) at test.c:6
6    int x = 1, y = 2, z = 0;
(gdb) next
7    int ans = 0;
(gdb) set (z = 5)
(gdb) print z
$1 = 5

Working with breakpoints

To set a breakpoint in gdb, just pass the name of what you’d like to break on to the break command.

(gdb) break main
Breakpoint 1 at 0x40049f: file test.c, line 6.
(gdb) run
Starting program: /home/michael/Development/gdb-tute/test 
Breakpoint 1, main (argc=1, argv=0x7fffffffe4d8) at test.c:6
6    int x = 1, y = 2, z = 0;

You can set a breakpoint at a specific source code line number:

(gdb) break 7
Breakpoint 1 at 0x4004b4: file test.c, line 7.

Finally, you can make a breakpoint using a combination of source code file name and line location:

(gdb) break test.c:7
Breakpoint 3 at 0x4004b4: file test.c, line 7.

View your existing breakpoints with info breakpoints

(gdb) info breakpoints
Num     Type           Disp Enb Address            What
3       breakpoint     keep y   0x00000000004004b4 in main at test.c:7

You can clear any set breakpoint with clear.

(gdb) clear main
Deleted breakpoint 1

You can step into code by issuing the step command

(gdb) step
7    int ans = 0;
(gdb) 
9    ans = x / z;
(gdb) 
Program received signal SIGFPE, Arithmetic exception.
0x00000000004004c3 in main (argc=1, argv=0x7fffffffe4d8) at test.c:9
9    ans = x / z;

You step over code by issuing next. Its usage is the same as step.

That’s all for now. As I come across other useful bits from GDB, I’ll certainly post them here. The items above have been lifesavers for me from time to time.

Unix IPC: Working with Signals

Incoming messages to your process can come in the form of a signal. Signals are standard message packets that the operating system will use to tell your process something about the environment.

This snippet will show you show to setup a signal handler in your program.

/** The signal handler */
void sigint_handler(int sig) {
	/* do something interesting with the sigint here */
}

int main(void) {
	struct sigaction sa;

	/* fill out the signal structure */
	sa.sa_handler = sigint_handler;
	sa.sa_flags   = 0;
	sigemptyset(&sa.sa_mask);

	/* assign it to the appropriate signal */
	sigaction(SIGINT, &sa, NULL);

	/* do other stuff here, sigint will be handled */
}

Further reading

Unix IPC: Unix Sockets

Unix sockets talk locally on the machine. Their operation is very similar to standard socket operations (as they are the same in every way). This snippet will show you a server and client.

int s, s2, t, len;
struct sockaddr_un local, remote;
char str[100];

/* --- as a server --- */

/* create the socket to serve on */
s = socket(AF_UNIX, SOCK_STREAM, 0);
/* fill out the address struct to listen on */
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCK_PATH);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);

/* bind the socket to this name */
bind(s, (struct sockaddr *)&local, len);
/* listen on this socket */
listen(s, 5);
/* accept any incoming connection */
s2 = accept(s, (struct sockaddr *)&remote, &t));
/* receive some data */
n = recv(s2, str, 100, 0);
/* close the client */
close(s2);


/* --- as a client --- */

/* create a socket to connect on */
s = socket(AF_UNIX, SOCK_STREAM, 0);

/* fill out the address struct to connect to */
remote.sun_family = AF_UNIX;
strcpy(remote.sun_path, SOCK_PATH);
len = strlen(remote.sun_path) + sizeof(remote.sun_family);

/* connect on this socket */
connect(s, (struct sockaddr *)&remote, len);

/* send some data */
send(s, str, strlen(str), 0);
/* close the socket */
close(s);

Further reading

Unix IPC: Shared Memory

Shared memory allows multiple processes to view, modify and control shared segments of memory. This snippet will show you how to obtain a pointer to some shared memory and then release the pointer.

key_t key;
int shmid;

/* get an ipc key */
key = ftok("filename", 'R');

/* connect to the segment */
shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT);

/* attach to the segment */
data = shmat(shmid, (void *)0, 0);

/* perform writes and reads on the "data" pointer */

/* detach from the segment */
shmdt(data);

Further reading