#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <poll.h> #define TIMEOUT_SECS 2 #define FORMAT "OPTIONS * HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n" #define MESSAGE_LEN 128 #define MAXLINE 1024 #define POLL_TIMEOUT_MSECS 20 char message[MESSAGE_LEN]; char line[MAXLINE]; int message_len; int message_pos=0; typedef enum { initial, connecting, writing, reading, done, error } connect_state; connect_state state; /** * Set the timeout on a socket * @param sockfd the socket file descriptor * @param secs the number of seconds to timeout after * @return 1 if it worked, else 0 */ static int socket_set_timeout( int sockfd, int secs ) { int res; struct timeval tv; tv.tv_sec = secs; tv.tv_usec = 0; res = setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv,sizeof(tv) ); if ( res == -1 ) printf("failed to set timeout on socket\n"); return res==0; } /** * Write n bytes * @param fd the descriptor to write to * @param vptr the data to write * @param n its length * @return -1 on error, else number of bytes written */ static ssize_t writen( int fd, const void *vptr, size_t n ) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while ( nleft > 0 ) { //printf("about to call write with nleft=%d\n",(int)nleft); if ((nwritten = write(fd,ptr,nleft)) <= 0 ) { //printf("nwritten=%d errno=%d\n",(int)nwritten,errno); if ( errno == EINTR ) nwritten = 0; else return -1; } nleft -= nwritten; ptr += nwritten; } return n; } /** * Read from a socket * @param sock the socket to read from * @return number of bytes read or -1 on error */ static int readn( int sock ) { int n,total = 0; for ( ; ; ) { n=read( sock, line, MAXLINE ); if ( n < 0 ) { total = -1; printf( "failed to read. err=%s socket=%d\n",strerror(errno),sock); break; } else if ( n == 0 ) { // just finished reading break; } else total += n; } return total; } /** * Connect the given socket to the host and its destination port * @param sock the socket to connect * @param host the host ip adddress * @param port the host port * @return 0 if it fails, else 1 */ int do_connect( int sock, char *host, char *port ) { struct sockaddr_in addr; /* clear addr structure first */ memset( &addr, 0, sizeof(addr) ); /* reuse addr structure to connect to host and port */ int res = inet_pton( AF_INET,host,&addr.sin_addr); if ( res == 1 ) { /* port number must be in network byte order */ addr.sin_port = htons(atoi(port)); /* establish TCP connection via handshake (SYN,SYN-ACK,ACK) */ res = connect(sock,(const struct sockaddr *)&addr, sizeof(addr)); if ( res == 0 ) { printf("connected successfully to %s on port %s\n",host,port); return 1; } else if ( res == -1 ) { if ( errno == EINTR || errno == EINPROGRESS ) { state = connecting; return 0; } else { state = error; printf("couldn't connect to %s on port %s\n",host,port); } } } else printf("inet_pton failed: %s\n",strerror(errno) ); state = error; return 0; } /** * In non-blocking mode we must poll the socket to see if the connection * is established * @param sock the socket to test for connection * @return 1 if it is now connected, else 0 */ int try_connect( int sock ) { struct pollfd fds[1]; fds[0].fd = sock; fds[0].events = POLLWRBAND | POLLOUT; int res = poll( fds, 1, POLL_TIMEOUT_MSECS ); if ( res == 1 ) { return 1; } else if ( res == -1 ) { state = error; } return 0; } /** * Set up and bind a socket to a local ip and port * @param blocking use the non-blocking mode * @param localip the local ip address to bind to * @return the bound socket if successful, else -1 */ int tcp_bind( int blocking, char *localip ) { /* create an IPv4 TCP socket */ int sock = socket( AF_INET, SOCK_STREAM, 0 ); if ( sock != -1 ) { /* bind socket to localhost */ struct sockaddr_in addr; memset( &addr, 0, sizeof(addr) ); int res=0,flags; if ( !blocking ) { /* get existing socket flags */ flags = fcntl (sock, F_GETFL, 0 ); /* switch socket to non-blocking mode */ res = fcntl( sock, F_SETFL, flags | O_NONBLOCK ); } if ( res != -1 ) { /* set timeout for read/write */ if ( !socket_set_timeout(sock,TIMEOUT_SECS) ) { close( sock ); return -1; } addr.sin_family = AF_INET; /* use a random port as the socket's source port */ addr.sin_port = 0; /* load the address of localhost as the socket's source address */ int res = inet_pton( AF_INET, localip, &addr.sin_addr ); if ( res != 1 ) printf("inet_pton error %s\n",strerror(errno)); else { res = bind( sock, (const struct sockaddr *)&addr,sizeof(addr)); if ( res != -1 ) { printf("bound socket %d to %s \n",sock,localip); return sock; } else printf("failed to bind sock %d to %s. err=%s\n", sock,localip,strerror(errno)); } close( sock ); printf( "closed b-socket %d on error\n",sock); } else printf("failed to set socket to non-blocking mode\n"); } else printf("couldn't open nb-socket. error=%s\n",strerror(errno)); return -1; } /** * Send a blocking message to the dest_ip from localhost to the * given dest_ip and dest_port * @param argv program name, dest-ip and dest-port * @return 1 on success, else 0 */ int sendn( char **argv ) { int res = 0; int sock = tcp_bind( 1, "127.0.0.1" ); if ( sock != -1 ) { if ( do_connect(sock,argv[1],argv[2]) ) { /* write message to sock */ int sent = writen( sock, message, message_len ); if ( sent == message_len ) { printf( "successfully sent %d bytes to %s, port %s\n", sent,argv[1],argv[2]); int rcvd = readn( sock ); if ( rcvd > 0 ) { printf("received %d bytes from %s\n",rcvd,argv[1]); res = 1; } else printf("failed to read anything or error\n"); } else printf("tried to send %d via blocking socket but only sent %d\n", message_len,sent); } close( sock ); } return res; } /** * Test for writing and if possible, write */ int try_writen( int sock ) { ssize_t nwritten; struct pollfd fds[1]; fds[0].events |= POLLOUT; fds[0].events |= POLLWRBAND; int res = poll(fds, 1, POLL_TIMEOUT_MSECS); if ( res > 0 ) { int nleft = message_len-message_pos; while ( nleft > 0 ) { if ((nwritten = write(sock,&message[message_pos],nleft)) <= 0 ) { if ( errno != EINTR || errno != EAGAIN ) state = error; return 0; } nleft -= nwritten; if ( nleft > 0 ) message_pos += nwritten; else { message_pos = 0; return 1; } } } else if ( res < 0 ) { printf( "error: %s\n", strerror(errno) ); state = error; } return 0; } /** * Test for reading on socket and if you can, reading * @param sock the socket to read from */ int try_readn( int sock ) { int n,total = 0; struct pollfd fds[1]; fds[0].events |= POLLIN; fds[0].events |= POLLPRI; int res = poll(fds, 1, POLL_TIMEOUT_MSECS); if ( res > 0 ) { for ( ; ; ) { n=read( sock, line, MAXLINE ); if ( n < 0 ) { if ( errno != EINTR || errno != EAGAIN ) { state = error; printf("error: %s\n",strerror(errno)); } return 0; } else if ( n == 0 ) { // just finished reading break; } else total += n; } } else if ( res < 0 ) { printf("error: %s\n",strerror(errno)); state = error; } return total; } /** * Send non-blocking * @param argv the argument list */ int sendnb( char **argv ) { int res; int sock = tcp_bind( 0, "127.0.0.1" ); if ( sock != -1 ) { do { switch ( state ) { case initial: res = do_connect( sock, argv[1], argv[2] ); if ( res ) state = writing; break; case connecting: res = try_connect( sock ); if ( res ) state = writing; break; case writing: res = try_writen( sock ); if ( res ) state = reading; break; case reading: res = try_readn( sock ); if ( res ) state = done; break; } } while ( state != done && state != error ); close( sock ); if ( state == done ) return 1; } return 0; } /* * test program to open a blocking socket, then close it * and reopen it on the same ip as non-blocking. Each time * send and receive data */ int main( int argc, char **argv ) { int res = 0; if ( argc != 3 ) { printf("usage: ./socktest dest_ip port\n"); exit(0); } state = initial; message_len = snprintf( message, MESSAGE_LEN, FORMAT, argv[1] ); if ( message_len > 0 ) { /* send in blocking and non-blocking modes */ if (res=sendn(argv) ) { printf("blocking send succeeded\n"); res = sendnb(argv); if ( res ) printf("sent non-blocking successfully\n"); } } return 0; }
socktest.c
Subscribe to:
Posts (Atom)
No comments:
Post a Comment