TCP Client IPv4/IPv6
12. November 2012 04:24

Eine Demonstration, wie man unter Linux Sockets mit Dualstack nutzt.

                    /*
 	Small, simple tcp client

	-Using epoll
	-Fairly protocol independent (IPv4 + IPv6 support)
	-With resolver function

	August 2010 tuxwave.net
	Sebastian Kricner
 */

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <errno.h>

void client(char *host, char *port, int mode)
{
	int error, count, epollfd, statechanged, sock_fd, flags, result, on = 1;
	struct sockaddr *sa;
	socklen_t salen;
	struct addrinfo hints;
	struct addrinfo *res;
        struct epoll_event ev[2], *events;
	char *msg;
	msg = malloc(sizeof(char)*64);
	memset(&hints, 0, sizeof(hints));

	epollfd = epoll_create(2);
        memset(ev, 0, sizeof(ev));
        events = malloc(sizeof(struct epoll_event));
	memset(events, 0, sizeof(struct epoll_event));

	if(mode == 4)
	{
		hints.ai_family = PF_INET;
	}
	else if(mode == 6)
	{
		hints.ai_family = PF_INET6;
	}
	else
	{
		hints.ai_family = PF_UNSPEC;
	}
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	error = getaddrinfo(host, port, &hints, &res);
	if(error)
	{
		fprintf(stderr, "%s\n", gai_strerror(error));
		exit(EXIT_FAILURE);
	}
	else
	{
		while(res)
		{
			sa = res->ai_addr;
			salen = res->ai_addrlen;
			char *hostname = malloc(sizeof(char)*512);
			char *ipaddr = malloc(sizeof(char)*res->ai_addrlen+1);
			error = getnameinfo(sa, salen, hostname, 512, NULL, 0, 0); 
			if(error)
			{
				fprintf(stderr, "%s\n", gai_strerror(error));
				freeaddrinfo(res);
				exit(EXIT_FAILURE);
			}
			switch (res->ai_family)
			{
				case AF_INET:
					if((inet_ntop(res->ai_family, res->ai_addr->sa_data + 2, ipaddr, res->ai_addrlen)) == NULL)
					{
						perror("getaddrinfo");
						freeaddrinfo(res);
						exit(EXIT_FAILURE);
					}
					break;
				case AF_INET6:
					if((inet_ntop(res->ai_family, res->ai_addr->sa_data + 6, ipaddr, res->ai_addrlen)) == NULL)
					{
						perror("getaddrinfo");
						freeaddrinfo(res);
						exit(EXIT_FAILURE);
					}
					break;
			}
			if(mode == 9)
			{
				printf("Hostname: %s Address: %s\n", hostname, ipaddr);
				printf("-----------------------------------------------------------\n");
				res = res->ai_next;
				continue;
			}
			printf("Connecting to %s [%s] on port %s...\n", hostname, ipaddr, port); 
			sock_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
			if(sock_fd == -1)
			{
				perror("Socket error");
				freeaddrinfo(res);
				exit(EXIT_FAILURE);
			}
			error = connect(sock_fd, sa, salen);
			if(error != 0)
			{
				perror("Connect error");
				freeaddrinfo(res);
				exit(EXIT_FAILURE);
			}
			printf("->Connected<-\n");
			
			error = ioctl(sock_fd, FIONBIO, (char *)&on);
			if(error < 0)
			{
				perror("ioctl failed");
				close(sock_fd);
				freeaddrinfo(res);
				exit(EXIT_FAILURE);
			}

			ev[0].events = EPOLLIN;
			ev[0].data.fd = sock_fd;
			ev[1].events = EPOLLIN;
			ev[1].data.fd = 0;
			if((epoll_ctl(epollfd, EPOLL_CTL_ADD, sock_fd, &ev[0])) == -1)
			{
				perror("epoll");
				close(sock_fd);
				freeaddrinfo(res);
				exit(EXIT_FAILURE);
			}
			if((epoll_ctl(epollfd, EPOLL_CTL_ADD, 0, &ev[1])) == -1)
			{
				perror("epoll");
				close(sock_fd);
				freeaddrinfo(res);
				exit(EXIT_FAILURE);
			}
			while(1)
			{
				statechanged = epoll_wait(epollfd, events, 1, -1);				
				for(count = 0;count < statechanged;count++)
				{
					int fd = events[count].data.fd;
					if(fd == sock_fd)
					{
						while(1)
						{
							memset(msg, 0, sizeof(char)*64);
							result = recv(sock_fd, msg, sizeof(char)*64, 0);
							if(result < 0)
							{
								if(errno != EWOULDBLOCK)
								{
									perror("recv failed");
									close(sock_fd);
									freeaddrinfo(res);
									exit(EXIT_FAILURE);
								}
								break;
							}
							if(result == 0)
							{
								printf("->Connection closed<-\n");
								close(sock_fd);
								freeaddrinfo(res);
								exit(EXIT_SUCCESS);
							}
							printf("%s", msg);
						}
					}
					else
					{
						memset(msg, 0, sizeof(char)*64);
						if((fgets(msg, sizeof(char)*64, stdin)) != NULL)
						{
							if(strlen(msg) == 1)
							{
								msg[0] = 0x0D;
								msg[1] = 0x0A;
							}
							error = send(sock_fd, msg, strlen(msg), 0);
							if(error == -1)
							{
								perror("Send error");
								close(sock_fd);
								freeaddrinfo(res);
								exit(EXIT_FAILURE);
							}
						}
					}
				}
			}
			close(sock_fd);
			res = res->ai_next;
		}
	}
	freeaddrinfo(res);
}

int main(int argc, char *argv[], char *envp[])
{
	int option, mode = 0;
	char *hostname, *service;

	if(argc < 3)
	{
		printf("Usage: %s {flags} [host] [port]\n", argv[0]);
		printf("Flags:\n"
		"-4	IPv4 only\n"
		"-6	IPv6 only\n"
		"-d	just resolve\n");
		exit(EXIT_FAILURE);
	}
	while((option = getopt(argc, argv, "46d")) != -1)
	{
		switch(option)
		{
			case '4':
				mode = 4;
				break;
			case '6':
				mode = 6;
				break;
			case 'd':
				mode = 9;
				break;
		}
	}
	if(argc-optind == 2)
	{
		client(argv[optind], argv[optind+1], mode);
	}
	else if(argc-optind == 1 && mode == 9)
	{
		client(argv[optind], (char *)0, mode);
	}
	else
	{
		fprintf(stderr, "Invalid arguments\n");
		exit(EXIT_FAILURE);
	}
}


                    
        
Download