2. 웹 서비스 개요
• 간단하게 웹 서버 구현해서 어플리케이션
연동
• 어플리케이션에서 웹 서비스 실행
– 외부 웹 브라우저를 통해 통신
메모리 사용량, 사용 리소스 리스트, 동접자 리스트, 서버 상태 등
모니터 가능
3. 장점
• 외부 모니터링 창구로 활용
익스플로러, 크롬, 파이어폭스를 클라이언트로 사용
• 손쉬운 시각화
html형태의 출력을 통해 다양한 시각화 처리
• 메시업 가능
mashup : 외부 데이터 소스에서 가져온 콘텐트를 사용하여 새로운 서
비스를 제공
일부 기능을 구현해서 외부 페이지로 컨트롤 기능 처리
• 큰 부하 없이 처리 가능
4. 웹 서비스 개요
• TCP 서비스
• 성격상 연결 기반의 통신은 아니기 때문에
단순한 세션 관리 가능
• 간단한 텍스트 기반의 통신 체계
5. 절차
• 서버를 연다
기본포트번호 80
• 연결 후 데이터를 받는다.
라인 피드가 연속(nn)된 부분까지 수신
• 패킷을 파싱해서 입력된 host, 경로, 세팅값
들을 얻는다
GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: ko
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: 127.0.0.1
Connection: Keep-Alive
6. 절차
• 해당 소켓으로 헤더에 구성한 후 결과와 함
께 보낸다
HTTP/1.1 200 OK
Date: Sat, 19 May 2007 13:49:37 GMT
Server: SimpleServer
Pragma: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=ISO-8859-1
Content-Language: en-US
Content-Length: 67
<header><title>simple server</title></header><body> Path = /</body>
7. 서버 구성
• 간단한 tcp 서버로 구성
• MutiplexIO 사용
– SimpleServer 프로젝트 참고
//softnette/lab/simpleserver
– IOCP 혹은 Select 사용
동시 접근 유저수가 많지 않다면 select로도 충분
• 간단한 서비스 구성을 지향
어플리케이션의 통신 수단으로의 역할
• Select 사용할 경우 메인 루프에 포함 가능
동기화의 스트레스가 없음
8. Select 서버 예
• 서비스 시작
bool CSimpleServer::Start(unsigned short port, int threadcount)
{
m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
if (m_hSocket == INVALID_SOCKET)
return false;
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
if (bind(m_hSocket, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
return false;
if (listen(m_hSocket, SOMAXCONN) == SOCKET_ERROR)
return false;
unsigned threadID = 0;
if (threadcount > 0)
m_hThread = (HANDLE)_beginthreadex(NULL, 0, _select, (void*)this, 0, &threadID);
return true;
}
9. Select 서버 예
• 쓰레드에서 소켓 이벤트 검사
void CSimpleServer::Update(unsigned long timeouttick)
{
unsigned int i;
fd_set fd;
timeval t;
FD_ZERO(&fd);
FD_SET(m_hSocket, &fd);
for(i=0; i<m_SessionList.size(); i++)
FD_SET(m_SessionList[i]->socket, &fd);
t.tv_sec = timeouttick / 1000;
t.tv_usec = (timeouttick % 1000) * 1000;
if (::select(FD_SETSIZE, (fd_set*)&fd, (fd_set*)0, (fd_set*)0, timeouttick < 0 ? NULL : &t) != SOCKET_ERROR)
{
for(i=0; i<fd.fd_count; i++)
{
소켓 이벤트 처리
}
}
}
unsigned int CSimpleServer::SelectThread()
{
while(m_bTerminate == false)
Update(1000);
return 0;
}
10. Select 서버 예
• 새롭게 연결되면 연결처리
for(i=0; i<fd.fd_count; i++)
{
if (fd.fd_array[i] == m_hSocket)
{
SOCKADDR_IN addr;
int addrlen;
addrlen = sizeof(addr);
SOCKET client = accept(m_hSocket, (struct sockaddr*)&addr, &addrlen);
if (CreateSession(client, addr.sin_addr.S_un.S_addr, addr.sin_port) == false)
closesocket(client);
}
else
{
RECV처리
}
}
11. Select 서버 예
• 끊겼으면 세션 닫고 아니면 읽기
for(i=0; i<fd.fd_count; i++)
{
if (fd.fd_array[i] == m_hSocket)
연결처리
else
{
std::vector<_SESSION*>::const_iterator it;
for(it=m_SessionList.begin(); it!=m_SessionList.end(); it++)
{
if ((*it)->socket == fd.fd_array[i])
{
_SESSION* s = (*it);
unsigned long dwReadbytes;
ioctlsocket(s->socket, FIONREAD, &dwReadbytes);
if (dwReadbytes == 0)
{
CloseSession(s);
}
else
{
s->len += recv(s->socket, &s->buffer[s->len], dwReadbytes, 0);
UpdateSession(s);
}
break;
}
}
}
12. 프로토콜
• http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
• 요청 GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, */
– 단순요청 Accept-Language: ko
GET 경로 rn
User-Agent: Mozilla/5.0 (compatible; MSIE 9.
Accept-Encoding: gzip, deflate
GET /test rn Host: 127.0.0.1
Connection: Keep-Alive
– 전체요청
Method 경로 프로토콜버전 rn
GET /test HTTP/1.1rn
Method = GET, SHOWMETHOD, PUT, POST, …
– 요청 헤더 필드
필드이름 : 데이터 rn
Accept: text/plain, text/html
필드 = From, Accept, Accept-Encoding, User-
Agent, referer, Authorization, Connection, Pragma, Host, …
– 연속 라인 피드 (헤더 끝)
13. 프로토콜
• 응답 HTTP/1.1 200 OK
Date: Sat, 19 May 2007 13:49:37 GMT
– 상태
Server: SimpleServer
Pragma: no-cache
프로토콜버전 상태 rn Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=ISO-8859
HTTP/1.1 200 OK rn Content-Language: en-US
Content-Length: 67
– 응답 헤더 필드
필드이름 : 데이터 rn <header><title>simple server</title></he
Content-Type: text/html;charset=ISO-8859-1 rn
필드 = From, Accept, Accept-Encoding, User-
Agent, referer, Authorization, Connection, Pragma, Host,
…
– 데이터 크기
Content-Length: 크기
– 연속 라인 피드 (헤더 끝)
– 데이터 (앞에서 언급한 크기, html 문법)
14. HTML
• 하이퍼텍스트 마크업 언어
• 기본 예
<html>
<head>
<title>Hello HTML</title>
</head>
<body>
<p>Hello World!</p>
</body>
</html>
15. 간단한 서버
• 경로를 읽어서 웹 브라우저에 표시
int CWebServer::OnRecv(const void* buffer, int len)
{
if (Find("rnrn", (char*)buffer, len) != -1)
{
std::string url = Get("GET", (char*)buffer, len);
if (url.empty() == true)
return 0;
char path[512];
sscanf_s(url.c_str(), "%s", path, sizeof(path));
Parse(path);
return len;
}
return 0;
}
void CWebServer::SendHTML(const char* src)
{
const char* _fmt = "HTTP/1.1 200 OKn"
"Date: Sat, 19 May 2007 13:49:37 GMTn"
"Server: SimpleServern"
"Pragma: no-cachen"
"Expires: Thu, 01 Jan 1970 00:00:00 GMTn"
"Content-Type: text/html;charset=ISO-8859-1n"
"Content-Language: en-USn"
"Content-Length: %dnn";
char header[1024];
sprintf_s(header, _fmt, strlen(src));
Send(header, strlen(header));
Send(src, strlen(src));
}
void CWebServer::Parse(const char* path)
{
std::string html;
html = std::string("<header><title>simple server</title></header><body> Path = ") + path + std::string("</body>");
SendHTML(html.c_str());
}
16. 매시업
• 간단한 http 규격의 API를 제공하는 것으로
여러 가지 서비스가 가능
스테이지 별 리소스 사용량을 분석하기 위해서 어플리케이션에 기능
을 넣을 필요 없이 스테이지와 리소스를 얻을 수 있는 http 인터페이
스를 만든다면, 외부 웹 서비스와 연동하여 스테이지 별 리소스, 게임
의 릴리즈 버전 별 차이, 테스터 레벨 별 스테이지의 리소스 사용량 등
의 정보를 손쉽게 만들 수 있음