------

[ AD ] Port Monitor ( Try to use a Best WebSite Monitoring Tool )

------
Winsock 서버 IOCP 사용하여 설계 문제

마지막 검토: 2005년 7월 11일 월요일 - 수정: 2.1

http://goo.gl/9bOl

이 문서에서는 이미 있는 경우 Windows NT I/O 완료 포트 (IOCP) I/O 모델을 이해하고 관련된 API를 사용하여 익숙한 가정합니다. IOCP 확인할 수 없으면 Windows 고급 참조하십시오 (세 번째 버전을) Jeffery Richter 15 장에 의해 장치 I/O 훌륭한 토론 IOCP 구현 및 API를 사용해야 합니다.

IOCP 매우 높은 성능과 확장성이 매우 서버 프로그램을 개발하기 위한 모델을 제공합니다. 직접 IOCP 지원을 위해 Winsock2 추가되었으며 Windows NT 플랫폼에서 완전히 구현됩니다. 그러나 IOCP 가장 어려운 이해하고 간에 모든 Windows NT I/O 모델을 구현할 수 있습니다. IOCP 사용하여 더 나은 소켓 서버를 설계하는 데 도움이 되는 팁 수는 이 문서에서가 제공됩니다.

1 TIP: 사용 Winsock2 IOCP 사용할 수 없는 함수, WSASend 및 WSARecv, 같은 WriteFile 및 ReadFile 같은 Win32 파일 I/O 함수를 통해.

Win32 파일 I/O 호출을 사용하여 핸들을 사용할 수 있도록 Microsoft 기반 프로토콜 공급자로부터 소켓 핸들이 IFS 핸들이 있습니다. 그러나, 공급자 및 파일 시스템 간의 상호 작용을 많은 커널 및 사용자 모드 전환, 스레드 컨텍스트 전환 및 있는 상당한 성능 저하를 초래할 매개 변수 마샬링합니다가 포함됩니다. IOCP 함께 Winsock2 IOCP - 가능 함수만 사용해야 합니다.

공급자가 없는 경우에만 추가 매개 변수 마샬링합니다 및 ReadFile 및 WriteFile 모드 전환 XP1_IFS_HANDLES WSAPROTOCOL_INFO 구조 dwServiceFlags1에 비트가 설정된 있어야 발생합니다.

참고: 이 공급자는 ReadFile 및 WriteFile 이상 가질 수 있지만 경우에도 의 WSASend 및 WSARecv, 피할 수 없는 추가 모드 전환이 있습니다.

2 TIP: 허용된 동시 작업자 스레드 및 총 배치되지 작업자 스레드 선택하십시오.

작업자 스레드 수 및 해당 IOCP 사용하는 동시 스레드 수를 동일한 작업을 할 수 없습니다. 해당 IOCP 10개의 작업자 스레드 풀을 사용하는 동시 스레드 2 최대 결정할 수 있습니다. dequeued 완료 패킷 처리 작업자 스레드 "대기" Win32 함수 중 하나를 다른 대기 중인된 I/O 패킷 처리 연기하지 호출할 수 있도록 IOCP에 의해 사용되는 동시 스레드 수를 0보다 크거나 작업자 스레드 풀이 있습니다.

완료 패킷 대기 중인 dequeued 수 있는 경우 다른 작업자 스레드를 나열합니다 됩니다. 결국 첫 번째 스레드가 대기 기능 및 다시 실행할 수 만족합니다. 이런 경우, 실행할 수 있는 스레드 수를 IOCP (예를 들어, NumberOfConcurrentThreads) 허용되는 동시성 높은 것입니다. 하지만 다음 작업자 스레드를 GetQueueCompletionStatus 호출하고 대기 상태에 들어갈 때 시스템 이가 활성화되어 있지 않습니다. 즉, 시스템이 사용자 요청한 동시 작업자 스레드 수를 시도합니다.

일반적으로 경우에만 CPU 당 하나의 동시 작업자 스레드에 대한 IOCP 합니다. 해당 IOCP 처음 만들 때 이 작업을 수행하려면 0에 대한 NumberOfConcurrentThreads CreateIoCompletionPort 호출에서 입력하십시오.

3 TIP: 게시된 I/O 작업이 dequeued 완료 패킷을 사용하여 연결.

GetQueuedCompletionStatus에 대한 I/O 완료 패킷 큐를 때 완성 키와 Overlapped 구조체를 반환합니다. 이러한 두 구조를 핸들을 당 및 I/O 작업 정보를 당 각각 반환하도록 사용해야 합니다. IOCP 당 핸들을 정보를 제공할 수 있는 소켓 등록할 때 소켓 핸들을 완료 키로 사용할 수 있습니다. I/O 당 제공하도록 작업을 "Overlapped 구조체를 응용 프로그램별 I/O를 상태 정보를 포함하도록 확장하는". 또한 각 중첩된 I/고유한 Overlapped 구조체를 제공해야 합니다. 겹친된 I/O 구조가 동일한 포인터는 I/O를 완료할 때 반환됩니다.

팁 4: I/O 완료 패킷 큐 동작입니다.

어떤 I/O에 IOCP 완료 패킷 대기 중인 순서를 Winsock2 I/O 호출이 동일한 순서대로 되지는지 않습니다. 또한 Winsock2 I/O 호출 성공 또는 IO_PENDING 반환하면 소켓 핸들을 닫지 여부에 관계없이 I/O가 완료되면 완료 패킷 수 IOCP 수 됩니다 해당 큐에 보장됩니다. 소켓 핸들을 닫은 후, 성공 또는 완료 패킷 생성하지 IO_PENDING 이외의 반환 코드를 사용하여 WSASend, WSASendTo, WSARecv, WSARecvFrom 또는 이후의 호출이 실패합니다. 이 경우 이전에 게시한 I/O GetQueuedCompletionStatus에 의해 검색된 완료 패킷 상태 실패를 나타낼 수 있습니다.

IOCP 자체를 삭제하는 경우 IOCP 핸들이 유효하지 않기 때문에 IOCP 데 더 많은 I/O 게시할 수 있습니다. 그러나 성공적으로 게시된 모든 I/O 완료될 때까지 시스템의 내부 IOCP 커널 구조를 즉시 이동하지 마십시오.

팁 5: IOCP 정리입니다.

I/O 수행하는 ICOP 정리 동일한 사용할 때 때 기억해야 할 가장 중요한 사항은 겹쳐진:에 대한 I/O 아직 완료된 경우 Overlapped 구조체를 해제하지 마십시오. HasOverlappedIoCompleted 매크로 I/O가 겹쳐진된 구조가 완료된 경우 감지할 수 있습니다.

일반적으로 한 서버를 종료하는 두 가지 시나리오가 있습니다. 첫 번째 시나리오에서는 해결되지 않은 I/O 완료 상태에 대한 맡고 있으므로 가능한 한 빨리 종료해야 할. 두 번째 시나리오, 서버를 종료한 싶지만 각 해결되지 않은 I/O 완료 상태를 알아야 합니다.

첫 번째 시나리오에서는 게시물을 즉시 끝내려면, 모든 소켓을 닫을 수 처리하는 작업자 스레드에 알리는 특수 완료 패킷 및 관련된 겹친된 구조체에 PostQueueCompletionStatus (N 시간, 작업자 스레드 수를 N 위치) 호출 및 완료 포트를 닫을 수 있습니다. 다시 HasOverlappedIoCompleted 이를 약속 전에 겹쳐진된 구조가 완료 상태를 확인할 수 있는지 확인하십시오. 소켓이 닫혀 있으면 소켓에 보류 중인 모든 I/O 결국에는 빠르게 완료합니다.

두 번째 시나리오에서는 모든 완료 패킷을 제대로 dequeued 수 있도록 작업자 스레드를 종료하는 지연시킬 수 있습니다. 소켓 핸들이 모두 닫고 IOCP에 의해 시작할 수 있습니다. 그러나 안전 스레드가 끝내려면 때 작업자 스레드가 알 수 있도록 처리 중인 I/O 수를 유지 합니다. 임계 IOCP 서버의 보호된 전역 I/O 카운터 필요 없이 성능 저하 문제가 있는 경우 자세한 완료 패킷을 대기열에 대기 중인 현재 작업자 스레드 out 전환하지 않습니다 때문에 예상한 대로 같은 잘못된 아닙니다.

Developing a Truly Scalable Winsock Server using IO Completion Ports
22 Sep 2001

The article expects the reader to be familiar with the C++, Winsock API 2.0, MFC, Multithreading.


http://goo.gl/g2AG



HANDLE CreateIoCompletionPort ( HANDLE FileHandle, // handle to file
HANDLE ExistingCompletionPort,  // handle to I/O completion port
ULONG_PTR CompletionKey,        // completion key
DWORD NumberOfConcurrentThreads // number of threads to execute concurrently );

BOOL CClientListener::AssociateSocketWithCompletionPort(SOCKET socket, 
                                                        HANDLE hCompletionPort, 
                                                        DWORD dwCompletionKey)
{
	HANDLE h = CreateIoCompletionPort((HANDLE) socket, hCompletionPort, dwCompletionKey, 0);
	return h == hCompletionPort;
}

struct ClientContext 
{
OVERLAPPED m_Overlapped;
LastClientIO m_LastClientIo;
SOCKET m_Socket;

// Store buffers
CBuffer m_ReadBuffer;
CBuffer m_WriteBuffer;

// Input Elements for Winsock
WSABUF m_wsaInBuffer;
BYTE m_byInBuffer[8192]; 

// Output elements for Winsock
WSABUF m_wsaOutBuffer;
HANDLE m_hWriteComplete;

// Message counts... purely for example purposes
LONG m_nMsgIn;
LONG m_nMsgOut; 
};


BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort,      // handle to completion port
LPDWORD lpNumberOfBytes,    // bytes transferred
PULONG_PTR lpCompletionKey, // file completion key
LPOVERLAPPED *lpOverlapped, // buffer
DWORD dwMilliseconds        // optional timeout value
);

m_nThreadPoolMin  // The minimum threads in the pool
m_nThreadPoolMax  // The maximum threads allowed in the pool
m_nCPULoThreshold // The CPU threshold when unused threads can removed from the Worker ThreadPool
m_nCPUHiThreshold // The CPU threshold when a thread can be added to the Worker ThreadPool


// Here we use the natural (well...) way to neatly handle each queued status
BEGIN_IO_MSG_MAP()
IO_MESSAGE_HANDLER(ClientIoInitializing, OnClientInitializing)
IO_MESSAGE_HANDLER(ClientIoRead, OnClientReading)
IO_MESSAGE_HANDLER(ClientIoWrite, OnClientWriting)
END_IO_MSG_MAP()

bool OnClientInitializing (ClientContext* pContext, DWORD dwSize = 0);
bool OnClientReading (ClientContext* pContext, DWORD dwSize = 0);
bool OnClientWriting (ClientContext* pContext, DWORD dwSize = 0);




스티브 잡스 "안드로이드 안돼…우리가 승자"

http://goo.gl/TRKv

스티브 잡스 애플 최고경영자(CEO)는 3분기 실적 발표 컨퍼런스콜


“지금까지 언론 등에서는 애플과 안드로이드를 폐쇄적이고 개방적인 것으로 밝혀왔지만 진정 소비자를 위한 것인지를 볼 때 이는 분열적이냐 통합적이냐를 가르기 위한 것“이라며 ”나는 안드로이드가 분열적이어서 소비자들에게 많은 효익을 제공하지 못할 것이라고 생각한다”


“7인치 화면으로는 소비자들이 원하는 좋은 태블릿 앱을 만들어 내기 충분하지 않다”며 “운영체제를 제공하는 구글 조차도 7인치에는 2.2 프로요 버전이 적합하지 않다고 말했다"


애플의 지난 3분기 순이익은 43억1000만달러(주당 4.64달러)로 작년 같은 기간 25억3000만달러(주당 2.77달러)보다 70%나 급증했다고 발표했다. 매출은 203억4000만달러로 작년 같은 기간보다 67%나 급증했다
[상태 변화]
PreCondition android:launchMode=""
Case 1 Hold Press 슬립 시작
onSaveInstanceState()
onPause()
onWindowFocusChanged() Go Home? Home
1.hold Press 다시 눌러 화면 깨우기
onResume()
onWindowFocusChanged() Come Back
Case 2 Home Long Press 실행중 app 시작
onWindowFocusChanged() Go Home? Popup
1.Home Long Press 아이콘 다시 누르기
onWindowFocusChanged() Come Back
2.Back Press 취소 누르기
onWindowFocusChanged() Come Back
Case 3 Home Press 바로 바탕화면 시작
onSaveInstanceState()
onPause()
onWindowFocusChanged
onStop()
1. App click 다시 app 실행
onRestart()
onStart()
onResume()
실제 구현 코딩 ---

'온라인게임' 카테고리의 다른 글

Developing a Truly Scalable Winsock Server using IO Completion Ports  (0) 2010.10.20
잡스 steve jobs  (0) 2010.10.20
Factory 패턴  (0) 2010.10.19
use case diagram  (0) 2010.10.19
Winsock2 IOCP  (0) 2010.10.19
■ Factory 패턴
  ○ 용도
    -  Instance 작성을 하위 class에게 위임
  ○ 특 징
    - Instance를 만드는 방법은 상위 class에서 결정하지만,
     구체 적인 class의 이름까지는 정하지 않음
    - Instance를 생성을 위한 framework과 실제 instance
      생성하는 class를 분리함

'온라인게임' 카테고리의 다른 글

잡스 steve jobs  (0) 2010.10.20
안드로이드 app 홈키에 의한 화면 복구/쓰레드 복구시  (0) 2010.10.19
use case diagram  (0) 2010.10.19
Winsock2 IOCP  (0) 2010.10.19
S R P G  (0) 2010.10.18
use case diagram /유즈 케이스 다이어 그램

- UML 에서 가장 중요한 모델 요소중 하나이다.
- 요구사항분석 ~ 테스트 까지 모든 개발 과정을 이끌어 가는 모델 요소
- 구축 시스템이 실행할 작업이 무엇인지를 표현한다.
- Use Case Specification도 필요하다.


- Actor  Generalization / 상속


- Actor - Use Case Association



- Use Case Specification





- Actor Inheritance 


- Abstract Actor


Use

Use Case
간의 관계
 Generalization , Include , extend 관계를 이용하여 세분화한다

포함 ( Include )
기본 Use Case가 다른 Use Case행동을  명시적으로 포함됨을 의미한다.

확장 ( extend )
사용자가 선택적으로 보는 시스템 행동을 의미한다. 특별한 조건에서만 수행되는 행위를 모델화

일반화 ( Generalization )
자식Use Case는 부모Use Case의 행동과 의미를 상속받는다.

'온라인게임' 카테고리의 다른 글

안드로이드 app 홈키에 의한 화면 복구/쓰레드 복구시  (0) 2010.10.19
Factory 패턴  (0) 2010.10.19
Winsock2 IOCP  (0) 2010.10.19
S R P G  (0) 2010.10.18
삼성 갤럭시탭, 구글 공식인증 받아  (0) 2010.10.18

IOCP의 기본적은 구성은두개의 쓰레드로 만들어 진다. 왼편에 있는 루프는 보통 main() 함수에서 있는 루프고 오른쪽에 있는 루프는 main함수가 루프를 들어가기 전에 생성해야 하는 쓰레드로 보통 Worker쓰레드라고 한다. 권장하는 Worker쓰레드의 수는 하나의 CPU당 두개로(Send/Recv) 되어 있다. 또한 2번에서 3번으로의 데이터 전송은 전역변수를 이용하거나 Worker쓰레드를 생성할 때, 포인터를 전송하여 두 루프가 변수를 공유하는 방법이다. 이에 대해서는 달리 자세히 설명하지는 않겠다. 1. wait for client to connect ( accept )
이 과정에 이전에 이미IOCP 핸들과 소켓 초기화, 클라이언트의 접속을 대기하는 소켓, 접속한 클라이언트들의 정보를 가지는 배열( 물론, 자료구조는 자신의 선택이다. )을 생성해 두어야 한다. 여기는 클라이언트의 접속을 대기하는 소켓에 Accept()를 호출하여 클라이언트가 접속하기를 기다리는 과정이다.
unsigned int WINAPI	CallAccepterThread(LPVOID p)
{
	cIOCompletionPort * pOverlappedEvent = (cIOCompletionPort *)p;

	pOverlappedEvent->ThreadAccepter();

	return 0;
}
// 서버 시작 -- 준비
bool cIOCompletionPort::StartServer(void)
{


	m_hIOCP = CreateIoCompletionPort( INVALID_HANDLE_VALUE,
								NULL,
								NULL,
								0);
	if ( NULL == m_hIOCP ) 
	{
		m_pMainDlg->OutputMsg("StartServer [에러] CreateIoCompletionPort() 실패:%d:",GetLastError() );

		return false;
	}

	
	bool bRet = CreateWorkerThread();
	if ( false == bRet ) 
	{
		m_pMainDlg->OutputMsg("StartServer [에러] CreateWorkerThread() 실패:%d:",GetLastError() );

		return false;
	}

	bRet = CreateAccepterThread();
	if ( false == bRet ) 
	{
		m_pMainDlg->OutputMsg("StartServer [에러] CreateAccepterThread() 실패:%d:",GetLastError() );

		return false;
	}

	m_pMainDlg->OutputMsg(" StartServer() - CreateIoCompletionPort / Worker /Accepter 성공" );
	///
	return true;
}
bool cIOCompletionPort::CreateAccepterThread(void)
{

	unsigned int uiThreadId = 0;

	/// 클라이언트 접속 요청을 받은 쓰레드 생성
	m_hAccepterThread = (HANDLE)_beginthreadex(NULL, 0,
						&CallAccepterThread,
						this,
						CREATE_SUSPENDED,
						&uiThreadId);
	if ( m_hAccepterThread == NULL ) 
	{
		m_pMainDlg->OutputMsg("CreateAccepterThread()  [에러] 실패:%d:",GetLastError() );

		return false;
	}
	ResumeThread( m_hAccepterThread );


	m_pMainDlg->OutputMsg(" CreateAccepterThread() 성공" );
	///
	return true;
}
// 사용자 접속 받는 쓰레드
void cIOCompletionPort::ThreadAccepter(void)
{
	SOCKADDR_IN		stClientAddr;

	int nAddrLen = sizeof(SOCKADDR_IN);
	while ( m_bAccepterRun )
	{
		// 접속 받을 구조체 인덱스 얻기
		stClientInfo * pClientInfo = GetEmptyClientInfo();
		if ( NULL == pClientInfo )
		{
			m_pMainDlg->OutputMsg("** AccepterThread [에러] NULL == pClientInfo :%s:","Client FULL");

			return ;
		}
		m_pMainDlg->OutputMsg("** AccepterThread -- accept : pClientInfo->m_socketClient");
		// 클라이언트 접속 요청까지 대기
		//
		//  accept()
		//
		pClientInfo->m_socketClient = accept ( m_socketListen,
							(SOCKADDR *)&stClientAddr, &nAddrLen );

		if ( INVALID_SOCKET == pClientInfo->m_socketClient ) {
			continue;
		}
		m_pMainDlg->OutputMsg("** AccepterThread -- BindIOCompletionPort & 소켓 pClientInfo");
		// I/O Completion Port객체와 소켓을 연결 시킨다.
		//
		//  BindIOCompletionPort()
		//
		bool bRet = BindIOCompletionPort( pClientInfo );

		if ( false == bRet ) {
			return;
		}
		// Recv Overlapped I/O 작업을 요청한다
		m_pMainDlg->OutputMsg("** AccepterThread -- BindRecv & pClientInfo ");
		//
		//  BindRecv()
		//
		bRet = BindRecv(pClientInfo);

		if ( false == bRet ) {
			return;
		}

		m_pMainDlg->OutputMsg("** Accepter Thread -- [클라이언트 접속] ip(%s) SOCKET(%d)",
			inet_ntoa( stClientAddr.sin_addr) ,
			pClientInfo->m_socketClient);

		m_nClientCnt ++;

	}
}

2. open commnunication channel for client
1번 과정에서 클라이언트가 접속을 하면 여기서는 올바른 소켓인지 판단하고, 접속한 소켓을 클라이언트 정보를 저장하는 배열에 저장하고, IOCP에 해당 소켓을 등록하고, 소켓에 읽기 작업을 신청한다. ( 읽기를 먼저 신청하는 이유는 대해서는 설명하지 않겠다. ) 이 때 중요한 것은 현재 WSARecv(), ReadFile()함수를 사용하여 읽기 작업을 신청했지만, IOCP는 작업의 완료 시에 읽기 작업을 끝냈는지 쓰기 작업을 끝냈는지 가르쳐 주지 않는다는 것이다. 따라서,OVERLAPPED구조체를 상속( C관점에서는 새로운 구조체를 선언할 때, OVERLAPPED구조체를 제일 첨에 위치하는 멤버로 선언하여 사용할 수 있다.)하여 읽기 작업을 하는 건지 쓰기 작업을 하는 건지에 대한 흔적을 남겨야 한다.
bool cIOCompletionPort::BindIOCompletionPort(stClientInfo * pClientInfo)
{

	HANDLE hIOCP;

	/// socket 과 pClientInfo를  CompletionPort객체와 연결 시킨다.
	hIOCP = CreateIoCompletionPort( (HANDLE)pClientInfo->m_socketClient,
			m_hIOCP,
			reinterpret_cast( pClientInfo ),
			0);
	if ( NULL == hIOCP || m_hIOCP != hIOCP ) 
	{
		m_pMainDlg->OutputMsg("CreateIoCompletionPort() - [에러]  실패:%d:",GetLastError() );

		return false;
	}


	m_pMainDlg->OutputMsg(" BindIOCompletionPort() - 성공" );
	///
	return true;
}
bool cIOCompletionPort::BindRecv(stClientInfo * pClientInfo)
{
	DWORD	dwFlag = 0;
	DWORD	dwRecvNumBytes = 0;


	// Overlapped I/O Setting
	pClientInfo->m_stRecvOverlappedEx.m_wsaBuf.len = MAX_SOCKBUF;
	pClientInfo->m_stRecvOverlappedEx.m_wsaBuf.buf = 
		pClientInfo->m_stRecvOverlappedEx.m_szBuf;
	pClientInfo->m_stRecvOverlappedEx.m_eOperation = OP_RECV;

	
	//// 입력 버퍼 클리어 ???????????????
	//ZeroMemory(pClientInfo->m_stRecvOverlappedEx.m_szBuf, 1024);

	int nRet = WSARecv( pClientInfo->m_socketClient,
						&(pClientInfo->m_stRecvOverlappedEx.m_wsaBuf),
						1,
						&dwRecvNumBytes,
						&dwFlag,
						(LPWSAOVERLAPPED)&(pClientInfo->m_stRecvOverlappedEx),
						NULL);
	/// socket_error 이면 client socket이 끊어 진걸로 처리한다.
	if ( nRet ==  SOCKET_ERROR && ( ERROR_IO_PENDING != WSAGetLastError()  )  ) 
	{
		m_pMainDlg->OutputMsg("[에러] BindRecv - WSARecv() 실패 WSAGetLastError:%d:",WSAGetLastError() );

		return false;
	}
	m_pMainDlg->OutputMsg("\n [] BindRecv - WSARecv()   nRet : %d", nRet);
	return true;
}



3. Read Request from client
이 과정에서 GetQueuedCompletionStatus() 함수를 사용하여 현재 등록된 소켓들 중에 읽기나 쓰기 작업이 완료된 것이 있는지 확인한다. 이 때,IOCP가 알려주는 정보는 2번 과정에서 등록 할 때, 소켓과 같이 입력했던 KEY와 I/O작업을 신청할 때, 인자로 넘겨주었던 OVERLLAPPED 구조체( 확장을 시켰다면 확장 구조체형으로 캐스팅 해주면 된다.)의 주소이다.
void cIOCompletionPort::ThreadWorker(void)
{
	// CompletionKey를 받을 포인터 변수
	stClientInfo * pClientInfo = NULL;

	// 함수 호출 성공여부
	BOOL bSuccess = TRUE;

	// Overlapped I/O작업에서 전송된 데이타 크기
	DWORD dwIoSize = 0;

	// I/O 작업을 위해 요청한 Overlapped 구조체를 받을 포인터
	LPOVERLAPPED lpOverlapped = NULL;

	while ( m_bWorkerRun ) 
	{
		/**
		이 함수로 인해 쓰래들들은 WaitingThread Queue에 대기상태로 들어간다
		완료된 Overlapped I/O 작업이 발생하면 IOCP Queue에서 완료된 작업을 가져와 뒤처리
		그리고 PostQueuedCompletionStatus()함수에 의해 사용자 메시지가 도착되면 쓰레드 종료
		**/
		bSuccess =	GetQueuedCompletionStatus( m_hIOCP,
				&dwIoSize,							// 실제 전송된 바이트
				(LPDWORD)&pClientInfo,				// Completionkey
				&lpOverlapped,						// Overlappped I/O 객체
				INFINITE);							// 대기할 시간(무한대기)
		
		// 클라이언트가 접속 끊었을 때
		//
		//  FALSE == bSuccess
		//
		if ( FALSE == bSuccess && 0 == dwIoSize ) 
		{
			m_pMainDlg->OutputMsg("WorkerThread [클라이언트] SOCKET(%d) 접속 끊김",	pClientInfo->m_socketClient);
			CloseSocket(pClientInfo);
			continue;
		}

		// 사용자 스레드 종료 메시지 처리
		//
		//  TRUE == bSuccess
		//

		if ( TRUE == bSuccess && 0 == dwIoSize && NULL == lpOverlapped )
		{
			//
			// WorkerThread 종료
			//
			m_bWorkerRun = false ;
			continue;
		}

		if ( NULL == lpOverlapped ) {
			continue;
		}
		stOverlappedEx * pOverlappedEx =(stOverlappedEx *)lpOverlapped;

		// Overlapped I/O Recv 작업 결과 뒤 처리
		// 
		//	OP_RECV
		//
		if ( OP_RECV == pOverlappedEx->m_eOperation ) 
		{
			pOverlappedEx->m_szBuf[dwIoSize] = NULL;
			m_pMainDlg->OutputMsg("----->>>>> WorkerThread [수신] ( %d ) bytes , msg : %s ",dwIoSize,pOverlappedEx->m_szBuf);

			// 클라이언트에 메시지를 에코한다.
			//BindRecv( pClientInfo );

			//SendMsg(pClientInfo, pOverlappedEx->m_szBuf, dwIoSize );

			//pOverlappedEx->m_eOperation = OP_SEND;
			//BindRecv( pClientInfo );

			SendMsg( pClientInfo, pOverlappedEx->m_szBuf, dwIoSize);

		}
		// Overlapped I/O Send 작업 결과 뒤 처리
		// 
		//	OP_SEND
		//
		else if ( OP_SEND == pOverlappedEx->m_eOperation ) 
		{
			m_pMainDlg->OutputMsg("<<<<<-------WorkerThread [송신] ( %d ) bytes , msg : %s ",dwIoSize,pOverlappedEx->m_szBuf);
			
			//// 입력 버퍼 클리어 ???????????????
			ZeroMemory(pOverlappedEx->m_szBuf, 1024);

			BindRecv( pClientInfo );

		}
		else 
		{
			m_pMainDlg->OutputMsg("WorkerThread [클라이언트] SOCKET(%d) 예외 상황 ",pClientInfo->m_socketClient);
		}
		lpOverlapped = NULL;
	}
}

4. Execute request locally
이제3번 과정에서 받은 key와 OVERLAPPED구조체를 가지고 I/O작업을 신청했을 때, 만약 작업이 끝나면 했었을 작업을 해주면 된다.

5. return result to client
이 과정은 사실4번 과정과 같이 포함되는 부분이다. 즉, 채팅을 예로 들면, 어떤 사용자가 메시지를 입력했을 때, 4번과정의 If( pOverlapped->mode == 읽기 ) {} 블록안이 실행될 것이고, 어떤 메시지인지 확인한 후, 접속한 모든 사용자에게 메시지를 보내게 된다.

#include "StdAfx.h"

#include "cIOCompletionPort.h"

//// 흠
#include "IOCompletionPortDlg.h"

/// 쓰레드 만들기 
/// WSARecv , WSASend의 Overlapped I/O 작업을 위한 
unsigned int WINAPI	CallWorkerThread(LPVOID p)
{
	cIOCompletionPort * pOverlappedEvent = (cIOCompletionPort *)p;

	pOverlappedEvent->ThreadWorker();

	return 0;
}
unsigned int WINAPI	CallAccepterThread(LPVOID p)
{
	cIOCompletionPort * pOverlappedEvent = (cIOCompletionPort *)p;

	pOverlappedEvent->ThreadAccepter();

	return 0;
}
/// 
cIOCompletionPort::cIOCompletionPort(void)
{
	/// 모든 멤버 변수들의 초기화
	m_pMainDlg = NULL;
	m_bWorkerRun	= true;
	m_bAccepterRun	= true;
	m_nClientCnt = 0;
	m_hAccepterThread = NULL;
	m_hIOCP	= NULL;
	m_socketListen = INVALID_SOCKET;
	ZeroMemory(m_szBuf, 1024);
	for ( int i=0; i < MAX_WORKERTHREAD; i++ ) {
		m_hWorkerThread [i] = NULL;
	}
	m_pClientInfo = new stClientInfo[MAX_CLIENT];
}


cIOCompletionPort::~cIOCompletionPort(void)
{
	// 윈속 사용 해재
	WSACleanup();
	// 다 사용한 객체 삭제
	if ( m_pClientInfo ) 
	{
		delete[] m_pClientInfo;
		m_pClientInfo = NULL;
	}
}


bool cIOCompletionPort::InitSocket(void)
{
	WSADATA	wsaData;

	// 윈속 버젼 2.2
	int nRet = WSAStartup( MAKEWORD(2,2) , &wsaData);
	if ( 0 != nRet ) {

		m_pMainDlg->OutputMsg("[에러]WSAStartup() 실패:%d:",WSAGetLastError() );

		return false;
	}
	m_socketListen = WSASocket(AF_INET, SOCK_STREAM,
					IPPROTO_TCP, NULL, NULL, WSA_FLAG_OVERLAPPED );

	if ( INVALID_SOCKET == m_socketListen ) {

		m_pMainDlg->OutputMsg("[에러]WSASocket() 실패:%d:",WSAGetLastError() );

		return false;

	}
	m_pMainDlg->OutputMsg(" InitSocket() 성공" );
	///
	return true;
}




bool cIOCompletionPort::BindandListen(int nPort)
{

	SOCKADDR_IN		stServerAddr;

	stServerAddr.sin_family = AF_INET;

	stServerAddr.sin_port = htons(nPort);

	stServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

	int nRet = bind( m_socketListen, (SOCKADDR *)&stServerAddr, sizeof(SOCKADDR_IN) );
	if ( 0 != nRet ) {

		m_pMainDlg->OutputMsg("[에러] bind() 실패:%d:",WSAGetLastError() );

		return false;

	}
	nRet = listen( m_socketListen, 5 ) ;
	if ( 0 != nRet ) {

		m_pMainDlg->OutputMsg("[에러] listen() 실패:%d:",WSAGetLastError() );

		return false;

	}
	m_pMainDlg->OutputMsg(" BindandListen() 성공" );
	///
	return true;
}





bool cIOCompletionPort::CreateWorkerThread(void)
{

	unsigned int uiThreadId = 0;

	/// Waiting Thread Queue에 대기 상태로 넣을 쓰레드들 생성
	/// 권장하는 개수 : cpu *2 +1

	for ( int i =0; i < MAX_WORKERTHREAD; i++ ) 
	{

		m_hWorkerThread[i] = (HANDLE)_beginthreadex(NULL, 0,
							&CallWorkerThread,
							this,
							CREATE_SUSPENDED,
							&uiThreadId);
		if ( m_hWorkerThread[i] == NULL ) 
		{
			m_pMainDlg->OutputMsg("[에러] CreateWorkerThread() 실패:%d:",GetLastError() );

			return false;
		}
		ResumeThread( m_hWorkerThread[i] );
	}


	m_pMainDlg->OutputMsg(" CreateWorkerThread() 성공" );
	///
	return true;
}


bool cIOCompletionPort::CreateAccepterThread(void)
{

	unsigned int uiThreadId = 0;

	/// 클라이언트 접속 요청을 받은 쓰레드 생성
	m_hAccepterThread = (HANDLE)_beginthreadex(NULL, 0,
						&CallAccepterThread,
						this,
						CREATE_SUSPENDED,
						&uiThreadId);
	if ( m_hAccepterThread == NULL ) 
	{
		m_pMainDlg->OutputMsg("CreateAccepterThread()  [에러] 실패:%d:",GetLastError() );

		return false;
	}
	ResumeThread( m_hAccepterThread );


	m_pMainDlg->OutputMsg(" CreateAccepterThread() 성공" );
	///
	return true;
}
////
bool cIOCompletionPort::BindIOCompletionPort(stClientInfo * pClientInfo)
{

	HANDLE hIOCP;

	/// socket 과 pClientInfo를  CompletionPort객체와 연결 시킨다.
	hIOCP = CreateIoCompletionPort( (HANDLE)pClientInfo->m_socketClient,
			m_hIOCP,
			reinterpret_cast( pClientInfo ),
			0);
	if ( NULL == hIOCP || m_hIOCP != hIOCP ) 
	{
		m_pMainDlg->OutputMsg("CreateIoCompletionPort() - [에러]  실패:%d:",GetLastError() );

		return false;
	}


	m_pMainDlg->OutputMsg(" BindIOCompletionPort() - 성공" );
	///
	return true;
}

bool cIOCompletionPort::StartServer(void)
{


	m_hIOCP = CreateIoCompletionPort( INVALID_HANDLE_VALUE,
								NULL,
								NULL,
								0);
	if ( NULL == m_hIOCP ) 
	{
		m_pMainDlg->OutputMsg("StartServer [에러] CreateIoCompletionPort() 실패:%d:",GetLastError() );

		return false;
	}

	
	bool bRet = CreateWorkerThread();
	if ( false == bRet ) 
	{
		m_pMainDlg->OutputMsg("StartServer [에러] CreateWorkerThread() 실패:%d:",GetLastError() );

		return false;
	}

	bRet = CreateAccepterThread();
	if ( false == bRet ) 
	{
		m_pMainDlg->OutputMsg("StartServer [에러] CreateAccepterThread() 실패:%d:",GetLastError() );

		return false;
	}

	m_pMainDlg->OutputMsg(" StartServer() - CreateIoCompletionPort / Worker /Accepter 성공" );
	///
	return true;
}

	
bool cIOCompletionPort::BindRecv(stClientInfo * pClientInfo)
{
	DWORD	dwFlag = 0;
	DWORD	dwRecvNumBytes = 0;


	// Overlapped I/O Setting
	pClientInfo->m_stRecvOverlappedEx.m_wsaBuf.len = MAX_SOCKBUF;
	pClientInfo->m_stRecvOverlappedEx.m_wsaBuf.buf = 
		pClientInfo->m_stRecvOverlappedEx.m_szBuf;
	pClientInfo->m_stRecvOverlappedEx.m_eOperation = OP_RECV;

	
	//// 입력 버퍼 클리어 ???????????????
	//ZeroMemory(pClientInfo->m_stRecvOverlappedEx.m_szBuf, 1024);

	int nRet = WSARecv( pClientInfo->m_socketClient,
						&(pClientInfo->m_stRecvOverlappedEx.m_wsaBuf),
						1,
						&dwRecvNumBytes,
						&dwFlag,
						(LPWSAOVERLAPPED)&(pClientInfo->m_stRecvOverlappedEx),
						NULL);
	/// socket_error 이면 client socket이 끊어 진걸로 처리한다.
	if ( nRet ==  SOCKET_ERROR && ( ERROR_IO_PENDING != WSAGetLastError()  )  ) 
	{
		m_pMainDlg->OutputMsg("[에러] BindRecv - WSARecv() 실패 WSAGetLastError:%d:",WSAGetLastError() );

		return false;
	}
	m_pMainDlg->OutputMsg("\n [] BindRecv - WSARecv()   nRet : %d", nRet);
	return true;
}


bool cIOCompletionPort::SendMsg(stClientInfo * pClientInfo, char * pMsg, int nLen)
{
	DWORD	dwRecvNumBytes = 0;

	//전송될 메시지를 복사
	CopyMemory( pClientInfo->m_stSendOverlappedEx.m_szBuf, pMsg, nLen );


	// Overlapped I/O Setting 정보
	pClientInfo->m_stSendOverlappedEx.m_wsaBuf.len = nLen;

	pClientInfo->m_stSendOverlappedEx.m_wsaBuf.buf =
							pClientInfo->m_stSendOverlappedEx.m_szBuf;

	pClientInfo->m_stSendOverlappedEx.m_eOperation = OP_SEND;

	int nRet = WSASend( pClientInfo->m_socketClient, 
						&(pClientInfo->m_stSendOverlappedEx.m_wsaBuf),
						1,
						&dwRecvNumBytes,
						0,
						(LPWSAOVERLAPPED)&(pClientInfo->m_stSendOverlappedEx),
						NULL);
	if ( nRet == SOCKET_ERROR ) {
		m_pMainDlg->OutputMsg("[에러] SendMsg - WSASend() nRet:%s:","SOCKET_ERROR" );
	}
	/// socket_error 이면 client socket이 끊어 진걸로 처리한다.
	if ( nRet ==  SOCKET_ERROR && ( WSAGetLastError() != ERROR_IO_PENDING )  ) 
	{
		m_pMainDlg->OutputMsg("[에러] SendMsg - WSASend() 실패 WSAGetLastError:%d:",WSAGetLastError() );

		return false;
	}
			
	m_pMainDlg->OutputMsg(" [] SendMsg - WSASend() ");

	return true;
}
// 할당
stClientInfo * cIOCompletionPort::GetEmptyClientInfo(void)
{
	for(int i = 0; i < MAX_CLIENT; i++ ) 
	{
		if(INVALID_SOCKET == m_pClientInfo[i].m_socketClient) 
		{
			return &m_pClientInfo[i];
		}
	}
	return NULL;
}



// 사용자 접속 받는 쓰레드
void cIOCompletionPort::ThreadAccepter(void)
{
	SOCKADDR_IN		stClientAddr;

	int nAddrLen = sizeof(SOCKADDR_IN);
	while ( m_bAccepterRun)
	{
		// 접속 받을 구조체 인덱스 얻기
		stClientInfo * pClientInfo = GetEmptyClientInfo();
		if ( NULL == pClientInfo )
		{
			m_pMainDlg->OutputMsg("** AccepterThread [에러] NULL == pClientInfo :%s:","Client FULL");

			return ;
		}
		m_pMainDlg->OutputMsg("** AccepterThread -- accept : pClientInfo->m_socketClient");
		// 클라이언트 접속 요청까지 대기
		//
		//  accept()
		//
		pClientInfo->m_socketClient = accept ( m_socketListen,
							(SOCKADDR *)&stClientAddr, &nAddrLen );

		if ( INVALID_SOCKET == pClientInfo->m_socketClient ) {
			continue;
		}
		m_pMainDlg->OutputMsg("** AccepterThread -- BindIOCompletionPort & 소켓 pClientInfo");
		// I/O Completion Port객체와 소켓을 연결 시킨다.
		//
		//  BindIOCompletionPort()
		//
		bool bRet = BindIOCompletionPort( pClientInfo );

		if ( false == bRet ) {
			return;
		}
		// Recv Overlapped I/O 작업을 요청한다
		m_pMainDlg->OutputMsg("** AccepterThread -- BindRecv & pClientInfo ");
		//
		//  BindRecv()
		//
		bRet = BindRecv(pClientInfo);

		if ( false == bRet ) {
			return;
		}

		m_pMainDlg->OutputMsg("** Accepter Thread -- [클라이언트 접속] ip(%s) SOCKET(%d)",
			inet_ntoa( stClientAddr.sin_addr) ,
			pClientInfo->m_socketClient);

		m_nClientCnt ++;

	}
}


void cIOCompletionPort::ThreadWorker(void)
{
	// CompletionKey를 받을 포인터 변수
	stClientInfo * pClientInfo = NULL;

	// 함수 호출 성공여부
	BOOL bSuccess = TRUE;

	// Overlapped I/O작업에서 전송된 데이타 크기
	DWORD dwIoSize = 0;

	// I/O 작업을 위해 요청한 Overlapped 구조체를 받을 포인터
	LPOVERLAPPED lpOverlapped = NULL;

	while ( m_bWorkerRun ) 
	{
		/**
		이 함수로 인해 쓰래들들은 WaitingThread Queue에 대기상태로 들어간다
		완료된 Overlapped I/O 작업이 발생하면 IOCP Queue에서 완료된 작업을 가져와 뒤처리
		그리고 PostQueuedCompletionStatus()함수에 의해 사용자 메시지가 도착되면 쓰레드 종료
		**/
		bSuccess =	( m_hIOCP,
				&dwIoSize,							// 실제 전송된 바이트
				(LPDWORD)&pClientInfo,				// Completionkey
				&lpOverlapped,						// Overlappped I/O 객체
				INFINITE);							// 대기할 시간(무한대기)
		
		// 클라이언트가 접속 끊었을 때
		//
		//  FALSE == bSuccess
		//
		if ( FALSE == bSuccess && 0 == dwIoSize ) 
		{
			m_pMainDlg->OutputMsg("WorkerThread [클라이언트] SOCKET(%d) 접속 끊김",	pClientInfo->m_socketClient);
			CloseSocket(pClientInfo);
			continue;
		}

		// 사용자 스레드 종료 메시지 처리
		//
		//  TRUE == bSuccess
		//

		if ( TRUE == bSuccess && 0 == dwIoSize && NULL == lpOverlapped )
		{
			//
			// WorkerThread 종료
			//
			m_bWorkerRun = false ;
			continue;
		}

		if ( NULL == lpOverlapped ) {
			continue;
		}
		stOverlappedEx * pOverlappedEx =(stOverlappedEx *)lpOverlapped;

		// Overlapped I/O Recv 작업 결과 뒤 처리
		// 
		//	OP_RECV
		//
		if ( OP_RECV == pOverlappedEx->m_eOperation ) 
		{
			pOverlappedEx->m_szBuf[dwIoSize] = NULL;
			m_pMainDlg->OutputMsg("----->>>>> WorkerThread [수신] ( %d ) bytes , msg : %s ",dwIoSize,pOverlappedEx->m_szBuf);

			// 클라이언트에 메시지를 에코한다.
			//BindRecv( pClientInfo );

			//SendMsg(pClientInfo, pOverlappedEx->m_szBuf, dwIoSize );

			//pOverlappedEx->m_eOperation = OP_SEND;
			//BindRecv( pClientInfo );

			SendMsg( pClientInfo, pOverlappedEx->m_szBuf, dwIoSize);

		}
		// Overlapped I/O Send 작업 결과 뒤 처리
		// 
		//	OP_SEND
		//
		else if ( OP_SEND == pOverlappedEx->m_eOperation ) 
		{
			m_pMainDlg->OutputMsg("<<<<<-------WorkerThread [송신] ( %d ) bytes , msg : %s ",dwIoSize,pOverlappedEx->m_szBuf);
			
			//// 입력 버퍼 클리어 ???????????????
			ZeroMemory(pOverlappedEx->m_szBuf, 1024);

			BindRecv( pClientInfo );

		}
		else 
		{
			m_pMainDlg->OutputMsg("WorkerThread [클라이언트] SOCKET(%d) 예외 상황 ",pClientInfo->m_socketClient);
		}
		lpOverlapped = NULL;
	}
}

void cIOCompletionPort::SetMainDlg(CIOCompletionPortDlg * pMainDlg)
{
	/// .h에서 .cpp로 옮겨 놓았는데? 문제 없나?
	m_pMainDlg = pMainDlg;
}


void cIOCompletionPort::DestroyThread(void)
{
	// 1. 종료 버튼 클릭시 
	/
	for(int i=0; i< MAX_WORKERTHREAD; i++)
	{
		// WaitingThreadQueue에서 대기중인 쓰레드에 사용자 종료 메시지 보내기
		PostQueuedCompletionStatus( m_hIOCP,0,0,NULL);
	}
	// Worker Thread 종료
	for(int i=0; i< MAX_WORKERTHREAD; i++)
	{
		CloseHandle( m_hWorkerThread[i] );
		WaitForSingleObject ( m_hWorkerThread[i], INFINITE );
	}

	m_bAccepterRun = false;
	// Accepter Thread 종료
	closesocket( m_socketListen );
	// Thread 종료
	WaitForSingleObject( m_hAccepterThread, INFINITE );


}


void cIOCompletionPort::CloseSocket(stClientInfo * pClientInfo, bool bIsForce)
{
	struct linger stLinger = {0,0};

	if ( true ) {
		// timeout=0으로 설정되어 강제 종료. 주의 : 데이타 손실 가능성
		// right now !!!
		stLinger.l_onoff = 1;
	}
	// 데이타 송수신 모두 중단
	shutdown( pClientInfo->m_socketClient, SD_BOTH );

	// 소켓 옵션
	setsockopt( pClientInfo->m_socketClient, SOL_SOCKET, SO_LINGER,
			(char *)&stLinger, sizeof(stLinger) );
	// 소켓 연결 종료
	closesocket(pClientInfo->m_socketClient);
	pClientInfo->m_socketClient = INVALID_SOCKET;
}

'온라인게임' 카테고리의 다른 글

Factory 패턴  (0) 2010.10.19
use case diagram  (0) 2010.10.19
S R P G  (0) 2010.10.18
삼성 갤럭시탭, 구글 공식인증 받아  (0) 2010.10.18
홈 버튼이 마지막 액티비티를 재시작 안할때...  (0) 2010.10.18

전략? 무슨 전략?


우선 SRPG 게임이 뭔지, 어떤 장르인지 생각을 좀 해봐야 할 것 같습니다.

4각 격자 위에서 진행되는 SRPG 게임을 하나 하고 있다고 치겠습니다.
 A와 B라는 유닛이 있다고 하지요. 각 유닛은 빨간색으로 표시된 부분에 배치된 적을 공격할 수 있다고 하겠습니다.

 
이제, 아래와 같이 생긴 맵 상에서 적 유닛 C를 공격하려고 합니다.
A, B 가 각각 하나씩 있다고 하겠습니다. 이 유닛들을 어떻게 배치해야 할까요?

 
두말할 것도 없습니다. 정답은 이겁니다.

 
그럼 문제.
유닛 A 를 아래와 같이 배치하면 안되는 이유가 뭘까요?
이렇게 해도 A가 C를 공격할 수 있는 데 말입니다
.


이유는 간단합니다. 위와 같이 A를 배치해도 C를 공격하는 데는 문제가 없습니다. 하지만 이렇게 되면 B는 C를 공격할 자리를 잃어버리게 되죠.

이런 식으로 놓고 보면, 아래 그림에서 회색으로 칠한 칸은 단순히 빈 칸이 아닙니다. 그 자체로 희소한 자원이죠. 유닛의 배치란, 이 자원을 효율적으로 사용하기 위한 판단의 결과물입니다.


 

'온라인게임' 카테고리의 다른 글

use case diagram  (0) 2010.10.19
Winsock2 IOCP  (0) 2010.10.19
삼성 갤럭시탭, 구글 공식인증 받아  (0) 2010.10.18
홈 버튼이 마지막 액티비티를 재시작 안할때...  (0) 2010.10.18
Activity Tag  (0) 2010.10.18

삼성 갤럭시탭, 구글 공식인증 받아

입력시각 : 2010-10-18 16:40

삼성전자는 내달께 국내 시장에 선보일 태블릿PC인 갤럭시탭이 7인치 태블릿PC 가운데 처음으로 구글의 공식 인증을 받았다고 18일 밝혔다.

갤럭시탭이 구글의 공식 인증을 받게 됨에 따라 갤럭시탭으로 구글의 안드로이드 마켓을 포함한 다양한 구글 서비스(검색, 지도 등)의 이용이 가능해졌다.


이번 공식 인증은 안드로이드 2.2버전인 프로요가 7인치 태블릿에는 적합지 않다는 일각에서의 지적과 달리 구글이 7인치 태블릿에 대한 본격적인 지원을 시작한 것으로 풀이된다.

구글은 특히 전 세계 안드로이드 개발자들에게 갤럭시탭의 7인치 WSVGA 해상도에 최적화된 애플리케이션을 개발할 수 있는 개발자 툴킷(SDK)을 전 세계 개발자들에게 배포, 갤럭시탭용 애플리케이션 확보에 적극적으로 나설 계획이다.

한편 삼성전자는 갤럭시 탭 출시 시점에 신문, 잡지, 교육, 내비게이션 등 갤럭시 탭에 최적화되고 국내 환경에 특화된 갤럭시탭용 애플리케이션 150여개를 제공하는 등 삼성앱스를 통해 갤럭시탭용 특화 애플리케이션을 대거 선보일 계획이다.

갤럭시탭용 애플리케이션은 7인치 WSVGA 해상도에 최적화된 화질, 사이즈와 화면 분할 등 고해상도 대형 화면에 특화된 다양한 사용자환경(UI)을 제공한다.

삼성전자 관계자는 "삼성앱스와 안드로이드 마켓에서 소비자가 편리하게 갤럭시탭 애플리케이션을 이용할 수 있는 방안을 지속적으로 강구하는 한편 갤럭시탭 전용 개발 프로그램 제공으로 향후 애플리케이션 확보에도 노력해 나갈 것"이라고 말했다.

(서울연합뉴스) 이광빈 기자 lkbin@yna.co.kr

'온라인게임' 카테고리의 다른 글

Winsock2 IOCP  (0) 2010.10.19
S R P G  (0) 2010.10.18
홈 버튼이 마지막 액티비티를 재시작 안할때...  (0) 2010.10.18
Activity Tag  (0) 2010.10.18
android:launchMode  (0) 2010.10.18
Android Home button not resuming last Activity
 홈 버튼 눌린후에 마지막 액티비티를 재시작 안할때...

[실행결과]
A----->B------->C------>Home -------resume/재실행-------->A
 
[원하는 결과]
A----->B------->C------>Home -------resume/재실행-------->C

I am /trying to solve/ an issue sort of similar to what was done here (link text)/
나는 /문제를 풀기위해 노력중/ 정렬 이슈 링크에서 있었던것과 유사한 /

Except in my case where Activity A, started Activity B, started Activity C,
when the user resumes the app after pressing the home button -
 I want C, the last Activity, to be displayed.

액티비티A에서 액티비티 B가 시작되었고, 액티비티 C가 시작 되었다
사용자가 홈버튼을 누르고 난후 app를 재시작할때,
나는,마지막 액티비티 ,C를 보이기를 원하는데

Instead what is happening is I am back to A. 대신에 A로 돌아가는 현상이 일어난다.

Also on a side note I have discovered that even when I exit the application,
또한 어플리케이션을 종료할때 발견 했다.
 
if I hold down the Home button,
홈버튼을 누른다면

my app is still listed as an active app even though in the LogCat I can see that it exited OK.
내 어플리케이션은 작동중 app으로 여전히 리스트되지만 로그캣에서 종료 성공을 볼 수 있다

Also, in case it matters, 또한 이것과 관련되거나
I did recently add android:launchMode ="singleTask" in order to make some notification logic work correctly.
논리 작업을 정확하게 알려주기 위한 약간의 명시작업을 위해 "싱글 태스크" 실행모드를 추가 했다.

I was seeing the Home button behavior before that though.
나는 홈버튼 동작을 위와 같은 작업 이후에 볼 수 있었다.
 I/ really appreciate/ any ideas/ you might have.
당신이 갖고 있을 어떤 아이디어도 정말로 고마워 할꺼다.

=== When you long press home it is showing a list of recently active applications,
홈을 길게 누르면 최근 활동 어플리케이션의 리스트를 보여준다
not applications that are currently running.
현재 실행중인 어플리케이션은 아니다.

So you should still see your app in the list, even if it is closed.
그래서 여전히 목록에서 어플리케이션을 볼 수 있다, 그것이 종료되었다 하더라도

As the docs note:
The other modes — singleTask and singleInstance — are not appropriate for most applications,
since they result in an interaction model that is likely to be unfamiliar to users and is
very different from most other applications.

I'd imagine that your using singleTask is affecting the history stack,
내가 생각하기에는 당신의 "싱글 태스크"를 사용하는 것이 히스토리 스택에 영향을 미치는 것 같고

and thus preventing Activity C from being remembered.
이와같이 액티비티 C를 재기억 되는 것으로 부터 막는다 ..

 If you remove that from Activity C, does it resolve the problem?
만약 액티비티 C로 부터 그것을 제거한다면, 문제가 해결 되는 것이 아닌가?

Otherwise, I'd look at what you are doing in onPause/onDestroy etc for Activity C.
그 밖에, 액티비티 C를 위해 정지/삭제에 무엇을 할것인가를 살펴 보고 싶다.

Are you doing something that would cause it to finish or close?
해제나 닫기에 영향을 주는 뭔가를 하고 있지 않는가?

'온라인게임' 카테고리의 다른 글

S R P G  (0) 2010.10.18
삼성 갤럭시탭, 구글 공식인증 받아  (0) 2010.10.18
Activity Tag  (0) 2010.10.18
android:launchMode  (0) 2010.10.18
AlertDialog Builder에서  (0) 2010.10.18

+ Recent posts