summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKyle K <kylek389@gmail.com>2011-08-29 20:19:30 -0500
committerKamil Kaminski <kamilkss@gmail.com>2011-08-29 20:19:30 -0500
commite99c498e79fc971bda62803375a025242b68335f (patch)
tree5d6841b75848977764849c7b9495e4f7e5abfe61
parent6161b42693193db3eadf9ebf12d5976f8acdd54c (diff)
downloadsandbox-e99c498e79fc971bda62803375a025242b68335f.tar.gz
sandbox-e99c498e79fc971bda62803375a025242b68335f.tar.bz2
sandbox-e99c498e79fc971bda62803375a025242b68335f.zip
sockets: add a tcp client example
-rw-r--r--Makefile2
-rw-r--r--tcpclient.c222
2 files changed, 223 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 686a8d7..9ff95e4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
BINS = ascii class depipe_strings dup fpipe pipe realloc strpbrk strsep \
tokenizer getopt prime_mask linked_list pi_bbp string_tokenizer \
- tcpserver
+ tcpserver tcpclient
CC = gcc
CFLAGS = -Wall -std=gnu99 -pedantic
DBGFLAGS = -g -O0
diff --git a/tcpclient.c b/tcpclient.c
new file mode 100644
index 0000000..3410b97
--- /dev/null
+++ b/tcpclient.c
@@ -0,0 +1,222 @@
+/* tcpclient.c
+ *
+ * TCP Client
+ * note: 'echo "hello" | nc -l -p 8080 localhost'
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#define TARGET_MAX_SZ 1*1024*1024
+
+static int sockfd;
+static struct addrinfo *res;
+static char *recv_buff;
+
+void cleanup(void)
+{
+ shutdown(sockfd, SHUT_RDWR);
+ close(sockfd);
+
+ if (recv_buff)
+ free(recv_buff);
+
+ /* free the linked list */
+ if (res)
+ freeaddrinfo(res);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 2)
+ {
+ fprintf(stderr, "usage: %s <host>\n", argv[0]);
+ exit(-1);
+ }
+ if (!strstr(argv[1], "http://"))
+ {
+ fprintf(stderr, "host needs to prefixed with \"http://\" protocol\n");
+ exit(-1);
+ }
+
+ /* grab the host and path from the URL */
+ char hoststr[100] = { 0 }, pathstr[100] = { 0 }, filestr[100] = { 0 };
+ sscanf(argv[1], "http://%[^/]/%s", hoststr, pathstr);
+ printf("host is: \"%s\"\n"
+ "path is: \"%s\"\n", hoststr, pathstr);
+
+ /* parse out a filestrname */
+ char *filepart = strrchr(pathstr, '/');
+ if (filepart && strlen(filepart + 1))
+ strcpy(filestr, filepart + 1);
+ else
+ strcpy(filestr, "index.html");
+ printf("file is: \"%s\"\n", filestr);
+
+ int ret;
+
+ /* prepare for getaddrinfo call */
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ if ((ret = getaddrinfo(hoststr, "http", &hints, &res)) != 0)
+ {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
+ exit(-1);
+ }
+
+ /* try all available IP addresses */
+ struct addrinfo *p;
+ char ipstr[INET6_ADDRSTRLEN];
+ void *addr;
+ char *ipver;
+ struct sockaddr_in *ipv4;
+#if 0
+ struct sockaddr_in6 *ipv6;
+#endif
+
+ printf("trying all available IP addresses for %s:\n", hoststr);
+ for (p = res; p != NULL; p = p->ai_next)
+ {
+ /* get the pointer to the address itself,
+ * different fields in IPv4 and IPv6 */
+ if (p->ai_family == AF_INET)
+ { /* IPv4 */
+ ipv4 = (struct sockaddr_in *) p->ai_addr;
+ addr = &(ipv4->sin_addr);
+ ipver = "IPv4";
+ }
+ else
+ continue; /* ignore IPv6 for now */
+#if 0
+ else
+ { /* IPv6 */
+ ipv6 = (struct sockaddr_in6 *) p->ai_addr;
+ addr = &(ipv6->sin6_addr);
+ ipver = "IPv6";
+ }
+#endif
+
+ /* convert the IP to a string and print it */
+ inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr));
+ printf("attempting to connect to %s: %s\n", ipstr, ipver);
+
+ /* create a socket */
+ if ((sockfd = socket(p->ai_family, p->ai_socktype, res->ai_protocol)) == -1)
+ {
+ perror("socket");
+ close(sockfd);
+ }
+
+ /* attempt to connect */
+ if (connect(sockfd, (struct sockaddr *) ipv4, sizeof(*ipv4)) == -1)
+ perror("connect");
+ else
+ {
+ printf("successfully connected\n");
+ break; /* break out of a loop, we got a connection */
+ }
+ }
+
+ if (!p)
+ {
+ fprintf(stderr, "failed to connect to a host\n");
+ exit(-1);
+ }
+
+ atexit(cleanup);
+
+ /* send http commands and receive data */
+ char request[128] = { 0 };
+ sprintf(request, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", pathstr, hoststr);
+ size_t request_len = strlen(request) * sizeof(char);
+
+ ret = send(sockfd, (void *) request, request_len, 0);
+ if (ret == -1)
+ {
+ perror("send");
+ exit(-1);
+ }
+ else if (ret != request_len)
+ {
+ fprintf(stderr, "sent less bytes than requested\n");
+ exit(-1);
+ }
+
+ /* receive until connection gets closed */
+ recv_buff = (char *) malloc(sizeof(char) * TARGET_MAX_SZ);
+ if (!recv_buff)
+ {
+ perror("malloc");
+ exit(-1);
+ }
+ size_t recv_len = 0;
+
+ do
+ {
+ ret = recv(sockfd, recv_buff + recv_len, TARGET_MAX_SZ - recv_len, 0);
+ if (ret == -1)
+ {
+ perror("recv");
+ exit(-1);
+ }
+ else if (ret == 0)
+ printf("remote host has closed the connection, received %lu bytes\n", recv_len);
+
+ recv_len += ret;
+
+ } while (ret > 0);
+
+ if (recv_len < 1)
+ {
+ fprintf(stderr, "did not receive any data from the host!\n");
+ exit(-1);
+ }
+
+ /* poke at http header and check for errors */
+ int code;
+ sscanf(recv_buff, "HTTP/1.%*[01] %d ", &code);
+ if (code == 200)
+ printf("received file\n");
+ else
+ {
+ printf("got an error code %d\n", code);
+ char errorline[100];
+ sscanf(recv_buff, "%[^\r\n]", errorline);
+ fprintf(stderr, "%s\n", errorline);
+ exit(-1);
+ }
+
+ /* save to disk */
+ char *buff_p = strstr(recv_buff, "\r\n\r\n"); /* seek past http header */
+ if (buff_p)
+ {
+ buff_p += 4;
+
+ FILE *fp = fopen(filestr, "w");
+ if (!fp)
+ {
+ perror("fopen");
+ exit(-1);
+ }
+
+ ret = fwrite(buff_p, recv_len - (buff_p - recv_buff), sizeof(char), fp);
+ fclose(fp);
+ }
+ else
+ fprintf(stderr, "could not seek past http header\n");
+}
+