Practical Hyper-V Socket Communication
Programming with sockets is not a new thing. In a nutshell, programming with sockets boils down to simple flow of create a socket, connect, bind, listen, accept, send, and receive packets. Getting started requires visiting multiple sources and pulling together pieces of information. I found useful to compare the Winsock sample code with the Hyper-V socket API and see the differences between them. You can use this sample code to get a head start on your own code.
Starting with Windows 10, a new set of sockets arrived to facilitate the communication between your host and one or more virtual machines free of any network connection. Here you can find an article regarding the Hyper-V sockets and how can you make your own integration services. In addition, MSDN offers sample code to create a basic client/server TCP communication.
Familiarize yourself with Complete Winsock Client Code.aspx) and Complete Winsock Server Code.aspx) first. In this blog I discuss the similarities of the code. If you follow the guidance, you will be able to create your own better version to create host to virtual machine communication using Hyper-V sockets.
Hyper-V Socket Client Code
Start by making your own integration service. Instead of defining the DEFAULT_PORT, define the virtual machine ID and service GUID. These values can be collected as command line input in your code. For clarity in the sample code I used fixed values.
// Vm ID: {CDEE5C88-C5A5-4CC8-9D2D-C15816A01349}
DEFINE_GUID(VmId,
0xcdee5c88, 0xc5a5, 0x4cc8, 0x9d, 0x2d, 0xc1, 0x58, 0x16, 0xa0, 0x13, 0x49);
// Service GUID: {8BA03980-174A-43E7-852A-E5EC79A671C7}
DEFINE_GUID(ServiceId,
0x8ba03980, 0x174a, 0x43e7, 0x85, 0x2a, 0xe5, 0xec, 0x79, 0xa6, 0x71, 0xc7);
In this context, the getaddrinfo doesn't exist because it is specific to network communication and there is no need to resolve any server address and port. The Hyper-V socket secret sauce resides in the definition and initialization of the socket.
SOCKADDR_HV clientService;
ZeroMemory(&clientService, sizeof(clientService));
clientService.Family = AF_HYPERV;
clientService.VmId = VmId;
clientService.ServiceId = ServiceId;
The only one change remaining is in the connection, where the clientService variable is used.
iResult = connect(connectSocket, (SOCKADDR *)(&clientService), sizeof(clientService));
Hyper-V Socket Server Code
We need to make the same modifications for the server code except now the VmId should be the HV_GUID_PARENT. The connect function is not called in the server code, instead we use the bind function. The code looks like this.
iResult = bind(ListenSocket, (SOCKADDR *)(&clientService), sizeof(clientService));
Running the Code Sample
The following links contain the complete sample source code for the basic Hyper-V socket communication. The code is the same as Winsock sample to make the differences between them very clear.
Complete Hyper-V Socket Client Code
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <stdafx.h>
#include <ws2tcpip.h>
#include <objbase.h>
#define DEFAULT_BUFLEN 512
// Vm ID: {CDEE5C88-C5A5-4CC8-9D2D-C15816A01349}
DEFINE_GUID(VmId,
0xcdee5c88, 0xc5a5, 0x4cc8, 0x9d, 0x2d, 0xc1, 0x58, 0x16, 0xa0, 0x13, 0x49);
// Service GUID: {8BA03980-174A-43E7-852A-E5EC79A671C7}
DEFINE_GUID(ServiceId,
0x8ba03980, 0x174a, 0x43e7, 0x85, 0x2a, 0xe5, 0xec, 0x79, 0xa6, 0x71, 0xc7);
int __cdecl main(void)
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo hints;
SOCKADDR_HV clientService;
char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
int iResult;
int recvbuflen = DEFAULT_BUFLEN;
// add your own input here
CONST GUID *vmId = &VmId;
CONST GUID *serviceId = &ServiceId;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
ZeroMemory(&clientService, sizeof(clientService));
clientService.Family = AF_HYPERV;
clientService.VmId = *vmId;
clientService.ServiceId = *serviceId;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_HYPERV;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = HV_PROTOCOL_RAW;
hints.ai_addrlen = sizeof(SOCKADDR_HV);
hints.ai_addr = reinterpret_cast<SOCKADDR *>(&clientService);
// Create a SOCKET for connecting to server
ConnectSocket = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
// Connect to server.
iResult = connect(ConnectSocket, hints.ai_addr, (int)hints.ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
if (ConnectSocket == INVALID_SOCKET) {
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
// Send an initial buffer
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// shutdown the connection since no more data will be sent
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// Receive until the peer closes the connection
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
printf("Bytes received: %d\n", iResult);
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed with error: %d\n", WSAGetLastError());
} while (iResult > 0);
// cleanup
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
Complete Hyper-V Socket Server Code
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <stdafx.h>
#include <ws2tcpip.h>
#include <objbase.h>
#define DEFAULT_BUFLEN 512
// Service GUID: {8BA03980-174A-43E7-852A-E5EC79A671C7}
DEFINE_GUID(ServiceId,
0x8ba03980, 0x174a, 0x43e7, 0x85, 0x2a, 0xe5, 0xec, 0x79, 0xa6, 0x71, 0xc7);
int main(void)
{
WSADATA wsaData;
int iResult;
int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
struct addrinfo hints;
SOCKADDR_HV clientService;
// add your own input here
CONST GUID *serviceId = &ServiceId;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
ZeroMemory(&clientService, sizeof(clientService));
clientService.Family = AF_HYPERV;
clientService.VmId = HV_GUID_PARENT;
clientService.ServiceId = *serviceId;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_HYPERV;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = HV_PROTOCOL_RAW;
hints.ai_addrlen = sizeof(SOCKADDR_HV);
hints.ai_addr = reinterpret_cast<SOCKADDR *>(&clientService);
// Create a SOCKET for connecting to server
ListenSocket = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
// Setup the Hyper-V listening socket
iResult = bind(ListenSocket, hints.ai_addr, (int)hints.ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// No longer need server socket
closesocket(ListenSocket);
// Receive until the peer shuts down the connection
do {
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
// Echo the buffer back to the sender
iSendResult = send(ClientSocket, recvbuf, iResult, 0);
if (iSendResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iResult == 0)
printf("Connection closing...\n");
else {
printf("recv failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
} while (iResult > 0);
// shutdown the connection since we're done
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// cleanup
closesocket(ClientSocket);
WSACleanup();
return 0;
}
Originally published August 29, 2016
