SelectServer.cpp
//===========================================================
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFSIZE 512
// 소켓 정보 저장을 위한 구조체
//-------------------------------------------------------------------
struct SOCKETINFO
{
SOCKET sock;
char buf[BUFSIZE+1];
int recvbytes;
int sendbytes;
};
// 플레이어정보
struct PLAYER{
float ptx;
float pty;
float pbx;
float pby;
float pbz;
float psx;
float psy;
float psz;
int count;
int nID;
}player_1;
struct PLAYER_sck{
unsigned int sck1;
unsigned int sck2;
}player_sck;
int nTotalSockets = 0;
SOCKETINFO *SocketInfoArray[FD_SETSIZE];
// 소켓 관리 함수
BOOL AddSocketInfo(SOCKET sock);
void RemoveSocketInfo(int nIndex);
//----------------------------------------------------------------------
// 오류 출력 함수
/* ++++++++++++++++++++원위치
void err_quit(char *msg);
void err_display(char *msg);
*/
void err_quit(char *msg)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
MessageBox(NULL, (LPCTSTR)lpMsgBuf, msg, MB_ICONERROR);
LocalFree(lpMsgBuf);
exit(-1);
}
// 소켓 함수 오류 출력
void err_display(char *msg)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
printf("[%s] %s", msg, (LPCTSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
}
int main(int argc, char* argv[])
{
int retval;
// 윈속 초기화
WSADATA wsa;
if(WSAStartup(MAKEWORD(2,2), &wsa) != 0)
return -1;
// socket()
SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if(listen_sock == INVALID_SOCKET) err_quit("socket()");
// bind()
SOCKADDR_IN serveraddr;
ZeroMemory(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(9000);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
retval = bind(listen_sock, (SOCKADDR *)&serveraddr, sizeof(serveraddr));
if(retval == SOCKET_ERROR) err_quit("bind()");
// listen()
retval = listen(listen_sock, SOMAXCONN);
if(retval == SOCKET_ERROR) err_quit("listen()");
//----------------------------------------------------------------------
// 넌블로킹 소켓으로 전환
u_long on = TRUE;
retval = ioctlsocket(listen_sock, FIONBIO, &on);
if(retval == SOCKET_ERROR) err_display("ioctlsocket()");
//----------------------------------------------------------------------
// 데이터 통신에 사용할 변수
//-----------------------------------------------------------------------
FD_SET rset;
FD_SET wset;
//------------------------------------------------------------------
SOCKET client_sock;
SOCKADDR_IN clientaddr;
int addrlen;
while(1){
// 소켓 셋 초기화
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_SET(listen_sock, &rset);
for(int i=0; i<nTotalSockets; i++){
if(SocketInfoArray[i]->recvbytes > SocketInfoArray[i]->sendbytes)
FD_SET(SocketInfoArray[i]->sock, &wset);
else
FD_SET(SocketInfoArray[i]->sock, &rset);
}
// select()
retval = select(0, &rset, &wset, NULL, NULL);
if(retval == SOCKET_ERROR) err_quit("select()");
// 소켓 셋 검사(1): 클라이언트 접속 수용
if(FD_ISSET(listen_sock, &rset)){
addrlen = sizeof(clientaddr);
client_sock = accept(listen_sock, (SOCKADDR *)&clientaddr, &addrlen);
if(client_sock == INVALID_SOCKET){
if(WSAGetLastError() != WSAEWOULDBLOCK)
err_display("accept()");
}
else{
printf("[서버] 클라이언트 접속: IP 주소=%s, 포트 번호=%d\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
// 소켓 정보 추가
if(AddSocketInfo(client_sock) == FALSE){
printf("[TCP 서버] 클라이언트 접속을 해제합니다!\n");
closesocket(client_sock);
}
}
}
// 소켓 셋 검사(2): 데이터 통신
for(int i=0; i<nTotalSockets; i++){
SOCKETINFO *ptr = SocketInfoArray[i];
if(FD_ISSET(ptr->sock, &rset)){
// 데이터 받기
//retval = recv(ptr->sock, ptr->buf, BUFSIZE, 0);
retval = recv(ptr->sock,(char*)&player_1, sizeof(PLAYER), 0);
//Check Connect data
//printf("Receive Data player_1.ptx : %3.3f pty : %3.3f nid : %d \n",player_1.ptx,player_1.pty,player_1.nID);
if(retval == SOCKET_ERROR){
if(WSAGetLastError() != WSAEWOULDBLOCK){
err_display("recv()");
RemoveSocketInfo(i);
}
continue;
}
else if(retval == 0){
RemoveSocketInfo(i);
continue;
}
ptr->recvbytes = retval;
// 받은 데이터 출력
addrlen = sizeof(clientaddr);
getpeername(ptr->sock, (SOCKADDR *)&clientaddr, &addrlen);
ptr->buf[retval] = '\0';
player_1.count = nTotalSockets;
/*printf("[TCP/%s:%d] %s\n", inet_ntoa(clientaddr.sin_addr),
ntohs(clientaddr.sin_port), ptr->buf);*/
printf("[client:%d] x:%3.3f, y:%3.3f, Ball-X : %3.3f,Ball-Y : %3.3f,Ball-Z : %3.3f,nID:%d count : %d\n",ntohs(clientaddr.sin_port),
player_1.ptx, player_1.pty, player_1.pbx, player_1.pby, player_1.pbz, player_1.nID,player_1.count);
}
if(FD_ISSET(ptr->sock, &wset)){
// 데이터 보내기
/*
retval = send(ptr->sock, ptr->buf + ptr->sendbytes,
ptr->recvbytes - ptr->sendbytes, 0); */
//retval = send(ptr->sock,(char*)&player_1,sizeof(PLAYER),0);
if(nTotalSockets == 2)
{
if(player_sck.sck1 == ptr->sock){
player_1.count = nTotalSockets;
player_1.nID = 1;
retval = send(player_sck.sck2,(char*)&player_1,sizeof(PLAYER),0);
}
else{
player_1.count = nTotalSockets;
player_1.nID = 2;
retval = send(player_sck.sck1,(char*)&player_1,sizeof(PLAYER),0);
}
}
else
{
player_1.count = nTotalSockets;
player_1.nID = nTotalSockets;
retval = send(ptr->sock,(char*)&player_1,sizeof(PLAYER),0);
}
if(retval == SOCKET_ERROR){
if(WSAGetLastError() != WSAEWOULDBLOCK){
err_display("send()");
RemoveSocketInfo(i);
}
continue;
}
ptr->sendbytes += retval;
if(ptr->recvbytes == ptr->sendbytes){
ptr->recvbytes = ptr->sendbytes = 0;
}
}
}
}
// 윈속 종료
WSACleanup();
return 0;
}
// 소켓 정보 추가
BOOL AddSocketInfo(SOCKET sock)
{
// FD_SETSIZE - 연결 대기 소켓
if(nTotalSockets >= (FD_SETSIZE-1)){
printf("[오류] 소켓 정보를 추가할 수 없습니다!\n");
return FALSE;
}
SOCKETINFO *ptr = new SOCKETINFO;
if(ptr == NULL){
printf("[오류] 메모리가 부족합니다!\n");
return FALSE;
}
ptr->sock = sock;
ptr->recvbytes = 0;
ptr->sendbytes = 0;
SocketInfoArray[nTotalSockets++] = ptr;
if(nTotalSockets == 1)player_sck.sck1 = sock;
else player_sck.sck2 = sock;
return TRUE;
}
// 소켓 정보 삭제
void RemoveSocketInfo(int nIndex)
{
SOCKETINFO *ptr = SocketInfoArray[nIndex];
// 클라이언트 정보 얻기
SOCKADDR_IN clientaddr;
int addrlen = sizeof(clientaddr);
getpeername(ptr->sock, (SOCKADDR *)&clientaddr, &addrlen);
printf("[서버] 클라이언트 종료: IP 주소=%s, 포트 번호=%d\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
closesocket(ptr->sock);
delete ptr;
for(int i=nIndex; i<nTotalSockets; i++){
SocketInfoArray[i] = SocketInfoArray[i+1];
}
nTotalSockets--;
}
// 소켓 함수 오류 출력 후 종료
/* 원위치 +++++++++++++++++++++++++++++++++++++++
void err_quit(char *msg)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
MessageBox(NULL, (LPCTSTR)lpMsgBuf, msg, MB_ICONERROR);
LocalFree(lpMsgBuf);
exit(-1);
}
// 소켓 함수 오류 출력
void err_display(char *msg)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
printf("[%s] %s", msg, (LPCTSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
}
*/
//===========================================================