#include #include #include #include #include #include #include #include "example.h" union sock { struct sockaddr s; struct sockaddr_in i; }; const char usage[] = "Usage: server [-h] [-p ]\n"; int port = SERVER_PORT; char client_ip[16]; void print_usage(void) { printf("%s",usage); exit(EXIT_FAILURE); } void get_options(int argc, char *argv[]) { int c; /* read command line options */ /* "hp:" means -h and -p */ while ( (c=getopt(argc,argv,"hp:")) != -1 ) { switch(c) { case 'p': port = atoi(optarg); if ( port <= 0 ) { print_usage(); } break; case 'h': default: print_usage(); } } } int create_server_socket(int port) { struct sockaddr_in serv_addr; int serv_sock; int opt = 1; /* Creation of server socket usually follows these 3 steps 1) make socket / create endpoint of communication 2) bind a name (address) to a socket 3) listen for incoming socket connections */ memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = PF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(port); /* make TCP socket */ /* PF_INET : IP protocol family For more options, check out /usr/include/bits/socket.h (e.g. PF_UNIX for unix domain socket) SOCK_STREAM : sequenced, reliable connection (e.g. SOCK_DGRAM for connectionless, unreliable connection) IPPROTO_TCP : Transmission Control Protocol/TCP (e.g. IPPROTO_UDP, IPPROTO_RSVP, etc) check out /usr/include/netinet/in.h /usr/include/linux/in.h */ if ((serv_sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1 ) { perror("socket"); return -1; } /* set port as reusable */ /* Port may not be usable if it's not closed properly (e.g. segfault, kill process) Specifies that the rules used in validating addresses supplied to bind() should allow reuse of local addresses */ if (setsockopt(serv_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)) == -1 ) { perror("setsockopt"); return -1; } /* bind a name (server_addr) to a socket (serv_sock) When a socket is created with socket(), it exists in a name space (address family) but has no name assigned. It is normally necessary to assign a local address using bind before a SOCK_STREAM socket may receive connections (accept()). */ if (bind(serv_sock,(struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1 ) { perror("bind"); return -1; } /* listen for socket connections and limit the queue of incoming */ if (listen(serv_sock,MAXPENDING) == -1 ) { perror("listen"); return -1; } return serv_sock; } void read_client_ip(int sock) { union sock client; int client_len; client_len = sizeof(struct sockaddr); /* get the name of the peer socket */ getpeername(sock,&(client.s),(socklen_t *)&client_len); /* inet_ntoa() convert the Internet host address to a string in the Internet standard dot notation. */ strncpy(client_ip,inet_ntoa(client.i.sin_addr),16); } int main(int argc, char *argv[]) { int server_sock, client_sock; int recv_size,sent_size; struct sockaddr_in c_addr; unsigned int c_len = sizeof(c_addr); char buffer[BUFF_SIZE]; get_options(argc,argv); server_sock = create_server_socket(port); /* waiting on a client connection */ /* The accept function is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET and SOCK_RDM). It extracts the first connection request on the queue of pending connections, creates a new connected socket with mostly the same properties as s, and allocates a new file descriptor for the socket, which is returned. */ if ( (client_sock=accept(server_sock,(struct sockaddr *)&c_addr,&c_len)) == -1 ) { perror("accept"); return EXIT_FAILURE; } read_client_ip(client_sock); /* 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(client_sock,buffer,BUFF_SIZE,0); if ( recv_size == -1 ) { perror("recv"); exit(EXIT_FAILURE); } printf("Message received from %s\n\t%s\n",client_ip,buffer); /* send a message from a socket */ /* Usually, recv/send doesn't fail, but always needs to check (good programming habit) */ sent_size = send(client_sock,SERVER_MSG, strlen(SERVER_MSG)+1, 0); if ( sent_size == -1 ) { perror("send"); exit(EXIT_FAILURE); } printf("Message sent to %s\n\t%s\n",client_ip,SERVER_MSG); /* 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(client_sock); close(server_sock); return EXIT_SUCCESS; }