OnReceive에 접속 요청, 파일 전송 요청 (do-while로 파일 끝까지 다 받기), 종료 요청 따로 따로 체크해서 처리
OnLbnDblclkList1에는 파일 오픈하는 부분만 있으면 됨
// 접속된 클라이언트로부터 데이터 받아와서 리스트박스에 표시하는 역할
// 데이터를 보내는 것은 소켓 클래스의 멤버 함수인 Send를 이용
// 데이터를 받을 때는 통신 소켓 클래스에 오버라이딩한 OnReceive 메시지 함수를 사용
LPARAM CChatServerDlg::OnReceive(UINT wParam, LPARAM lParam) {
//접속된 곳에서 데이터가 도착했을 때
char pTmp[256]; //pTmp : 데이터 임시저장 버퍼 (배열크기 256바이트로 설정)
CString strTmp; // strTmp < MFC 라이브러리에서 제공하는 문자열 클래스
// 메모리초기화 (메모리 크기를 변경할 메모리 시작 주소, 초기화 할 값, 크기 반환 값)
memset(pTmp, '\0', 256); // pTmp버퍼를 널(0)로 초기화 (이전 데이터 초기화하고 새 데이터 받아올 준비함)
// 데이터를 pTmp에 받는다.
m_socCom[wParam]->Receive(pTmp, 256); // wParam = 클라이언트 번호, 데이터 길이는 256바이트
strTmp.Format(_T("%s"), pTmp);
// 클라이언트로부터 접속 종료
if (strTmp.Compare(SOC_CLIENT_DISCONNECT) == 0) {
// erase + remove : 특정 조건에 해당하는 원소 아예 지움
//begin~end : 범위지정, wParam : 지울 값, m_using.end : 컨테이너의 end반복자
m_using.erase(std::remove(m_using.begin(), m_using.end(), wParam),
m_using.end());
count -= 1;
m_strCount.Format(_T("사용자 수 : %d"), count);
if (count == 0) {
m_strStatus = "접속상태 : 대기 중";
}
CString id;
id.Format(_T("%d"), wParam);
int i = m_list.GetCount();
m_list.InsertString(i, (_T("사용자") + id + " : " + _T("접속을 종료하였습니다.")));
UpdateData(false);
}
else if (strTmp.Compare(SOC_FILE_TRANSFER) == 0) {
CString id;
id.Format(_T("%d"), wParam);
int i = m_list.GetCount();
m_list.InsertString(i, (_T("사용자") + id + " : " + strTmp));
for each (int i in m_using) {
if (i != _ttoi(id)) { // 보낸 클라이언트 제외 모든 클라이언트한테 보냄
m_socCom[i]->Send((_T("사용자") + id + " : " + strTmp), 256);
}
}
receivedFile = strTmp.Mid(7);
char fileBuffer[256];
int bytesRead;
CFile file;
do {
// 받은 데이터 fileBuffer에 저장하고 bytesRead만큼 데이터를 파일에 씀
bytesRead = m_socCom[g_wParam]->Receive(fileBuffer, sizeof(fileBuffer));
if (bytesRead > 0) {
file.Write(fileBuffer, bytesRead);
}
} while (bytesRead > 0);
}
else {
// 리스트박스에 보여준다.
CString id;
id.Format(_T("%d"), wParam);
int i = m_list.GetCount();
m_list.InsertString(i, (_T("사용자") + id + " : " + strTmp));
// 이 부분 제외하면 서버만 다중 클라이언트로부터 채팅 가능
for each (int i in m_using) {
if (i != _ttoi(id)) { // 보낸 클라이언트 제외 모든 클라이언트한테 보냄
m_socCom[i]->Send((_T("사용자") + id + " : " + strTmp), 256);
}
}
}
UpdateData(false);
return TRUE;
} //end OnReceive()
void CChatServerDlg::OnLbnDblclkList1()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
int selectedIndex = m_list.GetCurSel(); //선택된 항목 인덱스 저장
if (selectedIndex != LB_ERR) // 오류가 아닐 때
{
CString selectedText;
m_list.GetText(selectedIndex, selectedText); //Index에 해당하는 텍스트를 Text에 저장
// 사용자가 리스트 박스에서 파일 이름을 더블클릭했을 때 ( -1 : 실패)
if (selectedText.Find(_T("[File]")) != -1) {
// 파일 저장 대화상자 열기
//FALSE : 파일 저장 대화상자 열기, nullptr : 부모윈도우핸들,
CFileDialog dlg(FALSE, nullptr, receivedFile, OFN_OVERWRITEPROMPT, _T("All Files (*.*)|*.*||")); // < 모든 파일 선택
if (dlg.DoModal() == IDOK) {
CString savePath = dlg.GetPathName(); // 사용자가 선택한 저장 경로 및 파일 이름
// 파일 데이터를 수신하는 함수를 호출하여 파일을 받아옴
CFile file;
// 파일 생성(이미 있으면 덮어쓰기), 쓰기모드, 이진 파일로 열기
if (file.Open(savePath, CFile::modeCreate | CFile::modeWrite | CFile::typeBinary)) {
//char fileBuffer[256];
//int bytesRead;
//do {
// // 받은 데이터 fileBuffer에 저장하고 bytesRead만큼 데이터를 파일에 씀
// bytesRead = m_socCom[g_wParam]->Receive(fileBuffer, sizeof(fileBuffer));
// if (bytesRead > 0) {
// file.Write(fileBuffer, bytesRead);
// }
//} while (bytesRead > 0);
m_list.AddString(_T("file.Close()"));
file.Close();
AfxMessageBox(_T("파일 저장이 완료되었습니다."));
}
}
}
else {
return;
}
}
} //end OnLbnDblclkList1()
void CChatClientDlg::OnBnClickedFileSend()
{
// 파일 선택 대화 상자를 열어 사용자가 전송할 파일을 선택
CFileDialog dlg(TRUE); // TRUE = 파일을 열기 위한 대화 상자 생성
if (dlg.DoModal() == IDOK) //사용자가 OK 버튼 눌렀을 때 조건문 실행
{
CString filePath = dlg.GetPathName(); //파일 경로
CString fileName = dlg.GetFileName(); //파일 이름
// 서버에 파일 이름 전송
CString request = _T("[File] ") + fileName;
// request.GetLength() * sizeof(TCHAR) < 전송할 데이터(request) 크기 (문자열 길이 * 데이터 타입 크기)
m_socCom.Send(request, request.GetLength() * sizeof(TCHAR));
// 파일 오픈
CFile file;
if (file.Open(filePath, CFile::modeRead | CFile::typeBinary)) // 파일 읽기 모드로 오픈/ 바이너리 모드로 오픈
{
// 파일 크기 가져오기
// (UINT) < 형변환 명시
UINT fileSize = (UINT)file.GetLength(); //GetLength() < CFile 클래스의 멤버함수로 열린 파일의 크기 반환
int nNameLen = fileName.GetLength(); // 파일 이름 길이 저장
// 파일 이름 길이와 파일 이름을 서버로 전송
m_socCom.Send(&nNameLen, sizeof(int)); // 파일 이름 길이 전달
m_socCom.Send((LPCTSTR)fileName, nNameLen * sizeof(TCHAR)); // 파일 이름 전달, LPCTSTR < 문자열 가리키는 포인터
// 파일 크기를 서버로 전송 (파일 크기를 알아야 서버에서 파일을 수신할 수 있기 때문)
m_socCom.Send(&fileSize, sizeof(UINT));
// 파일 데이터를 읽어와 소켓을 통해 전송
CByteArray fileData;
fileData.SetSize(fileSize); // 객체 크기를 fileSize(파일크기)로 설정
file.Read(fileData.GetData(), fileSize); //filesize만큼 파일을 읽어옴
file.Close();
// 소켓을 통해 파일 데이터를 서버에 전송
m_socCom.Send(fileData.GetData(), fileSize); // fileData.GetData() < 파일데이터의 시작위치, fileSize < 전송할 데이터 크기
m_socCom.Send(SOC_FILE_TRANSFER, 256);
// 전송한 데이터 리스트 박스에 보여줌
CString strTmp;
strTmp.Format(_T("나 : [File] %s"), fileName);
int i = m_list.GetCount();
m_list.InsertString(i, strTmp);
UpdateData(FALSE);
// 파일 전송 완료 메시지 표시
MessageBox(_T("파일 전송이 완료되었습니다."));
}
else
{
LPCTSTR pAppNameTemp = AfxGetApp()->m_pszAppName;
AfxGetApp()->m_pszAppName = _T("ERROR");
MessageBox(_T("파일을 열 수 없습니다."));
AfxGetApp()->m_pszAppName = pAppNameTemp;
}
}
}// end OnBnClickedFileSend()