태그 글목록: 비동기서버

[TCP/IP] 에코서버 & 클라이언트 – overlapped IO (Callback) 2015-05-28

서버

/*
## 소켓 서버 : 1 v n - overlapped callback
1. socket()			: 소켓생성
2. bind()			: 소켓설정
3. listen()			: 수신대기열생성
4. accept()			: 연결대기
5. read()&write()
	WIN recv()&send	: 데이터 읽고쓰기
6. close()
	WIN closesocket	: 소켓종료
*/

#include "stdafx.h"
#include <winsock2.h>

#pragma comment(lib, "Ws2_32.lib")

#define MAX_BUFFER		1024
#define SERVER_PORT		3500

struct SOCKETINFO
{
	WSAOVERLAPPED overlapped;
	WSABUF dataBuffer;
	SOCKET socket;
	char messageBuffer[MAX_BUFFER];
	int receiveBytes;
	int sendBytes;
};

void CALLBACK callback(DWORD Error, DWORD dataBytes, LPWSAOVERLAPPED overlapped, DWORD lnFlags);

int _tmain(int argc, _TCHAR* argv[])
{
	// Winsock Start - windock.dll 로드
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
	{
		printf("Error - Can not load 'winsock.dll' file\n");
		return 1;
	}

	// 1. 소켓생성  
	SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (listenSocket == INVALID_SOCKET)
	{
		printf("Error - Invalid socket\n");
		return 1;
	}

	// 서버정보 객체설정
	SOCKADDR_IN serverAddr;
	memset(&serverAddr, 0, sizeof(SOCKADDR_IN));
	serverAddr.sin_family = PF_INET;
	serverAddr.sin_port = htons(SERVER_PORT);
	serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

	// 2. 소켓설정
	if (bind(listenSocket, (struct sockaddr*)&serverAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
	{
		printf("Error - Fail bind\n");
		// 6. 소켓종료
		closesocket(listenSocket);
		// Winsock End
		WSACleanup();
		return 1;
	}

	// 3. 수신대기열생성
	if (listen(listenSocket, 5) == SOCKET_ERROR)
	{
		printf("Error - Fail listen\n");
		// 6. 소켓종료
		closesocket(listenSocket);
		// Winsock End
		WSACleanup();
		return 1;
	}

	SOCKADDR_IN clientAddr;
	int addrLen = sizeof(SOCKADDR_IN);
	memset(&clientAddr, 0, addrLen);
	SOCKET clientSocket;
	SOCKETINFO *socketInfo;
	DWORD receiveBytes;
	DWORD flags;
	
	while (1)
	{
		clientSocket = accept(listenSocket, (struct sockaddr *)&clientAddr, &addrLen);
		if (clientSocket == INVALID_SOCKET)
		{
			printf("Error - Accept Failure\n");
			return 1;
		}

		socketInfo = (struct SOCKETINFO *)malloc(sizeof(struct SOCKETINFO));
		memset((void *)socketInfo, 0x00, sizeof(struct SOCKETINFO));
		socketInfo->socket = clientSocket;
		socketInfo->dataBuffer.len = MAX_BUFFER;
		socketInfo->dataBuffer.buf = socketInfo->messageBuffer;
		flags = 0;

		// 중첩 소캣을 지정하고 완료시 실행될 함수를 넘겨준다.
		if (WSARecv(socketInfo->socket, &socketInfo->dataBuffer, 1, &receiveBytes, &flags, &(socketInfo->overlapped), callback))
		{
			if (WSAGetLastError() != WSA_IO_PENDING)
			{
				printf("Error - IO pending Failure\n");
				return 1;
			}
		}
	}

	// 6-2. 리슨 소켓종료
	closesocket(listenSocket);

	// Winsock End
	WSACleanup();
	
	return 0;
}

void CALLBACK callback(DWORD Error, DWORD dataBytes, LPWSAOVERLAPPED overlapped, DWORD lnFlags)
{
	struct SOCKETINFO *socketInfo;
	DWORD sendBytes = 0;
	DWORD receiveBytes = 0;
	DWORD flags = 0;

	socketInfo = (struct SOCKETINFO *)overlapped;
	memset(&(socketInfo->overlapped), 0x00, sizeof(WSAOVERLAPPED));

	if (dataBytes == 0)
	{
		closesocket(socketInfo->socket);
		free(socketInfo);
		return;
	}

	if (socketInfo->receiveBytes == 0)
	{
		// WSARecv(최초 대기에 대한)의 콜백일 경우
		socketInfo->receiveBytes = dataBytes;
		socketInfo->sendBytes = 0;		
		socketInfo->dataBuffer.buf = socketInfo->messageBuffer;
		socketInfo->dataBuffer.len = socketInfo->receiveBytes;

		printf("TRACE - Receive message : %s (%d bytes)\n", socketInfo->messageBuffer, dataBytes);

		if (WSASend(socketInfo->socket, &(socketInfo->dataBuffer), 1, &sendBytes, 0, &(socketInfo->overlapped), callback) == SOCKET_ERROR)
		{
			if (WSAGetLastError() != WSA_IO_PENDING)
			{
				printf("Error - Fail WSASend(error_code : %d)\n", WSAGetLastError());
			}			
		}
	}
	else
	{
		// WSASend(응답에 대한)의 콜백일 경우
		socketInfo->sendBytes += dataBytes;
		socketInfo->receiveBytes = 0;		
		socketInfo->dataBuffer.len = MAX_BUFFER;
		socketInfo->dataBuffer.buf = socketInfo->messageBuffer;

		printf("TRACE - Send message : %s (%d bytes)\n", socketInfo->messageBuffer, dataBytes);

		if (WSARecv(socketInfo->socket, &socketInfo->dataBuffer, 1, &receiveBytes, &flags, &(socketInfo->overlapped), callback) == SOCKET_ERROR)
		{
			if (WSAGetLastError() != WSA_IO_PENDING)
			{
				printf("Error - Fail WSARecv(error_code : %d)\n", WSAGetLastError());
			}			
		}
	}
}

클라이언트

/*
## 소켓 서버 : 1 v n - overlapped callback
1. socket()			: 소켓생성
2. connect()		: 연결요청
3. read()&write()
	WIN recv()&send	: 데이터 읽고쓰기
4. close()
	WIN closesocket	: 소켓종료
*/

#include "stdafx.h"
#include <winsock2.h>

#pragma comment(lib, "Ws2_32.lib")

#define MAX_BUFFER		1024
#define SERVER_IP		"127.0.0.1"
#define SERVER_PORT		3500

struct SOCKETINFO
{	
	WSAOVERLAPPED overlapped;
	WSABUF dataBuffer;	
	int receiveBytes;
	int sendBytes;
};

int _tmain(int argc, _TCHAR* argv[])
{	
	// Winsock Start - winsock.dll 로드
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 0), &WSAData) != 0)
	{
		printf("Error - Can not load 'winsock.dll' file\n");
		return 1;
	}

	// 1. 소켓생성
	SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (listenSocket == INVALID_SOCKET)
	{
		printf("Error - Invalid socket\n");
		return 1;
	}

	// 서버정보 객체설정
	SOCKADDR_IN serverAddr;
	memset(&serverAddr, 0, sizeof(SOCKADDR_IN));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(SERVER_PORT);
	serverAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);

	// 2. 연결요청
	if (connect(listenSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
	{
		printf("Error - Fail to connect\n");
		// 4. 소켓종료
		closesocket(listenSocket);
		// Winsock End
		WSACleanup();
		return 1;
	}
	else
	{
		printf("Server Connected\n* Enter Message\n->");
	}
	
	SOCKETINFO *socketInfo;
	DWORD sendBytes;
	DWORD receiveBytes;
	DWORD flags;

	while (1)
	{		
		// 메시지 입력
		char messageBuffer[MAX_BUFFER];
		int i, bufferLen;
		for (i = 0; 1; i++)
		{
			messageBuffer[i] = getchar();
			if (messageBuffer[i] == '\n')
			{
				messageBuffer[i++] = '\0';
				break;
			}
		}
		bufferLen = i;
		
		socketInfo = (struct SOCKETINFO *)malloc(sizeof(struct SOCKETINFO));
		memset((void *)socketInfo, 0x00, sizeof(struct SOCKETINFO));		
		socketInfo->dataBuffer.len = bufferLen;
		socketInfo->dataBuffer.buf = messageBuffer;

		// 3-1. 데이터 쓰기
		int sendBytes = send(listenSocket, messageBuffer, bufferLen, 0);
		if (sendBytes > 0)
		{
			printf("TRACE - Send message : %s (%d bytes)\n", messageBuffer, sendBytes);
			// 3-2. 데이터 읽기
			int receiveBytes = recv(listenSocket, messageBuffer, MAX_BUFFER, 0);
			if (receiveBytes > 0)
			{
				printf("TRACE - Receive message : %s (%d bytes)\n* Enter Message\n->", messageBuffer, receiveBytes);
			}			
		}
		
	}		

	// 4. 소켓종료
	closesocket(listenSocket);

	// Winsock End
	WSACleanup();
	
	return 0;
}

[TCP/IP] 에코서버 & 클라이언트 – overlapped IO (Event) 2015-05-27

서버

/*
## 소켓 서버 : 1 v n - overlapped event
1. socket()			: 소켓생성
2. bind()			: 소켓설정
3. listen()			: 수신대기열생성
4. accept()			: 연결대기
5. read()&write()
	WIN recv()&send	: 데이터 읽고쓰기
6. close()
	WIN closesocket	: 소켓종료
*/

#include "stdafx.h"
#include <winsock2.h>

#pragma comment(lib, "Ws2_32.lib")

#define MAX_BUFFER		1024
#define SERVER_PORT		3500

struct SOCKETINFO
{
	WSAOVERLAPPED overlapped;
	WSABUF dataBuffer;
	SOCKET socket;
	char messageBuffer[MAX_BUFFER];
	int receiveBytes;
	int sendBytes;
};
struct THREADINFO
{
	SOCKETINFO **sockets;
	WSAEVENT *events;
	int *eventCount;	
};

CRITICAL_SECTION criticalSection;

DWORD WINAPI makeThread(LPVOID workerThreadInfo);

int _tmain(int argc, _TCHAR* argv[])
{
	struct SOCKETINFO *sockets[WSA_MAXIMUM_WAIT_EVENTS];
	WSAEVENT events[WSA_MAXIMUM_WAIT_EVENTS];

	int totalIndex = 0;
	int listenSocketIndex = 0;


	// 임계영역 초기화
	InitializeCriticalSection(&criticalSection);
		
	// Winsock Start - windock.dll 로드
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
	{
		printf("Error - Can not load 'winsock.dll' file\n");
		return 1;
	}

	// 1. 소켓생성  
	SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (listenSocket == INVALID_SOCKET)
	{
		printf("Error - Invalid socket\n");
		return 1;
	}

	// 서버정보 객체설정
	SOCKADDR_IN serverAddr;
	memset(&serverAddr, 0, sizeof(SOCKADDR_IN));
	serverAddr.sin_family = PF_INET;
	serverAddr.sin_port = htons(SERVER_PORT);
	serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

	// 2. 소켓설정
	if (bind(listenSocket, (struct sockaddr*)&serverAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
	{
		printf("Error - Fail bind\n");
		// 6. 소켓종료
		closesocket(listenSocket);
		// Winsock End
		WSACleanup();
		return 1;
	}

	// 3. 수신대기열생성
	if (listen(listenSocket, 5) == SOCKET_ERROR)
	{
		printf("Error - Fail listen\n");
		// 6. 소켓종료
		closesocket(listenSocket);
		// Winsock End
		WSACleanup();
		return 1;
	}

	// 스레드 객체 설정	
	struct THREADINFO *workerThreadInfo;
	workerThreadInfo = (struct THREADINFO *)malloc(sizeof(struct THREADINFO));
	unsigned long threadId;	
		
	// - 소켓생성
	sockets[listenSocketIndex] = (struct SOCKETINFO *)malloc(sizeof(struct SOCKETINFO));
	memset((struct SOCKETINFO *)sockets[listenSocketIndex], 0x00, sizeof(struct SOCKETINFO));
	sockets[listenSocketIndex]->socket = listenSocket;
	sockets[listenSocketIndex]->receiveBytes = 0;
	sockets[listenSocketIndex]->sendBytes = 0;
	
	// - 이벤트 객체 생성
	events[listenSocketIndex] = WSACreateEvent();
	if (events[listenSocketIndex] == WSA_INVALID_EVENT)
	{
		printf("Error - Event Create Failure\n");
		return 1;
	}
	
	// 스레드 생성
	totalIndex = listenSocketIndex + 1;
	workerThreadInfo->eventCount = &totalIndex;
	workerThreadInfo->sockets = sockets;
	workerThreadInfo->events = events;
	
	HANDLE hThread = CreateThread(NULL, 0, makeThread, workerThreadInfo, 0, &threadId);
	if (hThread == NULL)
	{
		printf("Error - Thread Create Failure\n");
		return 1;
	}
	
	SOCKADDR_IN clientAddr;
	int addrLen = sizeof(SOCKADDR_IN);
	memset(&clientAddr, 0, addrLen);
	SOCKET clientSocket;
	SOCKETINFO *socketInfo;
	DWORD receiveBytes;
	DWORD flags;
	
	while (1)
	{
		clientSocket = accept(listenSocket, (struct sockaddr *)&clientAddr, &addrLen);
		if (clientSocket == INVALID_SOCKET)
		{
			printf("Error - Accept Failure\n");
			return 1;
		}

		// CriticalSection - START
		EnterCriticalSection(&criticalSection);
		
		socketInfo = (struct SOCKETINFO *)malloc(sizeof(struct SOCKETINFO));
		memset((void *)socketInfo, 0x00, sizeof(struct SOCKETINFO));
				
		sockets[totalIndex] = socketInfo;
		socketInfo->socket = clientSocket;
		socketInfo->dataBuffer.len = MAX_BUFFER;
		socketInfo->dataBuffer.buf = sockets[totalIndex]->messageBuffer;

		events[totalIndex] = WSACreateEvent();
		socketInfo->overlapped.hEvent = events[totalIndex];

		flags = 0;
		WSARecv(sockets[totalIndex]->socket, &sockets[totalIndex]->dataBuffer, 1, &receiveBytes, &flags, &(sockets[totalIndex]->overlapped), NULL);

		totalIndex = totalIndex + 1;

		LeaveCriticalSection(&criticalSection);
		// CriticalSection - END

		// event 오브젝트를 신호대기상태로 만든다.
		WSASetEvent(events[listenSocketIndex]);
	}

	// 6-2. 리슨 소켓종료
	closesocket(listenSocket);

	// Winsock End
	WSACleanup();
	
	return 0;
}

DWORD WINAPI makeThread(LPVOID workerThreadInfo)
{
	struct THREADINFO *threadInfo = (struct THREADINFO *)workerThreadInfo;
	int eventNumber;	
	struct SOCKETINFO *eventSocket;
	DWORD receiveBytes;
	DWORD flags;
	
	while (1)
	{
		// 이벤트 대기
		eventNumber = WSAWaitForMultipleEvents(*threadInfo->eventCount, threadInfo->events, FALSE, WSA_INFINITE, FALSE);
		if (eventNumber == WSA_WAIT_FAILED)
		{
			printf("Error - Wait Failure\n");
			continue;
		}
		
		// 이벤트 리셋 : 이벤트가 일어난 소켓을 찾아내고 이벤트 객체는 다시 대기상태가 된다.
		eventSocket = threadInfo->sockets[eventNumber - WSA_WAIT_EVENT_0];
		WSAResetEvent(threadInfo->events[eventNumber - WSA_WAIT_EVENT_0]);

		// 듣기소켓 관련해서는 처리하지 않는다.
		if ((eventNumber - WSA_WAIT_EVENT_0) == 0)
		{			
			continue;
		}

		// 중첩 소켓을 이용해 데이터를 처리 : 다른일을 처리하고 있다가 데이터 입력이 완료되면 이벤트가 발생 하도록 해준다.
		// ==> 리눅스와 달리 데이터 입력시 유저모드와 커널모드의 변환을 최소화해서 성능을 향상시킨다.
		if (WSAGetOverlappedResult(eventSocket->socket, &(eventSocket->overlapped), &receiveBytes, FALSE, &flags) == FALSE || receiveBytes == 0)
		{
			// 데이터 입력이 없다면 소켓 & 이벤트 객체를 닫고 메모리에서 해제한다.
			closesocket(eventSocket->socket);
			free(eventSocket);
			WSACloseEvent(threadInfo->events[eventNumber - WSA_WAIT_EVENT_0]);
			
			// 임계영역을 만들고 소캣과 이벤트 객체 저장소를 정리한다.
			EnterCriticalSection(&criticalSection);
			
			if (eventNumber - WSA_WAIT_EVENT_0 + 1 != *threadInfo->eventCount)
			{
				for (int i = eventNumber - WSA_WAIT_EVENT_0; i < *threadInfo->eventCount; i++)
				{
					threadInfo->sockets[i] = threadInfo->sockets[i + 1];
					threadInfo->events[i] = threadInfo->events[i + 1];
				}
			}
			*threadInfo->eventCount = *threadInfo->eventCount - 1;
			LeaveCriticalSection(&criticalSection);
			continue;
		}
		printf("TRACE - Receive message : %s (%d bytes)\n", eventSocket->messageBuffer, receiveBytes);

		// 이벤트 소캣 초기화
		memset((void *)&eventSocket->overlapped, 0x00, sizeof(WSAOVERLAPPED));
		eventSocket->overlapped.hEvent = threadInfo->events[eventNumber - WSA_WAIT_EVENT_0];
		eventSocket->dataBuffer.len = MAX_BUFFER;
		eventSocket->dataBuffer.buf = eventSocket->messageBuffer;

		int sendBytes = send(eventSocket->socket, eventSocket->messageBuffer, strlen(eventSocket->messageBuffer), 0);
		if (sendBytes > 0)
		{
			printf("TRACE - Send message : %s (%d bytes)\n", eventSocket->messageBuffer, sendBytes);
		}
		if (WSARecv(eventSocket->socket, &(eventSocket->dataBuffer), 1, &receiveBytes, &flags, &(eventSocket->overlapped), NULL) == SOCKET_ERROR)
		{
			if (WSAGetLastError() != ERROR_IO_PENDING)
			{
				printf("Error - IO pending Failure\n");
				return 1;
			}
		}
	}
	
	return 1;
}

클라이언트

/*
## 소켓 서버 : 1 v n - overlapped event
1. socket()			: 소켓생성
2. connect()		: 연결요청
3. read()&write()
	WIN recv()&send	: 데이터 읽고쓰기
4. close()
	WIN closesocket	: 소켓종료
*/

#include "stdafx.h"
#include <winsock2.h>

#pragma comment(lib, "Ws2_32.lib")

#define MAX_BUFFER		1024
#define SERVER_IP		"127.0.0.1"
#define SERVER_PORT		3500

int _tmain(int argc, _TCHAR* argv[])
{	
	// Winsock Start - winsock.dll 로드
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 0), &WSAData) != 0)
	{
		printf("Error - Can not load 'winsock.dll' file\n");
		return 1;
	}

	// 1. 소켓생성
	SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (listenSocket == INVALID_SOCKET)
	{
		printf("Error - Invalid socket\n");
		return 1;
	}

	// 서버정보 객체설정
	SOCKADDR_IN serverAddr;
	memset(&serverAddr, 0, sizeof(SOCKADDR_IN));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(SERVER_PORT);
	serverAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);

	// 2. 연결요청
	if (connect(listenSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
	{
		printf("Error - Fail to connect\n");
		// 4. 소켓종료
		closesocket(listenSocket);
		// Winsock End
		WSACleanup();
		return 1;
	}
	else
	{
		printf("Server Connected\n* Enter Message\n->");
	}
	
	while (1)
	{		
		// 메시지 입력
		char messageBuffer[MAX_BUFFER];
		int i, bufferLen;
		for (i = 0; 1; i++)
		{
			messageBuffer[i] = getchar();
			if (messageBuffer[i] == '\n')
			{
				messageBuffer[i++] = '\0';
				break;
			}
		}
		bufferLen = i;
		
		// 3-1. 데이터 쓰기
		int sendBytes = send(listenSocket, messageBuffer, bufferLen, 0);
		if (sendBytes > 0)
		{
			printf("TRACE - Send message : %s (%d bytes)\n", messageBuffer, sendBytes);
			// 3-2. 데이터 읽기
			int receiveBytes = recv(listenSocket, messageBuffer, MAX_BUFFER, 0);
			if (receiveBytes > 0)
			{
				printf("TRACE - Receive message : %s (%d bytes)\n* Enter Message\n->", messageBuffer, receiveBytes);
			}			
		}
		
	}		

	// 4. 소켓종료
	closesocket(listenSocket);

	// Winsock End
	WSACleanup();
	
	return 0;
}

[TCP/IP] 에코서버 & 클라이언트 – asynchronous Server 2015-05-19

서버

/*
## 소켓 서버 : 1 v n - asynchronous Server - eventSelect
1. socket()         : 소켓생성
2. bind()           : 소켓설정
3. listen()         : 수신대기열생성
4. accept()         : 연결대기
5. read()&write()
    WIN recv()&send : 데이터 읽고쓰기
6. close()
    WIN closesocket : 소켓종료
*/

#include "stdafx.h"
#include <winsock2.h>

#pragma comment(lib, "Ws2_32.lib")

#define MAX_BUFFER      1024
#define SERVER_PORT     3500

struct SOCKETINFO
{
	SOCKET socket;
	char messageBuffer[MAX_BUFFER];
	int receiveBytes;
	int sendBytes;
};

int _tmain(int argc, _TCHAR* argv[])
{
	// 1. 소켓생성 > 2. 소켓설정 > 3. 수신대기열생성
	// Winsock Start - windock.dll 로드
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
	{
		printf("Error - Can not load 'winsock.dll' file\n");
		return 1;
	}

	// 1. 소켓생성  
	SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (listenSocket == INVALID_SOCKET)
	{
		printf("Error - Invalid socket\n");
		return 1;
	}

	// 서버정보 객체설정
	SOCKADDR_IN serverAddr;
	memset(&serverAddr, 0, sizeof(SOCKADDR_IN));
	serverAddr.sin_family = PF_INET;
	serverAddr.sin_port = htons(SERVER_PORT);
	serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

	// 2. 소켓설정
	if (bind(listenSocket, (struct sockaddr*)&serverAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
	{
		printf("Error - Fail bind\n");
		// 6. 소켓종료
		closesocket(listenSocket);
		// Winsock End
		WSACleanup();
		return 1;
	}

	// 3. 수신대기열생성
	if (listen(listenSocket, 5) == SOCKET_ERROR)
	{
		printf("Error - Fail listen\n");
		// 6. 소켓종료
		closesocket(listenSocket);
		// Winsock End
		WSACleanup();
		return 1;
	}

	// Create Event - 최대 갯수의 소켓과 이벤트 객체 생성
	struct SOCKETINFO *sockets[WSA_MAXIMUM_WAIT_EVENTS];
	WSAEVENT events[WSA_MAXIMUM_WAIT_EVENTS];
	int totalIndex = 0;

	// - Listen Socket 설정
	struct SOCKETINFO *socketInfo;
	socketInfo = (struct SOCKETINFO *)malloc(sizeof(struct SOCKETINFO));
	memset((struct SOCKETINFO *)socketInfo, 0x00, sizeof(struct SOCKETINFO));
	socketInfo->socket = listenSocket;
	socketInfo->receiveBytes = 0;
	socketInfo->sendBytes = 0;
	sockets[totalIndex] = socketInfo;

	// - 이벤트 객체 생성
	events[totalIndex] = WSACreateEvent();
	if (events[totalIndex] == WSA_INVALID_EVENT)
	{
		printf("Error - Event Create Failure\n");
		return 1;
	}

	// - 이벤트 객체를 소켓과 묶음
	if (WSAEventSelect(listenSocket, events[totalIndex], FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)
	{
		printf("Error - Event Select Failure\n");
		return 1;
	}

	totalIndex = totalIndex + 1;

	WSANETWORKEVENTS networkEvents;
	SOCKADDR_IN clientAddr;
	int addrLen = sizeof(SOCKADDR_IN);
	memset(&clientAddr, 0, addrLen);
	SOCKET clientSocket;
	char messageBuffer[MAX_BUFFER];

	while (1)
	{
		// 이벤트를 기다림
		int eventIndex = WSAWaitForMultipleEvents(totalIndex, events, FALSE, WSA_INFINITE, FALSE);
		// - 예외처리
		if (eventIndex == WSA_WAIT_FAILED)
		{
			printf("Error - Event Wait Failure\n");
			break;
		}
		// - 신호상태인 이벤트객체의 정보를 가져옴
		if (WSAEnumNetworkEvents(
			sockets[eventIndex - WSA_WAIT_EVENT_0]->socket,
			events[eventIndex - WSA_WAIT_EVENT_0], &networkEvents) == SOCKET_ERROR)
		{
			printf("Error - Event Type Error\n");
			break;
		}
		// >> FD_ACCEPT : 클라이언트 요청이벤트일 경우 accept함수로 소켓을 만들고 이벤트객체와 묶음
		if (networkEvents.lNetworkEvents& FD_ACCEPT)
		{
			if (networkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)
			{
				printf("Error - Network Error\n");
				break;
			}
			if (totalIndex > WSA_MAXIMUM_WAIT_EVENTS)
			{
				printf("Error - Connection Full\n");
				break;
			}
			// 4. 연결대기
			clientSocket = accept(sockets[eventIndex - WSA_WAIT_EVENT_0]->socket, (struct sockaddr *)&clientAddr, &addrLen);
			if (clientSocket == INVALID_SOCKET)
			{
				printf("Error - Fail accept\n");
				continue;
			}
			// - 소켓생성			
			sockets[totalIndex] = (struct SOCKETINFO *)malloc(sizeof(struct SOCKETINFO));
			memset((struct SOCKETINFO *)sockets[totalIndex], 0x00, sizeof(struct SOCKETINFO));
			sockets[totalIndex]->socket = clientSocket;
			sockets[totalIndex]->receiveBytes = 0;
			sockets[totalIndex]->sendBytes = 0;

			// - 이벤트 객체 생성
			events[totalIndex] = WSACreateEvent();
			if (events[totalIndex] == WSA_INVALID_EVENT)
			{
				printf("Error - Event Create Failure\n");
				return 1;
			}

			// - 이벤트 객체를 소켓과 묶음
			if (WSAEventSelect(clientSocket, events[totalIndex], FD_READ) == SOCKET_ERROR)
			{
				printf("Error - Event Select Failure\n");
				return 1;
			}

			totalIndex = totalIndex + 1;
		}

		// >> FD_READ : 읽기 이벤트일 경우
		if (networkEvents.lNetworkEvents& FD_READ)
		{
			// 5-1. 데이터 읽기			
			int receiveBytes = recv(sockets[eventIndex - WSA_WAIT_EVENT_0]->socket, messageBuffer, MAX_BUFFER, 0);
			if (receiveBytes > 0)
			{
				printf("TRACE - Receive message : %s (%d bytes)\n", messageBuffer, receiveBytes);
				// 5-2. 데이터 쓰기
				int sendBytes = send(sockets[eventIndex - WSA_WAIT_EVENT_0]->socket, messageBuffer, strlen(messageBuffer), 0);
				if (sendBytes > 0)
				{
					printf("TRACE - Send message : %s (%d bytes)\n", messageBuffer, sendBytes);
				}
			}
		}

		// >> FD_CLOSE : 종료 이벤트일 경우
		if (networkEvents.lNetworkEvents& FD_CLOSE)
		{
			// 6-1. 클라이언트 소켓종료
			closesocket(sockets[eventIndex - WSA_WAIT_EVENT_0]->socket);

			// 메모리 해제
			free((void *)sockets[eventIndex - WSA_WAIT_EVENT_0]);

			// 이벤트 객체 닫기
			if (WSACloseEvent(events[eventIndex]) == TRUE)
			{
				printf("Error - Event Close Success\n");
			}
			else
			{
				printf("Error - Event Close Failure\n");
			}

			for (int i = eventIndex; i < totalIndex; i++)
			{
				sockets[i] = sockets[i + 1];
				events[i] = events[i + 1];
			}
			totalIndex--;
		}
	}

	// 6-2. 리슨 소켓종료
	closesocket(listenSocket);

	// Winsock End
	WSACleanup();

	return 0;
}

클라이언트

/*
## 소켓 서버 : 1 v n - asynchronous Server - eventSelect
1. socket()			: 소켓생성
2. connect()		: 연결요청
3. read()&write()
	WIN recv()&send	: 데이터 읽고쓰기
4. close()
	WIN closesocket	: 소켓종료
*/

#include "stdafx.h"
#include <winsock2.h>

#pragma comment(lib, "Ws2_32.lib")

#define MAX_BUFFER		1024
#define SERVER_IP		"127.0.0.1"
#define SERVER_PORT		3500

int _tmain(int argc, _TCHAR* argv[])
{	
	// Winsock Start - winsock.dll 로드
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 0), &WSAData) != 0)
	{
		printf("Error - Can not load 'winsock.dll' file\n");
		return 1;
	}

	// 1. 소켓생성
	SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (listenSocket == INVALID_SOCKET)
	{
		printf("Error - Invalid socket\n");
		return 1;
	}

	// 서버정보 객체설정
	SOCKADDR_IN serverAddr;
	memset(&serverAddr, 0, sizeof(SOCKADDR_IN));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(SERVER_PORT);
	serverAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);

	// 2. 연결요청
	if (connect(listenSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
	{
		printf("Error - Fail to connect\n");
		// 4. 소켓종료
		closesocket(listenSocket);
		// Winsock End
		WSACleanup();
		return 1;
	}
	else
	{
		printf("Server Connected\n* Enter Message\n->");
	}

	while (1)
	{		
		// 메시지 입력
		char messageBuffer[MAX_BUFFER];
		int i, bufferLen;
		for (i = 0; 1; i++)
		{
			messageBuffer[i] = getchar();
			if (messageBuffer[i] == '\n')
			{
				messageBuffer[i++] = '\0';
				break;
			}
		}
		bufferLen = i;

		// 3-1. 데이터 쓰기
		int sendBytes = send(listenSocket, messageBuffer, bufferLen, 0);
		if (sendBytes > 0)
		{
			printf("TRACE - Send message : %s (%d bytes)\n", messageBuffer, sendBytes);
			// 3-2. 데이터 읽기
			int receiveBytes = recv(listenSocket, messageBuffer, MAX_BUFFER, 0);
			if (receiveBytes > 0)
			{
				printf("TRACE - Receive message : %s (%d bytes)\n* Enter Message\n->", messageBuffer, receiveBytes);
			}			
		}

	}		

	// 4. 소켓종료
	closesocket(listenSocket);

	// Winsock End
	WSACleanup();

	return 0;
}