#include "example.h" #include #include #include #include #include #include const char usage[] = "Usage: client [-h] [-p ] [-s ]\n"; char *server_addr = NULL; int server_port = SERVER_PORT; void print_usage(void) { printf("%s",usage); exit(EXIT_FAILURE); } void read_options(int argc, char *argv[]) { int c; /* read command line options */ while ( (c=getopt(argc,argv,"hp:s:")) != -1 ) { switch(c) { case 'p': server_port = atoi(optarg); if ( server_port <= 0 ) { print_usage(); } break; case 's': server_addr = optarg; break; case 'h': default: print_usage(); } } if ( server_addr == NULL ) { print_usage(); } } int connect_to(char *host, int port) { int sock; struct addrinfo hint; struct addrinfo *addr; char port_str[8]; /* making connection from a client side is simpler than a server side setup. It follows 2 steps; 1) make socket (socket) 2) make connection (connect) */ memset(&hint, 0, sizeof(hint)); hint.ai_family = PF_INET; hint.ai_socktype = SOCK_STREAM; snprintf(port_str,8,"%d",port); /* First, need to find out network address with a given host name. host can be any form such as host name - tokyo.cc.gatech.edu or dotted decimal notation - 192.168.1.1. For a descriptive name, gethostbyname() or gethostbyname_r() can be used. The behavior of gethostbyname() when passed a numeric address string is unspecified. For a dotted notation, inet_addr() can be used. Since the return value from above 2 system calls are different, it should be handled in a different way. In this example, getaddrinfo() is used, and it simply handles both cases. */ /* The getaddrinfo() function shall translate the name of a service loca- tion (for example, a host name) and/or a service name and shall return a set of socket addresses and associated information to be used in creating a socket with which to address the specified service. */ getaddrinfo(host, port_str, &hint, &addr); /* Second, make socket (use addrinfo set by getaddrinfo() ) It can be simply socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) for tcp/ip connection. */ if((sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) == -1) { perror("socket"); return -1; } /* Third, connect a socket Since, getaddrinfo() sets what we need, use them here. For example, if gethostbyname() is used to resolve network address, you might use a different address here. */ if(connect(sock, addr->ai_addr, addr->ai_addrlen) == -1 ) { perror("connect"); return -1; } free(addr); return sock; } int main(int argc, char *argv[]) { int socket; int sent_size, recv_size; char buffer[BUFF_SIZE]; read_options(argc,argv); socket = connect_to(server_addr, server_port); if ( socket <= 0 ) { return EXIT_FAILURE; } /* send a message from a socket */ /* Usually, recv/send doesn't fail, but always needs to check (good programming habit) */ sent_size = send(socket,CLIENT_MSG,strlen(CLIENT_MSG)+1,0); if ( sent_size == -1 ) { perror("send"); exit(EXIT_FAILURE); } printf("Message sent to %s\n\t%s\n",server_addr,CLIENT_MSG); /* receive a message from a connected socket */ /* buffer for the message needs to be provided . It returns the length of the message written to the buffer unless there is an error. By default, it's blocking call. If interested, check out fcntl(), O_NONBLOCK, and EAGAIN return value. */ recv_size = recv(socket,buffer,BUFF_SIZE,0); if ( recv_size == -1 ) { perror("recv"); exit(EXIT_FAILURE); } printf("Message received from %s\n\t%s\n",server_addr,buffer); /* close sockets */ /* not really necessary since the program terminates, but it's good habit. Same for file descriptors and sockets. Also, there's a limitation for such descriptors that a user can open. Simply, you cannot open file/make socket above the limitation. */ close(socket); return EXIT_SUCCESS; }