Source code without comments here: client.c.
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv)
{
	int ret = 0;

	argc--, argv++;

	if (!argc) {
		puts("Number of args is wrong. At least one is needed (network "
			"name or address)");
		ret = 1;
		goto out;
Somebody wrote somewhere, that using of goto makes code unclean and causes its worse readability. He missed. There is one case, when goto can help you in that ways. If programmer needs to cope with some things, like freeing memory, calling functions, which closes descriptors etc., he'll use goto in that way, as you can see here.
	}

	struct protoent *pe = getprotobyname("tcp");
The number of protocol listed in /etc/protocols will appear in structure, on which pe points. We will use this number, when we initialize ai.
	struct addrinfo *out, ai = {0, PF_UNSPEC, SOCK_STREAM, pe->p_proto, };
We can't cut cold the comma before }. Yes, indeed, gcc compiles it. The main feature is, that elements which are not in {…} will be set to 0.
We could also explicitly write, which elements should be initialized: { .ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = pe->p_proto }
And now is the time to call IPv6-ready function, which give us pointer to structure holding informations about address, which the client will try to contact:
	if (getaddrinfo(argv[0], argv[1] ? : "10001", &ai, &out)) {
And you ask: „WTF did I writet there, ? :?” And I answer: „Yes, indeed, this gcc compiles and when user give us 2. parameter, it'll be used.” In other case will be used impicit 10001.
		perror("getaddrinfo error");
getaddrinfo, like other bazillion functions stores its error into errno, so we can call perror, which catenate after parameter string ›: ‹ and string containing info about errno error.
		ret = 100;
		goto out;
	}

	switch (out->ai_addr->sa_family) {
All this mummery is only for users – ii writes protocol, ip and port, on which will client connect.
	case AF_INET: {
Hack, which makes gcc4 compiles declaration after case.
		struct sockaddr_in *in =
			(struct sockaddr_in*)out->ai_addr;
			printf("ipv4: %s, %d\n", inet_ntoa(in->sin_addr),
			ntohs(in->sin_port));
		break;
	} case AF_INET6: {
		struct sockaddr_in6 *in6 =
			(struct sockaddr_in6*)out->ai_addr;
		char buf[INET6_ADDRSTRLEN];
		inet_ntop(AF_INET6, (void*)&in6->sin6_addr, buf,
			sizeof(buf));
		printf("ipv6: %s, %d\n", buf, ntohs(in6->sin6_port));
		break;
	} default:
		puts("unknown address class");
		ret = 2;
		goto frout;
	}

	int fd = socket(out->ai_addr->sa_family, SOCK_STREAM, pe->p_proto);
	if (fd < 0) {
		perror("socket error");
		ret = 3;
		goto frout;
	}

	if (connect(fd, out->ai_addr, out->ai_addrlen)) {
		perror("connect error");
		goto clout;
	}


	int len;
	char buf[128];
	struct timeval tv = {5, 0};
	fd_set fs;

	FD_ZERO(&fs);
	FD_SET(0, &fs);
	FD_SET(fd, &fs);
We need to take care of 2 descriptors simultaneously (standard input and the connection), so we have to use some mechanism to deal with that, if there are no data to read from some of them. Here won select.

	while (select(fd + 1, &fs, NULL, NULL, &tv) >= 0) {
		if (FD_ISSET(0, &fs)) {
			if ((len = read(0, buf, sizeof(buf))))
( and ) we write, because gcc checks = and ==, if you don't wite 1, it thinks, that you made mistake and writes warning.
				send(fd, buf, len, 0);
			else
				goto clout;
User pressed EOF.
		}

		if (FD_ISSET(fd, &fs)) {
			if ((len = recv(fd, buf, sizeof(buf), 0)))
				write(0, buf, len);
			else {
				puts("Server ends the relation");
				goto clout;
			}
		}

		FD_ZERO(&fs);
		FD_SET(0, &fs);
		FD_SET(fd, &fs);
		tv.tv_sec = 5;
		tv.tv_usec = 0;
	}

	perror("select error");

clout:
	close(fd);

frout:	
	freeaddrinfo(out);

out:
	return ret;
}