Preparing the Server for a Connection
At the top of your application, include vmci_sockets.h and declare a constant for the socket buffer size. In the example below, BUFSIZE defines the socket buffer size. The number 4096 is a good choice for efficiency on multiple platforms. It is not based on the size of a TCP packet, which is usually smaller.
#include "vmci_sockets.h"
#define BUFSIZE 4096
To compile on Windows, you must also call the Winsock WSAStartup() function.
err = WSAStartup(versionRequested, &wsaData);
if (err != 0) {
printf(stderr, "Could not register with Winsock DLL.\n");
goto cleanup;
}
This is not necessary on non-Windows systems.
Socket() Function
In a vSockets application, obtain the new address family (domain) to replace AF_INET.
int afVMCI = VMCISock_GetAFValue();
if ((sockfd = socket(afVMCI, SOCK_STREAM, 0)) == -1) {
perror("socket");
goto cleanup;
}
VMCISock_GetAFValue() returns a descriptor for the vSockets address family if available.
Set and Get Socket Options
vSockets allows you to set the minimum, maximum, and default size of communicating stream buffers. Names for the three options are:
SO_VMCI_BUFFER_SIZE – Default size of communicating buffers; 65536 bytes if not set.
SO_VMCI_BUFFER_MIN_SIZE – Minimum size of communicating buffers; defaults to 128 bytes.
SO_VMCI_BUFFER_MAX_SIZE – Maximum size of communicating buffers; defaults to 262144 bytes.
To set a new value for a socket option, call the setsockopt() function. To get a value, call getsockopt().
For example, to halve the size of the communications buffers from 65536 to 32768, and verify that the setting took effect, insert the following code:
uint64 setBuf = 32768, getBuf;
/* reduce buffer to above size and check */
if (setsockopt(sockfd, afVMCI, SO_VMCI_BUFFER_SIZE, (void *)&setBuf, sizeof setBuf) == -1) {
perror(“setsockopt”);
goto close;
}
if (getsockopt(sockfd, afVMCI, SO_VMCI_BUFFER_SIZE, (void *)&getBuf, sizeof getBuf) == -1) {
perror(“getsockopt”);
goto close;
}
if (getBuf != setBuf) {
printf(stderr, “SO_VMCI_BUFFER_SIZE not set to size requested.\n”);
goto close;
}
Parameters setBuf and getBuf must be declared 64 bit, even on 32-bit systems.
To have an effect, socket options must be set before establishing a connection. The buffer size is negotiated before the connection is established and stays consistent until the connection is closed. For a server socket, set options before any client establishes a connection. To be sure that this applies to all sockets, set options before calling listen(). For a client socket, set options before calling connect().
Bind() Function
This bind() call associates the stream socket with the network settings in the sockaddr_vm structure, instead of the sockaddr_in structure.
struct sockaddr_vm my_addr = {0};
my_addr.svm_family = afVMCI;
my_addr.svm_cid = VMADDR_CID_ANY;
my_addr.svm_port = VMADDR_PORT_ANY;
if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof my_addr) == -1) {
perror("bind");
goto close;
}
The sockaddr_vm structure contains an element for the context ID (CID), which specifies the virtual machine. For the client this is the local CID. For the server (listener), this could be any connecting virtual machine. Both VMADDR_CID_ANY and VMADDR_PORT_ANY are predefined so that at bind or connection time, the appropriate CID and port number are filled in from the client. VMADDR_CID_ANY is replaced with the CID of the virtual machine and VMADDR_PORT_ANY provides an ephemeral port from the nonreserved range (>= 1024).
The client (connector) can obtain its local CID by calling VMCISock_GetLocalCID().
The bind() function is the same as for a regular TCP sockets application.
Listen() Function
The listen() call prepares to accept incoming client connections. The BACKLOG macro predefines the number of incoming connection requests that the system accepts before rejecting new ones. This function is the same as listen() in a regular TCP sockets application.
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
goto close;
}
Accept() Function
The accept() call waits indefinitely for an incoming connection to arrive, creating a new socket (and stream descriptor newfd) when it does. The structure their_addr gets filled with connection information.
struct sockaddr_vm their_addr;
socklen_t their_addr_len = sizeof their_addr;
if ((newfd = accept(sockfd, (struct sockaddr *) &their_addr, &their_addr_len)) == -1) {
perror("accept");
goto close;
}
Select() Function
The select() call enables a process to wait for events on multiple file descriptors simultaneously. This function hibernates, waking up the process when an event occurs. You can specify a timeout in seconds or microseconds. After timeout, the function returns zero. You can specify the read, write, and exception file descriptors as NULL if the program can safely ignore them.
if ((select(nfds, &readfd, &writefds, &exceptfds, &timeout) == -1) {
perror("select");
goto close;
}
Recv() Function
The recv() call reads data from the client application. The server and client can communicate the length of data transmitted, or the server can terminate its recv() loop when the client closes its connection.
char recv_buf[BUFSIZE];
if ((numbytes = recv(sockfd, recv_buf, sizeof recv_buf, 0)) == -1) {
perror("recv");
goto close;
}
Send() Function
The send() call writes data to the client application. Server and client must communicate the length of data transmitted, or agree beforehand on a size. Often the server sends only flow control information to the client.
char send_buf[BUFSIZE];
if ((numbytes = send(newfd, send_buf, sizeof send_buf, 0)) == -1) {
perror("send");
goto close;
}
Close() Function
Given the original socket descriptor obtained from the socket() call, the close() call closes the socket and terminates the connection if it is still open. Some server applications close immediately after receiving client data, while others wait for additional connections. To compile on Windows, you must call the Winsock closesocket() instead of close().
#ifdef _WIN32
return closesocket(sockfd);
#else
return close(sockfd);
#endif
The shutdown() function is like close(), but shuts down the connection.
Poll() Information
Not all socket-based networking programs use poll(), but if they do, no changes are required. The poll() function is like select(). See Select() Function for related information.
Read() and Write()
The read() and write() socket calls are provided for convenience. They provide the same functionality as recv() and send().
Getsockname() Function
The getsockname() function retrieves the local address associated with a socket.
my_addr_size = sizeof my_addr;
if (getsockname(sockfd, (struct sockaddr *) &my_addr, &my_addr_size) == -1) {
perror("getsockname");
goto close;
}