535 lines
16 KiB
C++
535 lines
16 KiB
C++
/******************************************************************************\
|
|
* isb.cpp
|
|
*
|
|
* This simple IPv6 sample demonstrates the use of Winsock Ideal Send Backlog functionality.
|
|
*
|
|
* The Ideal Send Backlog functionality is new to Windows Sockets in Windows Vista SP1
|
|
* and Windows Server 2008.
|
|
*
|
|
* This sample requires that TCP/IP version 6 be installed on the system (default
|
|
* configuration for Windows Vista and Windows Server 2008).
|
|
*
|
|
* For an application to effectively use this functionality, the application should:
|
|
* 1. First query for the initial ideal send backlog value (idealsendbacklogquery).
|
|
* 2. Post overlapped idealsendbacklognotify to receive isb change indications.
|
|
* 3. Upon notify completion, immediately post another idealsendbacklognotify and
|
|
* query for new isb value (idealsendbacklogquery).
|
|
*
|
|
* The I/O model in the sample uses blocking send/recv calls to keep the sample simple.
|
|
* A real world application could use non-blocking or overlapped I/O for possibly better performance.
|
|
*
|
|
* Note:
|
|
* On Windows 7/Server 2008 R2 and later versions, a new feature called Send Path Auto-Tuning is available.
|
|
* With this new functionaly, Windows can perform the send auto-tuning (ideal send backlog)
|
|
* on the application's behalf. To enable this functionality, the application:
|
|
* 1. Must not change the connected socket's send buffer limit (SO_SNDBUF).
|
|
* 2. Must not query for the ideal send backlog value. In other words, application must
|
|
* not call idealsendbacklogquery.
|
|
*
|
|
* If application does either of the above, the send auto-tuning will be disabled for that connection.
|
|
*
|
|
* The appication may however, post idealsendbacklognotify to receive indications that a send auto-tuning
|
|
* event has occurred.
|
|
*
|
|
*
|
|
*
|
|
* This is a part of the Microsoft Source Code Samples.
|
|
* Copyright 1996 - 2009 Microsoft Corporation.
|
|
* All rights reserved.
|
|
* This source code is only intended as a supplement to
|
|
* Microsoft Development Tools and/or WinHelp documentation.
|
|
* See these sources for detailed information regarding the
|
|
* Microsoft samples programs.
|
|
\******************************************************************************/
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
|
#define ERR(e) \
|
|
_tprintf(TEXT("\n%s failed: %d\n"),e,WSAGetLastError())
|
|
|
|
#define MALLOC(x) \
|
|
HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,x)
|
|
|
|
#define FREE(p) \
|
|
if(NULL != p) \
|
|
{\
|
|
HeapFree(GetProcessHeap(),0,p); \
|
|
p = NULL;\
|
|
}
|
|
|
|
#define CLOSESOCK(s) \
|
|
if(INVALID_SOCKET != s) \
|
|
{\
|
|
closesocket(s); \
|
|
s = INVALID_SOCKET; \
|
|
}
|
|
|
|
#define FREEADDRINFO(p) \
|
|
if(NULL != p) \
|
|
{\
|
|
FreeAddrInfo(p); \
|
|
p = NULL; \
|
|
}
|
|
|
|
#define WS_VER 0x0202 //Latest Winsock version (v2.2)
|
|
|
|
#define RECV_MAX_BUFFER_SIZE (16*(1024*1024)) //16MB
|
|
|
|
|
|
VOID Usage()
|
|
{
|
|
_tprintf(TEXT("Usage: isb -l -n <ListenerAddress> -e <port> -a\n") \
|
|
TEXT("-l act as listener\n") \
|
|
TEXT("-n act as originator\n")
|
|
TEXT("-a enable auto-tuning\n")
|
|
);
|
|
ExitProcess(1);
|
|
}
|
|
|
|
BOOL bListener = FALSE,
|
|
bOriginator = FALSE,
|
|
bAutoTuning = FALSE;
|
|
_TCHAR szServerName[MAX_PATH];
|
|
USHORT port = 0;
|
|
|
|
VOID ValidateArgs(int argc, _TCHAR** argv)
|
|
{
|
|
int i;
|
|
|
|
if (argc < 2)
|
|
{
|
|
Usage();
|
|
}
|
|
|
|
for (i=1; i < argc ;i++)
|
|
{
|
|
|
|
if (lstrlen(argv[i]) < 2)
|
|
continue;
|
|
|
|
if ((argv[i][0] == TEXT('-') ) || (argv[i][0] == TEXT('/') ))
|
|
{
|
|
|
|
switch (argv[i][1])
|
|
{
|
|
|
|
case TEXT('l'):
|
|
bListener = TRUE;
|
|
break;
|
|
case TEXT('n'):
|
|
bOriginator = TRUE;
|
|
StringCbCopy(szServerName,
|
|
sizeof szServerName,
|
|
argv[i+1]
|
|
);
|
|
break;
|
|
case TEXT('e'):
|
|
port = (USHORT)_ttoi(argv[i+1]);
|
|
break;
|
|
case TEXT('a'):
|
|
bAutoTuning = TRUE;
|
|
break;
|
|
default:
|
|
Usage();
|
|
ExitProcess(1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((bListener && bOriginator) ||
|
|
((!bListener) && (!bOriginator)) )
|
|
{
|
|
Usage();
|
|
}
|
|
}
|
|
|
|
/*
|
|
DualStackSocket returns a socket which can be used for either v4 or v6 connections
|
|
on Vista and later, by setting the IPV6_V6ONLY option value to zero.
|
|
*/
|
|
SOCKET DualStackSocket(INT SockType)
|
|
{
|
|
SOCKET sock = INVALID_SOCKET;
|
|
INT off = 0;
|
|
|
|
__try
|
|
{
|
|
if (INVALID_SOCKET == (sock = socket(AF_INET6,
|
|
SockType,
|
|
0
|
|
)))
|
|
{
|
|
ERR(TEXT("socket"));
|
|
__leave;
|
|
}
|
|
|
|
if (SOCKET_ERROR == setsockopt(sock,
|
|
IPPROTO_IPV6,
|
|
IPV6_V6ONLY,
|
|
(char*)&off,
|
|
sizeof off
|
|
))
|
|
{
|
|
ERR(TEXT("setsockopt"));
|
|
CLOSESOCK(sock);
|
|
__leave;
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
}
|
|
|
|
return sock;
|
|
}
|
|
|
|
|
|
HANDLE hExit = NULL;
|
|
|
|
BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
|
|
{
|
|
BOOL bHandled = FALSE;
|
|
|
|
switch (dwCtrlType)
|
|
{
|
|
case CTRL_C_EVENT:
|
|
SetEvent(hExit);
|
|
bHandled = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return bHandled;
|
|
}
|
|
|
|
|
|
int _tmain(int argc, _TCHAR** argv)
|
|
{
|
|
WSADATA wsd;
|
|
ADDRINFOT hints = {0},
|
|
*res = NULL;
|
|
SOCKADDR_STORAGE lAddr = {0};
|
|
SOCKET sl = INVALID_SOCKET,
|
|
sc = INVALID_SOCKET,
|
|
sa = INVALID_SOCKET;
|
|
PCHAR buffer = NULL;
|
|
WSAOVERLAPPED ov = {0};
|
|
HANDLE hEvents[2];
|
|
INT nStartup = 0,
|
|
rc = 0,
|
|
err = 0,
|
|
isb = RECV_MAX_BUFFER_SIZE, //Ideal Send Backlog value
|
|
rcvsize = 0, //recv window size
|
|
timeout = 5000; //recv timeout val
|
|
_TCHAR szPort[6];
|
|
_int64 TotalBytes = 0;
|
|
|
|
ValidateArgs(argc,argv);
|
|
|
|
//set exit/cleanup event
|
|
|
|
hEvents[0] = hExit = CreateEvent(NULL,
|
|
TRUE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if (!SetConsoleCtrlHandler(ConsoleCtrlHandler,
|
|
TRUE
|
|
))
|
|
{
|
|
ERR(TEXT("SetConsoleCtrlHandler"));
|
|
return 1;
|
|
}
|
|
|
|
//Initialize winsock
|
|
|
|
nStartup = WSAStartup(WS_VER,&wsd);
|
|
if(nStartup)
|
|
_tprintf(TEXT("WSAStartup failed: %d\n"),nStartup);
|
|
else
|
|
nStartup++;
|
|
|
|
|
|
__try
|
|
{
|
|
//allocate our buffer
|
|
if (NULL == (buffer = (PCHAR)MALLOC(RECV_MAX_BUFFER_SIZE)))
|
|
{
|
|
ERR(TEXT("HeapAlloc"));
|
|
__leave;
|
|
}
|
|
|
|
/*
|
|
listener mode - create a dual stack socket and listen to
|
|
v6 addr_any on the given port.
|
|
*/
|
|
if (bListener)
|
|
{
|
|
//Fill send buffer
|
|
FillMemory(buffer,
|
|
RECV_MAX_BUFFER_SIZE,
|
|
'@'
|
|
);
|
|
|
|
if (INVALID_SOCKET == (sl = DualStackSocket(SOCK_STREAM)))
|
|
{
|
|
ERR(TEXT("DualStackSocket"));
|
|
__leave;
|
|
}
|
|
|
|
lAddr.ss_family = AF_INET6;
|
|
INETADDR_SETANY((PSOCKADDR)&lAddr);
|
|
SS_PORT((PSOCKADDR)&lAddr) = htons(port);
|
|
|
|
if (SOCKET_ERROR == bind(sl,(PSOCKADDR)&lAddr,
|
|
sizeof lAddr
|
|
))
|
|
{
|
|
ERR(TEXT("bind"));
|
|
__leave;
|
|
}
|
|
|
|
if (SOCKET_ERROR == listen(sl,
|
|
1
|
|
))
|
|
{
|
|
ERR(TEXT("listen"));
|
|
__leave;
|
|
}
|
|
|
|
if (INVALID_SOCKET == (sa = accept(sl,
|
|
NULL,
|
|
NULL
|
|
)))
|
|
{
|
|
ERR(TEXT("accept"));
|
|
__leave;
|
|
}
|
|
|
|
if (!bAutoTuning)
|
|
{
|
|
|
|
//query for initial isb
|
|
if (SOCKET_ERROR == (idealsendbacklogquery(sa,
|
|
(PULONG)&isb
|
|
)))
|
|
{
|
|
ERR(TEXT("idealsendbacklogquery"));
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
hEvents[1] = ov.hEvent = CreateEvent(NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
//post inital notify
|
|
if (SOCKET_ERROR == idealsendbacklognotify(sa,
|
|
&ov,
|
|
NULL
|
|
))
|
|
{
|
|
err = WSAGetLastError();
|
|
if (WSA_IO_PENDING != err)
|
|
{
|
|
ERR(TEXT("idealsendbacklognotify"));
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
//send data until error or exit
|
|
for (;;)
|
|
{
|
|
if (SOCKET_ERROR == (rc = send(sa,
|
|
buffer,
|
|
isb,
|
|
0
|
|
)))
|
|
{
|
|
ERR(TEXT("send"));
|
|
__leave;
|
|
}
|
|
|
|
TotalBytes += (_int64)rc;
|
|
|
|
_tprintf(TEXT("Last send completed with %d bytes. Total sent: %I64d \r\r"),rc,TotalBytes);
|
|
|
|
rc = WaitForMultipleObjects(2,
|
|
hEvents,
|
|
FALSE,
|
|
1
|
|
);
|
|
|
|
switch (rc)
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
//exit event
|
|
__leave;
|
|
break;
|
|
case WAIT_OBJECT_0+1:
|
|
//immediately post another notify so we don't lose any indications
|
|
if (SOCKET_ERROR == idealsendbacklognotify(sa,
|
|
&ov,
|
|
NULL
|
|
))
|
|
{
|
|
err = WSAGetLastError();
|
|
if (WSA_IO_PENDING != err)
|
|
{
|
|
ERR(TEXT("idealsendbacklognotify"));
|
|
__leave;
|
|
}
|
|
}
|
|
_tprintf(TEXT("\nisb change was indicated.\n"));
|
|
|
|
if (!bAutoTuning)
|
|
{
|
|
|
|
//query the updated backlog value
|
|
if (SOCKET_ERROR == idealsendbacklogquery(sa,
|
|
(PULONG)&isb
|
|
))
|
|
{
|
|
ERR(TEXT("idealsendbacklogquery"));
|
|
__leave;
|
|
}
|
|
_tprintf(TEXT("\nisb value indicated: %d\n"),isb);
|
|
}
|
|
break;
|
|
case WAIT_TIMEOUT:
|
|
break;
|
|
case WAIT_FAILED:
|
|
default:
|
|
_tprintf(TEXT("\nisb notify wait failed: %d\n"),rc);
|
|
__leave;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else //Originator
|
|
{
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
StringCbPrintf(szPort,
|
|
sizeof szPort,
|
|
TEXT("%d"),
|
|
port
|
|
);
|
|
|
|
if (GetAddrInfo(szServerName,
|
|
szPort,
|
|
&hints,
|
|
&res
|
|
))
|
|
{
|
|
ERR(TEXT("getaddrinfo"));
|
|
__leave;
|
|
}
|
|
|
|
sc = socket(res->ai_family,
|
|
res->ai_socktype,
|
|
res->ai_protocol
|
|
);
|
|
|
|
if (SOCKET_ERROR == connect(sc,
|
|
res->ai_addr,
|
|
(int)res->ai_addrlen
|
|
))
|
|
{
|
|
ERR(TEXT("connect"));
|
|
__leave;
|
|
}
|
|
|
|
if (SOCKET_ERROR == setsockopt(sc,
|
|
SOL_SOCKET,
|
|
SO_RCVTIMEO,
|
|
(PCHAR)&timeout,
|
|
sizeof timeout
|
|
))
|
|
{
|
|
ERR(TEXT("SO_RCVTIMEO"));
|
|
__leave;
|
|
}
|
|
|
|
//set a large receive window
|
|
rcvsize = RECV_MAX_BUFFER_SIZE;
|
|
if (SOCKET_ERROR == (rc = setsockopt(sc,
|
|
SOL_SOCKET,
|
|
SO_RCVBUF,
|
|
(PCHAR)&rcvsize,
|
|
sizeof rcvsize
|
|
)))
|
|
{
|
|
ERR(TEXT("SO_RCVBUF"));
|
|
__leave;
|
|
}
|
|
|
|
//recv data until fin, error or exit
|
|
for (;;)
|
|
{
|
|
if (WAIT_OBJECT_0 == WaitForSingleObject(hExit,
|
|
1
|
|
))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
if (SOCKET_ERROR == (rc = recv(sc,
|
|
buffer,
|
|
RECV_MAX_BUFFER_SIZE,
|
|
0
|
|
)))
|
|
{
|
|
if (WSAETIMEDOUT == WSAGetLastError())
|
|
{
|
|
_tprintf(TEXT("\nThe last recv timed-out after 5 seconds.\n"));
|
|
continue;
|
|
}
|
|
|
|
ERR(TEXT("recv"));
|
|
__leave;
|
|
}
|
|
|
|
if (0 == rc)
|
|
{
|
|
_tprintf(TEXT("\nFIN was indicated.\n"));
|
|
__leave;
|
|
}
|
|
|
|
TotalBytes += (_int64)rc;
|
|
|
|
_tprintf(TEXT("Last recv completed with %d bytes. Total recvd: %I64d \r\r"),rc,TotalBytes);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
FREE(buffer);
|
|
|
|
CLOSESOCK(sl);
|
|
CLOSESOCK(sc);
|
|
CLOSESOCK(sa);
|
|
|
|
FREEADDRINFO(res);
|
|
|
|
SetConsoleCtrlHandler(ConsoleCtrlHandler,
|
|
FALSE);
|
|
}
|
|
|
|
if(nStartup)
|
|
WSACleanup();
|
|
|
|
_tprintf(TEXT("\nExiting.\n"));
|
|
|
|
return 0;
|
|
}
|
|
|