2.7.2 Accepting a Connection Using a C Socket Interface
Example 2-12 shows how to use the accept() routine.
Example 2-12 Accepting a Connection Request Using C Socket Programming
/* * Accept connection from socket 2: * accepted connection will be on socket 3 */ namelength = sizeof (sock2_name); sock_3 = accept (sock_2,(1) & sock2_name,(2) & namelength);(3) if (sock_3 == -1) { perror ("accept"); cleanup( 2, sock_2, sock_3); }
In this example of an accept() routine:
Obtaining device socket information is useful if your program has
management functions or if you have a complex program that uses
multiple connections you need to track.
2.8.1 Getting Device Socket Information with OpenVMS System Services
To obtain information about the parts of a device socket, use the following OpenVMS system services:
When using the $GETDVI system service, use the channel number (not UCX$DEVICE:) as an input parameter to $GETDVIW to obtain the correct unit number and characteristics of an internet pseudodevice. For information on $GETDVI and its parameters, refer to the OpenVMS Programming Concepts Manual and the OpenVMS System Services Reference Manual.
Example 2-13 shows an example of a TCP/IP client using the IO$_SENSEMODE function to get a local socket name.
Example 2-13 Obtaining Device Socket Information by Using OpenVMS System Services
/* ** ** This is a table listing some of the valid combinations of level to optname ** ** level: +---------------------+-------------------------+----------------+ ** | SOL_SOCKET | UCX$C_TCP | UCX$C_RAW_IP | ** optname: +.....................+.........................+................+ ** | UCX$C_RCVBUF | UCX$C_USELOOPBACK | UCX$C_IP_TTL | ** | UCX$C_SNDBUF | UCX$C_TCP_NODELAY | UCX$C_IP_TOS | ** | UCX$C_NO_RCV_CHKSUM | UCX$C_TCP_PROBE_IDLE | | ** | UCX$C_NO_SND_CHKSUM | UCX$C_TCP_DROP_IDLE | | ** | UCX$C_NO_CHKSUM | UCX$C_FULL_DUPLEX_CLOSE | | ** | UCX$C_KEEPALIVE | | | ** | UCX$C_REUSEADDR | | | ** +---------------------+-------------------------+----------------+ ** */ setSockOptType setSockOpt; /* Struct used to set socket options */ iLSockOptType setSockOptItemList;/* Item List of setSockOpt(s) */ int setSockOneOption = 1; /* ** ** Set and validate option name and level. ** */ setSockOpt.length = sizeof( setSockOneOption ); setSockOpt.code = UCX$M_REUSEADDR; setSockOpt.optBuffAddr = (char*)&setSockOneOption; setSockOptItemList.length = sizeof( setSockOptType ); setSockOptItemList.optStructAddr = (char*) &setSockOpt; setSockOptItemList.code = UCX$C_SOCKOPT; /* ** ** Attempt to set options on the socket. ** */ sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChannel, /* chan.v */ IO$_SETMODE, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ 0, 0, 0, 0, /* p1, p2, p3, p4 UNUSED */ &setSockOptItemList, /* p5.r options item list */ 0 /* p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate the system service status. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the IO status. */ { cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); }
You can use any of the following C Socket routines to get device socket information:
Example 2-14 shows a TCP server using the getsockopt(), getpeername() and, getsockname() routines to get device socket information.
Example 2-14 Obtaining Device Socket Information by Using C Socket Programming
/* This program accepts a connection on TCP port 1234, sends the string, "Hello, world!", and immediately closes the connection and terminates. */ #if defined(__VMS) || defined(VMS) #include <types.h> #include <in.h> #include <socket.h> #include <unixio.h> #include <string.h> #else #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <unistd.h> #include <string.h> #endif #define PORTNUM 1234 main() { struct sockaddr_in lcladdr, name; int r, s, namelen = sizeof(name), one = 1; char *message = "Hello, world!\r\n"; lcladdr.sin_family = AF_INET; lcladdr.sin_addr.s_addr = INADDR_ANY; lcladdr.sin_port = htons(PORTNUM); if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("socket"); if (setsockopt(s, (1) SOL_SOCKET, (2) SO_REUSEADDR, (3) &one, (4) sizeof(one))) (5) perror("setsockopt"); if (bind(s, &lcladdr, sizeof(lcladdr))) perror("bind"); if (listen(s, 1)) perror("listen"); if ((r = accept(s, 0, 0)) < 0) perror("accept"); if (getsockname(r,(6) &name,(7)&namelen(8)))perror("getsockname"); else printf("Local address: %s\tport: %d\n", inet_ntoa(name.sin_addr.s_addr), ntohs(name.sin_port)); if (getpeername(r,(9) &name,(10) &namelen))(11) perror("getpeername"); else printf("Remote address: %s\tport: %d\n", inet_ntoa(name.sin_addr.s_addr), ntohs(name.sin_port)); if (send(r, message, strlen(message), 0) != strlen(message)) perror("send"); if (close(r)) perror("close"); if (close(s)) perror("close"); }
The setsockopt() routine sets the options on a socket, as follows:
The getsockname() routine returns the options set on a socket, as follows:
The getpeername() routine returns the name of the peer connected to the specified remote socket descriptor, as follows:
TCP/IP allows the application to read data after the following occurs:
The $QIO IO$_READVBLK function transfers data received from the internet host (kept in system dynamic memory) into the address space of the user's process. After the read operation completes, the data in dynamic memory is discarded.
Example 2-15 shows a TCP/IP server using the IO$_READVBLK function to read data into a single I/O buffer.
Example 2-15 Reading Data Using OpenVMS System Services
/* ** ** Attempt to receive data from the server. ** Use the function code of IO$_READVBLK, passing the address of the ** buffer to P1, and the maximum size of the buffer to P2. ** */ printf("\n Attempt to receive data from the server.\n" ); memset( IOBuff, 'Z', MaxBuff ); sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChannel, /* chan.v */ IO$_READVBLK, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ IOBuff, /* p1.r IO buffer */ MaxBuff, /* p2.v IO buffer size */ 0, /* p3 UNUSED */ 0, /* p4.v IO options flag */ 0, 0 /* p5, p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate system service status. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the IO status. */ { cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); } else if( iosb.count == 0 ) printf( " FAILED receiving data, no connection.\n" ); else printf( " SUCCEEDED in to receiving: '%s'\n", IOBuff );
You can also specify a list of read buffers by omitting the p1
and p2 arguments and passing the list of buffers as the
p6 parameter. See Section 3.7 for more information.
2.9.2 Reading Data with a C Socket Interface
Example 2-16 shows an example of a TCP/IP server using the recv() routine to read data.
Example 2-16 Reading Data Using C Socket Programming
/* * * INCLUDE FILES * */ #ifdef VMS #include <errno.h> #include <types.h> #include <stdio.h> #include <socket.h> #include <in.h> #include <netdb.h> /* change hostent to comply with BSD 4.3 */ #include <inet.h> #include <ucx$inetdef.h> /* INET symbol definitions */ . . . main(argc,argv) int argc; char **argv; { int sock_2, sock_3; /* sockets */ static char message[BUFSIZ]; static struct sockaddr_in sock2_name; /* Address struct for socket2.*/ static struct sockaddr_in retsock2_name; /* Address struct for socket2.*/ struct hostent hostentstruct; /* Storage for hostent data. */ struct hostent *hostentptr; /* Pointer to hostent data. */ static char hostname[256]; /* Name of local host. */ int flag; int retval; /* helpful for debugging */ int namelength; . . . /* * Receive message from client socket. */ retval = recv(sock_2,(1) message,(2) sizeof(message),(3) flag);(4) if (retval == -1) { perror ("receive"); cleanup( 1, sock_3); } else printf (" %s\n", message);
Only stream-type (TCP/IP) sockets can receive out-of-band (OOB) data. Upon receiving a TCP/IP OOB character, UCX stores a pointer in the received stream to the character that precedes the OOB character.
A read operation with a user buffer size larger than the size of the received stream up to the OOB character completes by returning to the user the received stream up to, but not including, the OOB character.
Poll the socket to determine if additional read operations are needed
before getting all the characters from the stream preceding the OOB
character.
2.10.1 Reading OOB Data with OpenVMS System Services
To receive OOB data from a remote process, issue the IO$_READVBLK function with the IO$M_INTERRUPT modifier.
To poll the socket, issue a $QIO command with the IO$_SENSEMODE function and the UCX$C_IOCTL subfunction that specifies the SIOCATMARK 'ioctl' parameter.
If the SIOCATMARK returns a value of 0, issue additional read QIOs to read more data before reading the OOB character. If the SIOCATMARK returns a value of 1, the next read QIO returns the OOB character.
These functions are useful if a socket has the socket option OOBINLINE set. The OOB character is read with the characters in the stream (IO$_READVBLK), but not before the preceding characters. To determine whether or not the first character in the user buffer on the next read is an OOB, poll the socket.
To get a received OOB character for a socket with the socket option OOBINLINE clear, issue one of the following functions:
Example 2-17 shows how to use the IO$M_INTERRUPT modifier to read out-of-band data.
Example 2-17 Reading OOB Data Using OpenVMS System Services
/* ** Attempt to receive the OOB data from the client. ** Use the function code of IO$_READVBLK, passing the address of the ** input buffer to P1, and the OOB code, UCX$C_MSG_OOB, to P4. ** We support the sending and receiving of a one byte of OOB data. */ sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChanClient, /* chan.v */ IO$_READVBLK, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ &OOBBuff, /* p1.r IO buffer */ MaxBuff, /* p2.v IO buffer size */ 0, /* p3 UNUSED */ UCX$C_MSG_OOB, /* p4.v IO options flag */ 0, 0 /* p5, p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate the system service. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the IO status. */ { cleanup( IOChanClient ); cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); } else if( iosb.count == 0 ) printf( " FAILED to receive the message, no connection.\n" ); else printf( " SUCCEEDED in receiving '%d'\n", OOBBuff );
You can use the recv() socket routine with the MSG_OOB flag set to receive out-of-band data regardless of how many of the preceding characters in the stream you have received.
Example 2-18 shows an example of a TCP/IP server using the recv() routine to receive out-of-band data.
Example 2-18 Reading OOB Data Using C Socket Programming
#include <types.h> #include <in.h> #include <socket.h> #include <unixio.h> #include <string.h> #define PORTNUM 1234 . . . /* * Accept connection from socket 2: * accepted connection will be on socket 3 */ namelength = sizeof (sock2_name); sock_3 = accept (sock_2, &sock2_name, &namelength); if (sock_3 == -1) { perror ("accept"); cleanup( 2, sock_2, sock_3); } /* * Receive message from socket 1. */ flag = MSG_OOB; retval = recv(sock_3,(1) message,(2)sizeof(message),(3)flag);(4) if (retval == -1) { perror ("receive"); cleanup( 2, sock_2, sock_3); } else printf (" %s\n", message);
You can issue a read operation to look at data in a socket receive
queue without removing the data from the buffer. This is called peeking.
2.11.1 Peeking at Data with OpenVMS System Services
To peek at data next in the socket receive queue, issue the
IO$_READVBLK function of the $QIO system service with the UCX$M_PEEK
flag. This allows you to issue multiple read operations on the same
data.
2.11.2 Peeking at Data with a C Socket Interface
Use the MSG_PEEK flag with the recv() routine to peek at data in the socket receive queue. Example 2-19 shows a TCP server using the recv() routine with the MSG_PEEK flag to peek at received data.
Example 2-19 Previewing Data Using C Socket Programming
/* This program accepts a connection on TCP port 1234, prompts for a character to be entered, and uses the "peek" feature to determine what character was chosen before actually reading it. */ #include <types.h> #include <in.h> #include <socket.h> #include <unixio.h> #define PORTNUM 1234 main() { struct sockaddr_in lcladdr; int r, s; char buffer[64]; lcladdr.sin_family = AF_INET; lcladdr.sin_addr.s_addr = INADDR_ANY; lcladdr.sin_port = htons(PORTNUM); if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) perror("socket"); if (bind(s, &lcladdr, sizeof(lcladdr))) perror("bind"); if (listen(s, 1)) perror("listen"); if ((r = accept(s, 0, 0)) < 0) perror("accept"); sprintf(buffer, "Please pick a character:\r\n"); if (send(r, buffer, strlen(buffer), 0) != strlen(buffer)) perror("send"); if (recv(r,(1) buffer,(2) 1,(3) MSG_PEEK)(4) != 1) perror("recv"); sprintf(buffer, "Before receiving, I see you picked '%c'.\r\n", buffer[0]); if (send(r, buffer, strlen(buffer), 0) != strlen(buffer)) perror("send"); if (recv(r, buffer, 1, 0) != 1) perror("recv"); sprintf(buffer, "Sure enough, I received '%c'.\r\n", buffer[0]); if (send(r, buffer, strlen(buffer), 0) != strlen(buffer)) perror("send"); if (close(r)) perror("close"); if (close(s)) perror("close"); }
The recv() routine receives data from a connected socket and places it in a buffer, as follows:
For programs that use TCP, data writing occurs after a client program
initiates a connection and the server program accepts the connection.
When using UDP, you also have the option of establishing a default peer
address with a specific socket, but this is not required for data
transfer.
2.12.1 Writing Data with OpenVMS System Services
The IO$_WRITEVBLK function of the $QIO system service copies data from the address space of the user's process to system dynamic memory and then transfers the data to an internet host or port.
Example 2-20 shows an example of a TCP client using the IO$_WRITEVBLK function to transmit a single data buffer. An IO$_ACCESS QIO was previously executed to specify the remote host address.
Example 2-20 Writing Data Using OpenVMS System Services
/* ** ** Attempt to send data to a previously established network connection. ** Use the function code of IO$_WRITEVBLK, passing the address of the ** output buffer to P1, and the size of the message to P2. ** */ sprintf( IOBuff, "The answer is %d", 42 ); sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChanClient, /* chan.v */ IO$_WRITEVBLK, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ IOBuff, /* p1.r IO buffer */ strlen( IOBuff )+ 1, /* p2.v IO buffer size */ 0, /* p3 UNUSED */ 0, /* p4.v IO options flag */ 0, 0 /* p5, p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate system service. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the IO status. */ { cleanup( IOChanClient /* chan.v */ ); cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); } else if( iosb.count == 0 ) printf( " FAILED to send message, no connection.\n\n" ); else printf( " SUCCEEDED in sending the message.\n\n" );
You can also specify a list of write buffers by omitting the
p1 and p2 parameters, and instead passing the list of
buffers as the p5 parameter. See Section 3.6 for more
information.
2.12.2 Writing Data with a C Socket Interface
Example 2-21 shows a TCP/IP client using the send() routine to transmit data.
Example 2-21 Writing Data Using C Socket Programming
#include <errno.h> #include <types.h> #include <stdio.h> #include <socket.h> #include <in.h> #include <netdb.h> /* change hostent to comply with BSD 4.3*/ #include <inet.h> #include <ucx$inetdef.h> /* INET symbol definitions */ #else . . . main(argc,argv) int argc; char **argv; { int sock_1; /* socket */ static char message[] = "Hi there."; static struct sockaddr_in sock2_name; /* Address struct for socket2 struct hostent hostentstruct; /* Storage for hostent data. */ struct hostent *hostentptr; /* Pointer to hostent data. */ static char hostname[256]; /* Name of local host. */ int flag; int retval; /* helpful for debugging */ int shut = FALSE; /* flag to cleanup */ /* * Send message to socket 2. */ flag = 0; /* maybe 0 or MSG_OOB */ retval = send(sock_1,(1) message,(2) sizeof (message),(3) flag);(4) if (retval < 0) { perror ("oob_messsend"); shut = TRUE; }
If your application uses TCP, you can send OOB data to a remote
process. At the remote process, the message is delivered to the user
through the data receive, or out-of-band data receive mechanisms.
2.13.1 Writing OOB Data with OpenVMS System Services
To send out-of-band data to a remote process, issue a $QIO system service using the IO$_WRITEVBLK function with the IO$M_INTERRUPT modifier. Example 2-22 shows a TCP server using the MSG_OOB flag with the send() routine.
Example 2-22 Writing OOB Data Using OpenVMS System Services
/* ** ** Attempt to send Out Of Band data to a previously established network ** connection. Use the function code of IO$_WRITEVBLK, passing the address ** of the buffer to P1, and the OOB code, UCX$C_MSG_OOB, to P4. ** */ OOBBuff = 7; sysSrvSts = sys$qiow( 0, /* efn.v | 0 */ IOChannel, /* chan.v */ IO$_WRITEVBLK, /* func.v */ &iosb, /* iosb.r | 0 */ 0, 0, /* astadr, astprm: UNUSED */ &OOBBuff, /* p1.r IO buffer */ 1, /* p2.v IO buffer size */ 0, /* p3 UNUSED */ UCX$C_MSG_OOB, /* p4.v IO options flag */ 0, 0 /* p5, p6 UNUSED */ ); if((( sysSrvSts & 1 ) != 1 ) || /* Validate the system service status. */ (( iosb.cond_value & 1 ) != 1)) /* Validate the IO status. */ { cleanup( IOChannel ); errorExit( sysSrvSts, iosb.cond_value ); } else if( iosb.count == 0 ) printf( " FAILED to send the OOB message, no connection.\n" ); else printf( " SUCCEEDED in sending the OOB message.\n" );
To send OOB data to a remote process, use the MSG_OOB flag with the send(), sendmsg(), and sendto() routines.