mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
560 lines
16 KiB
560 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
RnrClnt.c
|
|
|
|
Abstract:
|
|
|
|
Test and demonstration client for the RNR (service Registration and
|
|
Name Resolution) APIs. This is a simple client application designed
|
|
to show the basic principles involved in using the RNR APIs to _write
|
|
a protocol-independent Windows Sockets client application.
|
|
|
|
This client works by examining the protocols loaded on the machine,
|
|
looking for protocols which are reliable and stream-oriented. Then
|
|
it attempts to locate and connect to the service on these protocols.
|
|
When is has successfully connected to the service, it sends
|
|
exchanges several messages with the service and then terminates the
|
|
connection.
|
|
|
|
The OpenConnection() routine implemented herein is intended to be a
|
|
demonstration of RNR functionality commonly used in
|
|
protocol-independent clients. Application writers are encouraged to
|
|
leverage this code to assist them in writing protocol-independent
|
|
applications on top of the Windows Sockets API.
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 30-June-1994
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <windows.h>
|
|
#include <winsock.h>
|
|
#include <nspapi.h>
|
|
|
|
#define DEFAULT_TRANSFER_SIZE 512
|
|
#define DEFAULT_TRANSFER_COUNT 0x10
|
|
#define DEFAULT_CONNECTION_COUNT 1
|
|
#define DEFAULT_DELAY 0
|
|
|
|
#define DEFAULT_RECEIVE_BUFFER_SIZE 4096
|
|
#define DEFAULT_SEND_BUFFER_SIZE 4096
|
|
|
|
#define MAX_PROTOCOLS 10
|
|
|
|
WSADATA WsaData;
|
|
DWORD TransferSize = DEFAULT_TRANSFER_SIZE;
|
|
DWORD TransferCount = DEFAULT_TRANSFER_COUNT;
|
|
PCHAR IoBuffer;
|
|
DWORD RepeatCount = 1;
|
|
INT ReceiveBufferSize = DEFAULT_RECEIVE_BUFFER_SIZE;
|
|
INT SendBufferSize = DEFAULT_SEND_BUFFER_SIZE;
|
|
|
|
PCHAR RemoteName = "localhost";
|
|
PCHAR ServiceTypeName = "EchoExample";
|
|
|
|
VOID
|
|
DoEcho (
|
|
IN SOCKET s
|
|
);
|
|
|
|
SOCKET
|
|
OpenConnection (
|
|
IN PTSTR ServiceName,
|
|
IN LPGUID ServiceType,
|
|
IN BOOL Reliable,
|
|
IN BOOL MessageOriented,
|
|
IN BOOL StreamOriented,
|
|
IN BOOL Connectionless,
|
|
OUT PINT ProtocolUsed
|
|
);
|
|
|
|
void _CRTAPI1
|
|
main (
|
|
int argc,
|
|
char *argv[]
|
|
)
|
|
{
|
|
INT err;
|
|
DWORD i;
|
|
DWORD protocol[2];
|
|
SOCKET s;
|
|
BYTE buffer[1024];
|
|
BYTE buffer2[1024];
|
|
DWORD bytesRequired;
|
|
PPROTOCOL_INFO protocolInfo;
|
|
GUID serviceType;
|
|
|
|
//
|
|
// Initialize the Windows Sockets DLL.
|
|
//
|
|
|
|
err = WSAStartup( 0x0101, &WsaData );
|
|
if ( err == SOCKET_ERROR ) {
|
|
printf( "WSAStartup() failed: %ld\n", GetLastError( ) );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Parse command-line arguments.
|
|
//
|
|
|
|
for ( i = 1; i < (ULONG)argc != 0; i++ ) {
|
|
|
|
if ( _strnicmp( argv[i], "/name:", 6 ) == 0 ) {
|
|
RemoteName = argv[i] + 6;
|
|
} else if ( _strnicmp( argv[i], "/type:", 6 ) == 0 ) {
|
|
ServiceTypeName = argv[i] + 6;
|
|
} else if ( _strnicmp( argv[i], "/size:", 6 ) == 0 ) {
|
|
TransferSize = atoi( argv[i] + 6 );
|
|
} else if ( _strnicmp( argv[i], "/count:", 7 ) == 0 ) {
|
|
TransferCount = atoi( argv[i] + 7 );
|
|
} else if ( _strnicmp( argv[i], "/rcvbuf:", 8 ) == 0 ) {
|
|
ReceiveBufferSize = atoi( argv[i] + 8 );
|
|
} else if ( _strnicmp( argv[i], "/sndbuf:", 8 ) == 0 ) {
|
|
SendBufferSize = atoi( argv[i] + 8 );
|
|
} else {
|
|
printf( "Usage: rnrclnt [/name:SVCNAME] [/type:TYPENAME] [/size:N]\n" );
|
|
printf( " [/count:N] [/rcvbuf:N] [/sndbuf:N]\n" );
|
|
exit( 0 );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate memory to hold the network I/O buffer.
|
|
//
|
|
|
|
IoBuffer = malloc( TransferSize + 1 );
|
|
if ( IoBuffer == NULL ) {
|
|
printf( "Failed to allocate I/O buffer.\n" );
|
|
exit( 0 );
|
|
}
|
|
|
|
//
|
|
// Determine the type (GUID) of the service we are interested in
|
|
// connecting to.
|
|
//
|
|
|
|
err = GetTypeByName( ServiceTypeName, &serviceType );
|
|
if ( err == SOCKET_ERROR ) {
|
|
printf( "GetTypeByName for \"%s\" failed: %ld\n",
|
|
ServiceTypeName, GetLastError( ) );
|
|
exit( 0 );
|
|
}
|
|
|
|
//
|
|
// Open a connected socket to the service.
|
|
//
|
|
|
|
s = OpenConnection(
|
|
RemoteName,
|
|
&serviceType,
|
|
TRUE,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE,
|
|
&protocol[0]
|
|
);
|
|
if ( s == INVALID_SOCKET ) {
|
|
printf( "Failed to open connection to name \"%s\" type \"%s\"\n",
|
|
RemoteName, ServiceTypeName );
|
|
exit( 0 );
|
|
}
|
|
|
|
//
|
|
// The connection succeeded. Display some information on the
|
|
// protocol which was used.
|
|
//
|
|
|
|
bytesRequired = sizeof(buffer);
|
|
protocol[1] = 0;
|
|
|
|
err = EnumProtocols( protocol, buffer, &bytesRequired );
|
|
|
|
if ( err < 1 ) {
|
|
printf( "EnumProtocols failed for protocol %ld: %ld\n",
|
|
protocol[0], GetLastError( ) );
|
|
exit( 0 );
|
|
}
|
|
|
|
err = GetNameByType( &serviceType, buffer2, sizeof(buffer2) );
|
|
if ( err != NO_ERROR ) {
|
|
printf( "GetNameByType failed: %ld\n", GetLastError( ) );
|
|
exit ( 0 );
|
|
}
|
|
|
|
protocolInfo = (PPROTOCOL_INFO)buffer;
|
|
printf( "Connected to %s/%s with protocol \"%s\" (%ld)\n",
|
|
RemoteName, buffer2,
|
|
protocolInfo->lpProtocol,
|
|
protocolInfo->iProtocol );
|
|
|
|
//
|
|
// Send data to and from the service.
|
|
//
|
|
|
|
DoEcho( s );
|
|
|
|
} // main
|
|
|
|
|
|
VOID
|
|
DoEcho (
|
|
IN SOCKET s
|
|
)
|
|
{
|
|
INT err;
|
|
INT bytesReceived;
|
|
DWORD i;
|
|
DWORD startTime;
|
|
DWORD endTime;
|
|
DWORD transferStartTime;
|
|
DWORD transferEndTime;
|
|
DWORD totalTime;
|
|
INT thisTransferSize;
|
|
DWORD bytesTransferred = 0;
|
|
|
|
startTime = GetTickCount( );
|
|
|
|
for ( i = 0; i < TransferCount; i++ ) {
|
|
|
|
thisTransferSize = TransferSize;
|
|
|
|
transferStartTime = GetTickCount( );
|
|
|
|
err = send( s, IoBuffer, thisTransferSize, 0 );
|
|
if ( err != thisTransferSize ) {
|
|
printf( "send didn't work, ret = %ld, error = %ld\n", err, GetLastError( ) );
|
|
closesocket( s );
|
|
return;
|
|
}
|
|
|
|
bytesReceived = 0;
|
|
do {
|
|
err = recv( s, IoBuffer, thisTransferSize, 0 );
|
|
if ( err == SOCKET_ERROR ) {
|
|
printf( "recv failed: %ld\n", GetLastError( ) );
|
|
closesocket( s );
|
|
return;
|
|
} else if ( err == 0 && thisTransferSize != 0 ) {
|
|
printf( "socket closed prematurely by remote.\n" );
|
|
return;
|
|
}
|
|
bytesReceived += err;
|
|
} while ( bytesReceived < thisTransferSize );
|
|
|
|
transferEndTime = GetTickCount( );
|
|
printf( "%5ld bytes sent and received in %ld ms\n",
|
|
thisTransferSize, transferEndTime - transferStartTime );
|
|
|
|
bytesTransferred += thisTransferSize;
|
|
}
|
|
|
|
endTime = GetTickCount( );
|
|
totalTime = endTime - startTime;
|
|
|
|
printf( "\n%ld bytes transferred in %ld iterations, time = %ld ms\n",
|
|
bytesTransferred, TransferCount, totalTime );
|
|
printf( "Rate = %ld KB/s, %ld T/S, %ld ms/iteration\n",
|
|
(bytesTransferred / totalTime) * 2,
|
|
(TransferCount*1000) / totalTime,
|
|
totalTime / TransferCount );
|
|
|
|
err = closesocket( s );
|
|
if ( err == SOCKET_ERROR ) {
|
|
printf( "closesocket failed: %ld\n", GetLastError( ) );
|
|
return;
|
|
}
|
|
|
|
return;
|
|
|
|
} // DoEcho
|
|
|
|
|
|
SOCKET
|
|
OpenConnection (
|
|
IN PTSTR ServiceName,
|
|
IN LPGUID ServiceType,
|
|
IN BOOL Reliable,
|
|
IN BOOL MessageOriented,
|
|
IN BOOL StreamOriented,
|
|
IN BOOL Connectionless,
|
|
OUT PINT ProtocolUsed
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Examines the Windows Sockets transport protocols loaded on a machine
|
|
and determines those which support the characteristics requested by
|
|
the caller. Attempts to locate and connect to the specified service
|
|
on these protocols.
|
|
|
|
Arguments:
|
|
|
|
ServiceName - a friendly name which identifies the service we want
|
|
to connect to. On name spaces which support name resolution at
|
|
the service level (e.g. SAP) this is the name clients will use
|
|
to connect to this service. On name spaces which support name
|
|
resolution at the host level (e.g. DNS) this name is ignored
|
|
and applications must use the host name to establish
|
|
communication with this service.
|
|
|
|
ServiceType - the GUID value which uniquely identifies the type of
|
|
service we provide. A GUID is created with the UUIDGEN program.
|
|
|
|
Reliable - if TRUE, the caller requests that only transport protocols
|
|
which support reliable data delivery be used. If FALSE, both
|
|
reliable and unreliable protocols may be used.
|
|
|
|
MessageOriented - if TRUE, only message-oriented transport protocols
|
|
should be used. If FALSE, the caller either does not care
|
|
whether the protocols used are message oriented or desires only
|
|
stream-oriented protocols.
|
|
|
|
StreamOriented - if TRUE, only stream-oriented transport protocols
|
|
should be used. If FALSE, the caller either does not care
|
|
whether the protocols used are stream oriented or desires only
|
|
message-oriented protocols.
|
|
|
|
Connectionless - if TRUE, only connectionless protocols should be
|
|
used. If FALSE, both connection-oriented and connectionless
|
|
protocols may be used.
|
|
|
|
ProtocolUsed - if a connection is opened successfully, this
|
|
parameter receives the protocol ID of the protocol used to
|
|
establish the connection.
|
|
|
|
Return Value:
|
|
|
|
A connected socket handle, or INVALID_SOCKET if the connection
|
|
could not be established.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT protocols[MAX_PROTOCOLS+1];
|
|
BYTE buffer[2048];
|
|
DWORD bytesRequired;
|
|
INT err;
|
|
PPROTOCOL_INFO protocolInfo;
|
|
PCSADDR_INFO csaddrInfo;
|
|
INT protocolCount;
|
|
INT addressCount;
|
|
INT i;
|
|
DWORD protocolIndex;
|
|
SOCKET s;
|
|
|
|
//
|
|
// First look up the protocols installed on this machine. The
|
|
// EnumProtocols() API returns about all the Windows Sockets
|
|
// protocols loaded on this machine, and we'll use this information
|
|
// to identify the protocols which provide the necessary semantics.
|
|
//
|
|
|
|
bytesRequired = sizeof(buffer);
|
|
|
|
err = EnumProtocols( NULL, buffer, &bytesRequired );
|
|
if ( err <= 0 ) {
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
//
|
|
// Walk through the available protocols and pick out the ones which
|
|
// support the desired characteristics.
|
|
//
|
|
|
|
protocolCount = err;
|
|
protocolInfo = (PPROTOCOL_INFO)buffer;
|
|
|
|
for ( i = 0, protocolIndex = 0;
|
|
i < protocolCount && protocolIndex < MAX_PROTOCOLS;
|
|
i++, protocolInfo++ ) {
|
|
|
|
//
|
|
// If "reliable" support is requested, then check if supported
|
|
// by this protocol. Reliable support means that the protocol
|
|
// guarantees delivery of data in the order in which it is sent.
|
|
// Note that we assume here that if the caller requested reliable
|
|
// service then they do not want a connectionless protocol.
|
|
//
|
|
|
|
if ( Reliable ) {
|
|
|
|
//
|
|
// Check to see if the protocol is reliable. It must
|
|
// guarantee both delivery of all data and the order in
|
|
// which the data arrives. Also, it must not be a
|
|
// connectionless protocol.
|
|
//
|
|
|
|
if ( (protocolInfo->dwServiceFlags &
|
|
XP_GUARANTEED_DELIVERY) == 0 ||
|
|
(protocolInfo->dwServiceFlags &
|
|
XP_GUARANTEED_ORDER) == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
if ( (protocolInfo->dwServiceFlags & XP_CONNECTIONLESS) != 0 ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check to see that the protocol matches the stream/message
|
|
// characteristics requested. A stream oriented protocol
|
|
// either has the XP_MESSAGE_ORIENTED bit turned off, or
|
|
// else supports "pseudo stream" capability. Pseudo stream
|
|
// means that although the underlying protocol is message
|
|
// oriented, the application may open a socket of type
|
|
// SOCK_STREAM and the protocol will hide message boundaries
|
|
// from the application.
|
|
//
|
|
|
|
if ( StreamOriented &&
|
|
(protocolInfo->dwServiceFlags & XP_MESSAGE_ORIENTED) != 0 &&
|
|
(protocolInfo->dwServiceFlags & XP_PSEUDO_STREAM) == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
if ( MessageOriented &&
|
|
(protocolInfo->dwServiceFlags & XP_MESSAGE_ORIENTED) == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
} else if ( Connectionless ) {
|
|
|
|
//
|
|
// Make sure that this is a connectionless protocol. In a
|
|
// connectionless protocol, data is sent as discrete
|
|
// datagrams with no connection establishment required.
|
|
// Connectionless protocols typically have no reliability
|
|
// guarantees.
|
|
//
|
|
|
|
if ( (protocolInfo->dwServiceFlags & XP_CONNECTIONLESS) != 0 ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This protocol fits all the criteria. Add it to the list of
|
|
// protocols in which we're interested.
|
|
//
|
|
|
|
protocols[protocolIndex++] = protocolInfo->iProtocol;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that we found at least one acceptable protocol. If
|
|
// there no protocols on this machine which meet the caller's
|
|
// requirements then fail here.
|
|
//
|
|
|
|
if ( protocolIndex == 0 ) {
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
protocols[protocolIndex] = 0;
|
|
|
|
//
|
|
// Now attempt to find the address of the service to which we're
|
|
// connecting. Note that we restrict the scope of the search to
|
|
// those protocols of interest by passing the protocol array we
|
|
// generated above to GetAddressByName(). This forces
|
|
// GetAddressByName() to return socket addresses for only the
|
|
// protocols we specify, ignoring possible addresses for protocols
|
|
// we cannot support because of the caller's constraints.
|
|
//
|
|
|
|
bytesRequired = sizeof(buffer);
|
|
|
|
err = GetAddressByName(
|
|
NS_DEFAULT,
|
|
ServiceType,
|
|
ServiceName,
|
|
protocols,
|
|
0, // dwResolution
|
|
NULL, // lpServiceAsyncInfo
|
|
buffer,
|
|
&bytesRequired,
|
|
NULL, // lpAliasBuffer
|
|
NULL // lpdwAliasBufferLength
|
|
);
|
|
if ( err <= 0 ) {
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
//
|
|
// For each address, open a socket and attempt to connect. Note that
|
|
// if anything fails for a particular protocol we just skip on to
|
|
// the next protocol. As soon as we have established a connection,
|
|
// quit trying.
|
|
//
|
|
|
|
addressCount = err;
|
|
csaddrInfo = (PCSADDR_INFO)buffer;
|
|
|
|
for ( i = 0; i < addressCount; i++, csaddrInfo++ ) {
|
|
|
|
//
|
|
// Open the socket. Note that we manually specify stream type
|
|
// if so requested in case the protocol is natively a message
|
|
// protocol but supports stream semantics.
|
|
//
|
|
|
|
s = socket( csaddrInfo->LocalAddr.lpSockaddr->sa_family,
|
|
StreamOriented ? SOCK_STREAM : csaddrInfo->iSocketType,
|
|
csaddrInfo->iProtocol );
|
|
if ( s == INVALID_SOCKET ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Bind the socket to the local address specified.
|
|
//
|
|
|
|
err = bind( s, csaddrInfo->LocalAddr.lpSockaddr,
|
|
csaddrInfo->LocalAddr.iSockaddrLength );
|
|
if ( err != NO_ERROR ) {
|
|
closesocket( s );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Attempt to connect the socket to the service. If this fails,
|
|
// keep trying on other protocols.
|
|
//
|
|
|
|
err = connect( s, csaddrInfo->RemoteAddr.lpSockaddr,
|
|
csaddrInfo->RemoteAddr.iSockaddrLength );
|
|
if ( err != NO_ERROR ) {
|
|
closesocket( s );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The socket was successfully connected. Remember the protocol
|
|
// used and return the socket handle to the caller.
|
|
//
|
|
|
|
*ProtocolUsed = csaddrInfo->iProtocol;
|
|
return s;
|
|
}
|
|
|
|
//
|
|
// We failed to connect to the service.
|
|
//
|
|
|
|
return INVALID_SOCKET;
|
|
|
|
} // OpenConnection
|