PSPSDK Sample을 보면 PSP를 서버로 사용한 echo server가 있습니다.
PC에게 그 사실을 알려주는 프로그램을 작성해보도록 하겠습니다.
그럼 먼저 PC에서 실행시킬 서버 코드를 살펴보겠습니다.
서버는 Win32 API로 작성하였습니다.
Win32로 만든 TCP 서버 닫기
#include <windows.h> #include <process.h> #include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#define ID_EDIT 101 #define ID_START 102 #define ID_STATUS 103 #define ID_MSG 104
#define ID_LEFT 500 #define ID_RIGHT 501
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); HINSTANCE g_hInst; LPSTR lpszClass="PSPCtrlServer";
/* * 각 control들을 위한 핸들입니다. * 이 핸들은 각 control을 제어하기 위하여 사용됩니다. */ HWND hStart; HWND hStatus; HWND hMessage;
#define IS_SOCKET_INVALID(x) ((x) == INVALID_SOCKET) #define IS_SOCKET_ERROR(x) ((x) == SOCKET_ERROR)
/* * PSP가 보내온 메세지를 분석하여 화면에 출력해 줍니다. * str은 키를 정말 짧게 누르지 않는 이상 여러 문자가 전송됩니다. * 예를 들어 네모를 누른경우 SQUARESQUARESQUARE 이런식으로 전송됩니다. * 편의상 처음 한개의 값만 받아들이기 위해 처음 글자들만 비교하도록 합니다. */ void parseMsg(char* str, int len) { if(!strncmp(str, "SQUARE", 6)) { SetWindowText(hMessage, "SQUARE"); } if(!strncmp(str, "TRIANGLE", 8)) { SetWindowText(hMessage, "TRIANGLE"); } if(!strncmp(str, "CIRCLE", 6)) { SetWindowText(hMessage, "CIRCLE"); } if(!strncmp(str, "CROSS", 5)) { SetWindowText(hMessage, "CROSS"); }
if(!strncmp(str, "UP", 2)) { SetWindowText(hMessage, "UP"); } if(!strncmp(str, "DOWN", 4)) { SetWindowText(hMessage, "DOWN"); } if(!strncmp(str, "LEFT", 4)) { SetWindowText(hMessage, "LEFT"); } if(!strncmp(str, "RIGHT", 5)) { SetWindowText(hMessage, "RIGHT"); }
if(!strncmp(str, "START", 5)) { SetWindowText(hMessage, ""); }
if(!strncmp(str, "SELECT", 6)) { SetWindowText(hMessage, "SELECT"); } if(!strncmp(str, "NOTE", 4)) { SetWindowText(hMessage, "NOTE"); } if(!strncmp(str, "LTRIGGER", 8)) { SetWindowText(hMessage, "LTRIGGER"); } if(!strncmp(str, "RTRIGGER", 8)) { SetWindowText(hMessage, "RTRIGGER"); } }
void serverThread(void* param) { const int PACKET_SIZE = 8 * 1024; const int BIND_PORT = 21000;
/* server socket을 생성합니다. */ SOCKET s_ = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htons(INADDR_ANY); saddr.sin_port = htons(BIND_PORT); if (IS_SOCKET_ERROR(bind(s_, (struct sockaddr*)&saddr, sizeof(saddr)))) printf("bind error\n"); if (IS_SOCKET_ERROR(listen(s_, 5))) printf("listen error\n");
struct sockaddr_in caddr; int cbAddr = sizeof(caddr); /* client 접속을 대기합니다. */ SOCKET c_ = accept(s_, (struct sockaddr*)&caddr, &cbAddr); if (IS_SOCKET_INVALID(c_)) printf("accept error\n"); /* 접속이 완료되면 아래의 메세지를 출력합니다. */ SetWindowText(hStatus, "Client Connected!!!");
int len; char inbuf_[PACKET_SIZE] = ""; while ((len = recv(c_, inbuf_, sizeof(inbuf_), 0)) > 0) { parseMsg(inbuf_, len); }
closesocket(c_); closesocket(s_);
return; }
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance ,LPSTR lpszCmdParam,int nCmdShow) { HWND hWnd; MSG Message; WNDCLASS WndClass; g_hInst=hInstance; WndClass.cbClsExtra=0; WndClass.cbWndExtra=0; WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); WndClass.hCursor=LoadCursor(NULL,IDC_ARROW); WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION); WndClass.hInstance=hInstance; WndClass.lpfnWndProc=(WNDPROC)WndProc; WndClass.lpszClassName=lpszClass; WndClass.lpszMenuName=NULL; WndClass.style=CS_HREDRAW | CS_VREDRAW; RegisterClass(&WndClass);
hWnd=CreateWindow(lpszClass,"PSP Ctrl Server",WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,CW_USEDEFAULT, 200, 150, NULL,(HMENU)NULL,hInstance,NULL); ShowWindow(hWnd,nCmdShow);
while(GetMessage(&Message,0,0,0)) { TranslateMessage(&Message); DispatchMessage(&Message); } return Message.wParam; }
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam) { switch(iMessage) { case WM_CREATE: /* 소켓을 초기화 합니다. */ WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData);
/* Start Button을 생성합니다. */ hStart = CreateWindow("button", "Start", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 20, 10, 50, 25, hWnd, (HMENU)ID_START, g_hInst, NULL); /* 진행상황과 전달된 버튼의 종류가 표시될 static control을 생성합니다. */ hStatus = CreateWindow("static", "Not connected", WS_CHILD | WS_VISIBLE, 20, 50, 150, 25, hWnd, (HMENU)ID_STATUS, g_hInst, NULL);
hMessage = CreateWindow("static", "", WS_CHILD | WS_VISIBLE, 20, 70, 150, 25, hWnd, (HMENU)ID_MSG, g_hInst, NULL); return 0; case WM_DESTROY: /* 윈도우가 닫힐 때, 소켓 관련하여 열어둔것을 닫기 위해 WSACleanup을 불러줍니다. */ WSACleanup(); PostQuitMessage(0); return 0; case WM_COMMAND: switch(LOWORD(wParam)) { case ID_START: /* * ID_START Button을 클릭했을 경우 serverThread를 시작하고, * Start 버튼을 화면에서 안보이도록 합니다. */ _beginthread(serverThread, 0, 0); ShowWindow(hStart, SW_HIDE); break; } return 0;
} return(DefWindowProc(hWnd,iMessage,wParam,lParam)); }
간단한 TCP 서버입니다. 만약 recv에서 고대로 send 해준다면 TCP Echo 서버가 되겠지요.
이 서버는 다중 접속을 허용하지 않습니다.
왜냐믄 귀찮아서 하나만 접속하도록 만들었기 때문이죠 ㅡㅡ;;
이 소스를 돌려보실 분은 VC에서 다음을 설정해주셔야 합니다.
Project->Setting->C/C++->Category : Code Generation->USe run-time library : Multithreaded.
Win32로 만든 TCP 서버 닫기