서버
/*
## 소켓 서버 : 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;
}