2012. 4. 13. 12:18


===================================================================================================

음성채팅 분석내용이다. 아직 클라이언트만 했지만 일단 큰 흐름은 잡힌것 같다. 파일은 너무 용량이 커서 별첨한다. 

중요한 부분이라고 생각한 것의주로 봐봤는데, 실제 일반 소켓통신에 음성을 받아주는 버퍼값만 추가된 정도 같다.  한글문서로 만들엇다가 그대로 복사한 내용이므로 첨부된 한글 파일로 보면 좀더 편할 것이다.

===================================================================

For CodeReading SIG.hwp

음성채팅 프로그램 분석 [client]

For  SSM  CodeReading SIG

 



void CTalkclientDlg::OnBtnconnect()

: 서버에 연결하는 부분 설정을 잡고 InitSockets를 호출한다.

BOOL CTalkclientDlg::InitSockets(int iProtIndex) :

연결을 위해 소켓을 초기화 시킨다.

AF_IPX 타입인 경우 :

winapi인 GetAddressByName

를 호출함으로 VOICE_TALK_SERVER_IPX 라는 이름으로 프로토콜을 맞춤.

AF_INET 타입인 경우 :

GetAddressByName를 호출함으로써 server name으로 IP를 맞춤

AF_NETBIOS 인 경우 :

SET_NETBIOS_SOCKADDR를 호출함으로써 넷바이오스 설정으로 맞춤

 

 

m_MySock구조체에 소켓을 셋팅 해 준다.

m_iSockType는 소켓의 TYPE을 정해줌으로 인터넷 주소 체계를 지정한다.

m_iProtocol 는 프로토콜을 정의한다.

socket() 함수를 통하여 소켓을 생성하고 그 값을 socket에 저장한다.

 

connect()를 통하여 서버에 연결을 해준다.

 

WSAAsyncSelect() 비동기 분할 처리 방식으로 소켓을 바꾼다.

(이것을 이용하여 다수의 사용자가 동시에 연결 할 수 있게 된다.)

 

 

void CTalkclientDlg::DoConnection(BOOL bTrue)

:연결 했을 때 dialog 상태를 바꾸어 주는 역할만 한다.

 

void CTalkclientDlg::OnBtnsend()

//전송 버튼을 눌렀을 때 행동

typedef struct _VTMSG

{

BYTE m_ucIdentity; // Identifies the message

BYTE m_ucCmd; // message command

BYTE m_ucMsgType; // The message type

UINT m_lLength; // size of the message block

BYTE m_pData[VT_MAX]; // message data

} VTMSG, *LPVTMSG;

구조체에 쓰여질 문자열을 입력한다.

이부분은 일반 C소켓과 좀 다른 부분 같다. 일반 소켓은 char* 형으로 전달되는데 여기서는 음성을 위해 바이트 코드로 전송시켜주기 위함 같다.

 

데이터를 실어 나르는 곳은 m_pData[VT_MAX]; 이 부분이다.

 

SendVTMessage() 메소드를 호출함으로 메시지를 전송 시킨다.

if((sendingSize = (int)send(sock, (char *)sendBuf + byteSent,

byteAll - byteSent, 0)) == SOCKET_ERROR)

{

// Error

return FALSE;

}

byteSent += sendingSize;

 

위에서 말했던 Char * 부분이 여기 있다. send()는 함수를 이용하여 다 전송될 때 까지 보낸다.

 

void CTalkclientDlg::OnBtndisconnect()

:서버에서 연결을 끊는다.

DoConnection(FALSE);

를 이용하여 연결을 끊는다. flase 로 들어가게 되면 closesocket()를 통하여 소켓을 끝내고 다른 변수들을 초기화 시켜 준다.

 

void CTalkclientDlg::OnSelchangeCombchatter()

:사용자를 선택

if(m_ComBoTalker.GetCurSel()==LB_ERR) 메시지 버프에 기존의 정보로 문자열을 체우고 소켓 비동기형으로 다시 소켓 셋팅

else

새로운 사용자의 정보로 비동기 소켓을 열음

 

void CTalkclientDlg::OnChkvoice()

:음성 메시지를 보내는 것을 설정

:일반 메시지보내는 박스를 비활성화 시킴

m_vtRecord.StartRecord();

:녹음을 시작함

BOOL VTRecordWave::StartRecord(void)

: 음성 녹음 메소드

Initialize();

:음성 녹음 초기화

WAVEINCAPS wic;

WAVEFORMATEX wfx;

UINT uiDevID;

MMRESULT rc;

UINT nMaxDev = ::waveInGetNumDevs();

char sError[129];

 

같은 경우 MMSystem.h에서 제공해주는 음성를 저장하기 위한 변수

rc = ::waveInGetDevCaps(uiDevID, &wic, sizeof(wic));

마이크 상태값 가져오기.

 

 

for(uiDevID = 0; uiDevID < nMaxDev; uiDevID++)

{

// Get input device caps

rc = ::waveInGetDevCaps(uiDevID, &wic, sizeof(wic));

if(rc == MMSYSERR_NOERROR)

{

// Set the correct format for wave input device

wfx.nChannels = VTWAVECHANNEL[m_iFormat]; //1: mono, 2, stereo

wfx.nSamplesPerSec = VTWAVEFREQ[m_iFormat];

wfx.wFormatTag = WAVE_FORMAT_PCM;

wfx.wBitsPerSample = VTWAVEBITS[m_iFormat];

wfx.nBlockAlign = wfx.nChannels*wfx.wBitsPerSample/8;

wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

wfx.cbSize = 0;

 

// Open the wave input device

rc = waveInOpen(&m_hwIn, uiDevID, &wfx, (DWORD)m_hWnd,

0, CALLBACK_WINDOW);

 

if (rc == MMSYSERR_NOERROR)

break

else // Failed

{

// Get the error information

waveInGetErrorText(rc, sError, 128),

MessageBox(m_hWnd, sError, "Error in input device opening", MB_OK);

return FALSE;

}

}

}

rc = waveInPrepareHeader(m_hwIn, m_pWhdrIn, sizeof(WAVEHDR));

:음성 녹음을 담당하는 선언 해 주는 부분 (WIN API에서 제공)

[This function prepares a buffer for waveform input. This function allows both the audio driver and the operation System 새애 time consuming processing of the header and /or buffer once at initalization]

 

rc = waveInAddBuffer(m_hwIn, m_pWhdrIn, sizeof(WAVEHDR));

:음성을 담아두는 버퍼 부분

waveInGetErrorText(rc, sError, 128)

: 음성 설정 에러를 체크 하는 부분

rc = waveInStart(m_hwIn);

:음성 기록을 시작.

 

m_vtRecord.StopRecord();

:음성 기록 종료

 

 

void VTRecordWave::StopRecord(void)

:음성 레코드 기록을 멈춤

waveInStop(m_hwIn);

: 음성기록을 멈춤

waveInUnprepareHeader(m_hwIn, m_pWhdrIn, sizeof(WAVEHDR));

:음성준비를 끝냄

waveInClose(m_hwIn);

:음성 핸들러 종료

 

BOOL VTRecordWave::ResetRecord(void) (음성 기록을 상속 시켰으면 좀 편하지 않았을까?)

waveInStop(m_hwIn);

: 음성 기록을 멈춤

: 음성 재 기록 부분

waveInUnprepareHeader(m_hwIn, m_pWhdrIn, sizeof(WAVEHDR));

:음성준비를 끝냄

StopRecord()

:기록 종료

 

void CTalkclientDlg::EnableTalking(BOOL bTrue)

:음성 통신을 위해 상태를 정하는 flag 값이 들어가 있는 곳

:그외엔 별다른 특징 없음

 

void CTalkclientDlg::ParseMsgData(void)

:받은 메시지를 파싱해주는 부분

m_vtRecvMsgBuf.m_ucMsgType

:타입에 따라 어떻게 버퍼에 저장할지 정함

 

case VT_TEXT

: 일반 메시지로써 스트링값에 그냥 저장한다.

strRecv = (char*)(m_vtRecvMsgBuf.m_pData);

case VT_WAVE4S16:

m_progctrlRecv.SetPos(1);

// Get wave data buffer size

m_uiBufLen = m_vtRecvMsgBuf.m_lLength - SIZEVTMSGHDR;

// Copy wave data buffer

memcpy((void*)m_pWaveData, (void*)m_vtRecvMsgBuf.m_pData,m_uiBufLen);

 

음성값을 저장.

 

LONG CTalkclientDlg::OnVTDataReady(WPARAM wparam, LPARAM lparam)

:음성메시지를 위한 응답

 

Case :

m_vtRecvMsgBuf.m_ucCmd == VTCMD_REGNAME: 인 경우

:사용자이름을 말하는 레지스터이다.

:이부분에서 콤보박스에 어떤 사람과 채팅할 것인지 추가된다.

m_vtRecvMsgBuf.m_ucCmd == VTCMD_DEREGNAME 인 경우

:사용자 이름을 말하는 레지스트를 해지한다.

:이 부분에서 콤보박스에 위에서 추가한 사람이름을 삭제한다.

 

m_vtRecvMsgBuf.m_ucCmd == VTCMD_REQSESSION

:세션을 위한 초대부분이다.

:초대하는 sendMsgBuf를 만들어서 보낸다.

m_vtRecvMsgBuf.m_ucCmd == VTCMD_SESSIONREQRESP

:요청을 응답한다.

m_vtRecvMsgBuf.m_ucCmd == VTCMD_SESSIONCLOSE

:사용자가 나간다.

 

 

 

LONG CTalkclientDlg::OnVTReadyForWrite(WPARAM wparam, LPARAM lparam)

:메시지 쓰기를 위한 준비상태

SendMsgBuf 에 VTCMD_REGNAME 값을 cmd 로 넣고 보낸다.

 

 

LONG CTalkclientDlg::OnRecordEvent(WPARAM wparam, LPARAM lparam)

:MM_WIM_DATA를 위한 준비

WAVEHDR* lpwhdr = (WAVEHDR*)lparam;를 선언해 주고 여기에 받는다.

 

m_vtSendMsgBuf.m_ucIdentity = VT_IDENTITY;

m_vtSendMsgBuf.m_ucCmd = VTCMD_MSGDATA;

m_vtSendMsgBuf.m_ucMsgType = m_iWaveFormat;

m_vtSendMsgBuf.m_lLength = SIZEVTMSGHDR + lpwhdr->dwBufferLength;

memcpy((void*)m_vtSendMsgBuf.m_pData, (void*)lpwhdr->lpData,

lpwhdr->dwBufferLength);

이러한 식으로 음성포인터를 전해 전송한다.

 

 

LONG CTalkclientDlg::OnPlayEvent(WPARAM wparam, LPARAM lparam)

:MM_WOM_DONE 플레이 버퍼를 완료함

 

 

Posted by k1rha