2025-11-28 00:35:46 +09:00

530 lines
16 KiB
C++

/******************************************************************************\
* simples.c - Simple TCP/UDP server using Winsock 1.1
* This is a part of the Microsoft Source Code Samples.
* Copyright 1996 - 2000 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.
\******************************************************************************/
#ifdef _IA64_
#pragma warning(disable:4127)
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#include <wspiapi.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEFAULT_PORT "5001" // Default listening port
#define DEFAULT_PROTO SOCK_STREAM // Default to TCP protocol
#define DEFAULT_BUFFER_LEN 4096 // Default recv buffer length
// Function prototype
DWORD WINAPI ServerThread(LPVOID lpParam);
//
// Function: Usage
//
// Description:
// Print usage information to the console and exits.
//
void Usage(char *progname)
{
fprintf(stderr,"Usage\n%s -p [protocol] -e [endpoint] -i [interface] [-4] [-6]\n"
"Where:\n"
"\t-p protocol - is one of \"TCP\" or \"UDP\"\n"
"\t-e endpoint - is the port to listen on\n"
"\t-i interface - is the string local address to bind to\n"
"\t-4 - force IPv4\n"
"\t-6 - force IPv6\n"
"\n"
"Defaults are TCP,5001 and INADDR_ANY and IN6ADDR_ANY (if IPv6 present)\n",
progname
);
WSACleanup();
exit(1);
}
//
// Function: main
//
// Description:
// Parse the command line arguments and resolve the given local interface.
// By default we will request AF_UNSPEC which may cause getaddrinfo to return
// multiple addresses. In this case, we will create a server socket for each
// address returned (e.g. IPv4 and IPv6). A thread will be created for each
// socket to service the server socket. For TCP just a single client is handled
// at a time. For UDP, we'll simply receive a datagram and send it back to its
// source. The main thread waits until all child threads have terminated at which
// point it cleans up all resources and exits.
//
int __cdecl main(int argc, char **argv)
{
struct addrinfo hints,
*results = NULL,
*addrptr = NULL;
WSADATA wsaData;
SOCKET *server_sockets = NULL;
HANDLE *server_threads = NULL;
char hoststr[NI_MAXHOST],
servstr[NI_MAXSERV];
char *interface = NULL,
*port = DEFAULT_PORT;
int socket_type = DEFAULT_PROTO,
address_family = AF_UNSPEC, // Default to any (IPv4 or IPv6)
socket_count = 0,
retval,
i;
/* Parse arguments */
if (argc >1)
{
for (i=1;i <argc;i++)
{
if ( (strlen(argv[i]) == 2) && ( (argv[i][0] == '-') || (argv[i][0] == '/') ) )
{
switch (tolower(argv[i][1]))
{
case '4': // IPv4
address_family = AF_INET;
break;
case '6': // IPv6
address_family = AF_INET6;
break;
case 'p':
if (!_strnicmp(argv[i+1], "TCP", 3) )
socket_type = SOCK_STREAM;
else if (!_strnicmp(argv[i+1], "UDP", 3) )
socket_type = SOCK_DGRAM;
else
Usage(argv[0]);
i++;
break;
case 'i': // Local interface to listen/recv on
interface = argv[++i];
break;
case 'e': // Port number to listen/recv on
port = argv[++i];
break;
default:
Usage(argv[0]);
break;
}
}
else
Usage(argv[0]);
}
}
// Load Winsock
if ((retval = WSAStartup(0x202,&wsaData)) != 0)
{
fprintf(stderr,"WSAStartup failed with error %d\n",retval);
WSACleanup();
return -1;
}
// Make sure the supplied port isn't wildcard
if ( _strnicmp(port, "0", 1) == 0)
{
Usage(argv[0]);
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = address_family;
hints.ai_socktype = socket_type;
hints.ai_protocol = ((socket_type == SOCK_DGRAM) ? IPPROTO_UDP : IPPROTO_TCP);
// If interface is NULL then request the passive "bind" address
hints.ai_flags = ((interface == NULL) ? AI_PASSIVE : 0);
retval = getaddrinfo(interface, port, &hints, &results);
if (retval != 0)
{
fprintf(stderr, "getaddrinfo failed: %d\n", retval);
goto cleanup;
}
// Make sure we got at least one address back
if (results == NULL)
{
fprintf(stderr, "Unable to resolve interface %s\n", interface);
goto cleanup;
}
// Count how many addresses were returned
addrptr = results;
while (addrptr)
{
socket_count++;
addrptr = addrptr->ai_next;
}
// Allocate space for the server sockets
server_sockets = (SOCKET *)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(SOCKET) * socket_count
);
if (server_sockets == NULL)
{
fprintf(stderr, "HeapAlloc failed: %d\n", GetLastError());
goto cleanup;
}
// Initialize the socket array first
for (i=0; i < socket_count ;i++)
server_sockets[i] = INVALID_SOCKET;
// Create the server sockets - one for each address returned
socket_count = 0;
addrptr = results;
while (addrptr)
{
// Create the socket according to the parameters returned
server_sockets[socket_count] = socket(
addrptr->ai_family,
addrptr->ai_socktype,
addrptr->ai_protocol
);
if (server_sockets[socket_count] == INVALID_SOCKET)
{
fprintf(stderr, "socket failed: %d\n", WSAGetLastError());
goto cleanup;
}
// Bind the socket to the address returned
retval = bind(server_sockets[socket_count],
addrptr->ai_addr,
(int)addrptr->ai_addrlen
);
if (retval == SOCKET_ERROR)
{
fprintf(stderr, "bind failed: %d\n", WSAGetLastError());
goto cleanup;
}
// If a TCP socket, call listen on it
if (addrptr->ai_socktype == SOCK_STREAM)
{
retval = listen(
server_sockets[socket_count],
7
);
if (retval == SOCKET_ERROR)
{
fprintf(stderr, "listen failed: %d\n", WSAGetLastError());
goto cleanup;
}
}
// Print the address this socket is bound to
retval = getnameinfo(
addrptr->ai_addr,
(socklen_t)addrptr->ai_addrlen,
hoststr,
NI_MAXHOST,
servstr,
NI_MAXSERV,
NI_NUMERICHOST | NI_NUMERICSERV
);
if (retval != 0)
{
fprintf(stderr, "getnameinfo failed: %d\n", retval);
goto cleanup;
}
fprintf(stdout, "socket 0x%x bound to address %s and port %s\n",
server_sockets[socket_count], hoststr, servstr);
// Increment the socket count again
socket_count++;
// Increment the address pointer
addrptr = addrptr->ai_next;
}
// We need a server thread per socket so allocate space for the thread handle
server_threads = (HANDLE *)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(HANDLE) * socket_count
);
if (server_threads == NULL)
{
fprintf(stderr, "HeapAlloc failed: %d\n", GetLastError());
goto cleanup;
}
// Create a thread for each address family which will handle that socket
for (i=0; i < socket_count ;i++)
{
server_threads[i] = CreateThread(
NULL,
0,
ServerThread,
(LPVOID)server_sockets[i],
0,
NULL
);
if (server_threads[i] == NULL)
{
fprintf(stderr, "CreateThread failed: %d\n", GetLastError());
goto cleanup;
}
}
// Wait until the threads exit, then cleanup
retval = WaitForMultipleObjects(
socket_count,
server_threads,
TRUE,
INFINITE
);
if ((retval == WAIT_FAILED) || (retval == WAIT_TIMEOUT))
{
fprintf(stderr, "WaitForMultipleObjects failed: %d\n", GetLastError());
goto cleanup;
}
cleanup:
if (results != NULL)
{
freeaddrinfo(results);
results = NULL;
}
// Release socket resources
if (server_sockets != NULL)
{
for (i=0; i < socket_count ;i++)
{
if (server_sockets[i] != INVALID_SOCKET)
closesocket(server_sockets[i]);
server_sockets[i] = INVALID_SOCKET;
}
// Free the array
HeapFree(GetProcessHeap(), 0, server_sockets);
server_sockets = NULL;
}
// Release thread resources
if (server_threads != NULL)
{
for (i=0; i < socket_count ;i++)
{
if (server_threads[i] != NULL)
CloseHandle(server_threads[i]);
server_threads[i] = NULL;
}
// Free the array
HeapFree(GetProcessHeap(), 0, server_threads);
server_threads = NULL;
}
return 0;
}
//
// Function: ServerThread
//
// Description:
// This routine services a single server socket. For TCP this means accept
// a client connection and then recv and send in a loop. When the client
// closes the connection, wait for another client, etc. For UDP, we simply
// sit in a loop and recv a datagram and echo it back to its source. For any
// error, this routine exits.
//
DWORD WINAPI ServerThread(LPVOID lpParam)
{
SOCKET s, // Server socket
sc = INVALID_SOCKET; // Client socket (TCP)
SOCKADDR_STORAGE from;
char Buffer[DEFAULT_BUFFER_LEN],
servstr[NI_MAXSERV],
hoststr[NI_MAXHOST];
int socket_type,
retval,
fromlen,
bytecount;
// Retrieve the socket handle
s = (SOCKET) lpParam;
// Get the socket type back
fromlen = sizeof(socket_type);
retval = getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&socket_type, &fromlen);
if (retval == INVALID_SOCKET)
{
fprintf(stderr, "getsockopt(SO_TYPE) failed: %d\n", WSAGetLastError());
goto cleanup;
}
for (;;)
{
fromlen = sizeof(from);
if (socket_type == SOCK_STREAM)
{
if (sc != INVALID_SOCKET)
{
//
// If we have a client connection recv and send until done
//
bytecount = recv(sc, Buffer, DEFAULT_BUFFER_LEN, 0);
if (bytecount == SOCKET_ERROR)
{
fprintf(stderr, "recv failed: %d\n", WSAGetLastError());
goto cleanup;
}
else if (bytecount == 0)
{
// Client connection was closed
retval = shutdown(sc, SD_SEND);
if (retval == SOCKET_ERROR)
{
fprintf(stderr, "shutdown failed: %d\n", WSAGetLastError());
goto cleanup;
}
closesocket(sc);
sc = INVALID_SOCKET;
}
else
{
printf("read %d bytes\n", bytecount);
//
// Now echo the data back
//
bytecount = send(sc, Buffer, bytecount, 0);
if (bytecount == SOCKET_ERROR)
{
fprintf(stderr, "send failed: %d\n", WSAGetLastError());
goto cleanup;
}
printf("wrote %d bytes\n", bytecount);
}
}
else
{
//
// No client connection so wait for one
//
sc = accept(s, (SOCKADDR *)&from, &fromlen);
if (sc == INVALID_SOCKET)
{
fprintf(stderr, "accept failed: %d\n", WSAGetLastError());
goto cleanup;
}
// Display the client's address
retval = getnameinfo(
(SOCKADDR *)&from,
fromlen,
hoststr,
NI_MAXHOST,
servstr,
NI_MAXSERV,
NI_NUMERICHOST | NI_NUMERICSERV
);
if (retval != 0)
{
fprintf(stderr, "getnameinfo failed: %d\n", retval);
goto cleanup;
}
printf("Accepted connection from host %s and port %s\n",
hoststr, servstr);
}
}
else
{
//
// Receive and send data
//
bytecount = recvfrom(s, Buffer, DEFAULT_BUFFER_LEN, 0, (SOCKADDR *)&from, &fromlen);
if (bytecount == SOCKET_ERROR)
{
//
// We may get WSAECONNRESET errors in response to ICMP port unreachable
// messages so we'll just ignore those
//
if (WSAGetLastError() != WSAECONNRESET)
{
fprintf(stderr, "recvfrom failed; %d\n", WSAGetLastError());
goto cleanup;
}
else
{
continue;
}
}
//
// Display the source of the datagram
//
retval = getnameinfo(
(SOCKADDR *)&from,
fromlen,
hoststr,
NI_MAXHOST,
servstr,
NI_MAXSERV,
NI_NUMERICHOST | NI_NUMERICSERV
);
if (retval != 0)
{
fprintf(stderr, "getnameinfo failed: %d\n", retval);
goto cleanup;
}
printf("read %d bytes from host %s and port %s\n",
bytecount, hoststr, servstr);
//
// Echo the datagram back
//
bytecount = sendto(s, Buffer, bytecount, 0, (SOCKADDR *)&from, fromlen);
if (bytecount == SOCKET_ERROR)
{
fprintf(stderr, "sendto failed: %d\n", WSAGetLastError());
goto cleanup;
}
printf("sent %d bytes to host %s and port %s\n",
bytecount, hoststr, servstr);
}
}
cleanup:
// Close the client connection if present
if (sc != INVALID_SOCKET)
{
closesocket(sc);
sc = INVALID_SOCKET;
}
return 0;
}