반응형
UDP를 이용한 1대1 채팅에 이어 UDP 멀티캐스트를 이용한 다중 접속 채팅 프로그램을 구현하였다.
UDP_ServerDlg.h
public:
CListBox m_msg_list;
CEdit msgEdit;
CEdit mtIpEdit;
CEdit mtPortEdit;
CEdit myNumEdit;
void InsertMsg(CString str);
afx_msg void ConnectBtnClicked();
afx_msg void SendBtnClicked();
static UINT RecvFunc(LPVOID pParam);
private:
SOCKET send_socket;
SOCKET recv_socket;
SOCKADDR_IN my_addr;
SOCKADDR_IN multi_addr;
UDP_ServerDlg.cpp
// 연결 버튼 클릭 함수
void CUDPServerDlg::ConnectBtnClicked()
{
UINT mtPort;
CString strPort2;
mtPortEdit.GetWindowText(strPort2); // 사용자 입력 멀티 포트
mtPort = _tstoi(strPort2);
TCHAR mtIp[20];
mtIpEdit.GetWindowTextA(mtIp, 20); // 사용자 입력 멀티 ip
// 나의 IP 가져오기
char myaddr[20] = "";
gethostname(myaddr, sizeof(myaddr));
PHOSTENT hostInfo = gethostbyname(myaddr);
SOCKADDR_IN addr;
CArray<sockaddr_in, sockaddr_in& > ipArray;
CString msgIp("");
if (hostInfo) {
for (int i = 0; hostInfo->h_addr_list[i] != NULL; i++) {
memcpy(&addr.sin_addr, hostInfo->h_addr_list[i], hostInfo->h_length);
ipArray.Add(addr);
}
}
addr.sin_family = AF_INET;
addr.sin_port = htons(mtPort);
addr.sin_addr.s_addr = ipArray.GetAt(0).sin_addr.s_addr;
multi_addr.sin_family = AF_INET;
multi_addr.sin_port = htons(mtPort);
multi_addr.sin_addr.s_addr = inet_addr(mtIp);
// 메시지 송신을 위한 소켓
send_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (send_socket == INVALID_SOCKET)
{
InsertMsg("send socket error");
return;
}
// 멀티캐스트 패킷의 TTL 설정, 성공하면 sentto 가능
int time_live = 64; // TTL
if (setsockopt(send_socket, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&time_live, sizeof(time_live)) < 0)
{
InsertMsg("ttl error");
return;
}
// 메시지 수신을 위한 소켓
recv_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (recv_socket == INVALID_SOCKET)
{
InsertMsg("recv socket error");
return;
}
// 주소 재사용 설정 (생략 가능)
UINT yes = 1;
if (setsockopt(recv_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)) < 0)
{
InsertMsg("reuse error");
return;
}
// 멀티캐스트 그룹 가입을 위한 구조체
struct ip_mreq socket_group;
socket_group.imr_multiaddr = multi_addr.sin_addr;
socket_group.imr_interface.s_addr = ipArray.GetAt(0).sin_addr.s_addr;
// 멀티캐스트 그룹 가입
if (setsockopt(recv_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&socket_group, sizeof(socket_group)) < 0)
{
InsertMsg("membership error");
return;
}
// 수신 소켓 bind()
if (bind(recv_socket, (SOCKADDR*)&addr, sizeof(addr)) == -1)
{
InsertMsg(_T("bind error"));
return;
}
InsertMsg(_T("Address Setting Complete"));
AfxBeginThread(RecvFunc, this); // 수신 스레드 시작
}
반응형
// 수신 스레드
UINT CUDPServerDlg::RecvFunc(LPVOID pParam) {
CUDPServerDlg* pDlg = (CUDPServerDlg*)pParam;
while (1)
{
char temp[1024] = "";
int strlen;
// 메시지 수신, 수신한 메시지 길이 반환
strlen = recvfrom(pDlg->recv_socket, temp, sizeof(temp), 0, NULL, NULL);
if (strlen == -1) // 오류
{
pDlg->InsertMsg("read error");
break;
}
else // 메시지 정상 수신
{
// 멀티 그룹 전체에 보내기 때문에 자신의 메시지도 수신
CString str = (LPSTR)temp;
pDlg->InsertMsg(str); // 메지시 ListBox 추가
}
}
closesocket(pDlg->recv_socket);
return 0;
}
// 전송 버튼 클릭 함수
void CUDPServerDlg::SendBtnClicked()
{
UINT mNum;
CString strNum;
myNumEdit.GetWindowText(strNum);
mNum = _tstoi(strNum); // 고유번호
CString msg, str;
msgEdit.GetWindowText(msg);
// 메시지 보낼 때 User+num으로 보이도록 메시지 가공
str.Format(_T("User%d : "), mNum);
msg = str + msg;
int len = msg.GetLength();
char* message = new char[len + 10];
strcpy(message, msg.GetBuffer(0));
// 상대에게 메시지 전송
if (sendto(send_socket, message, 1024, 0, (SOCKADDR*)&multi_addr, sizeof(multi_addr)) == -1)
{
InsertMsg("send error");
}
else
{
msgEdit.SetWindowTextA("");
}
}
// 메시지 ListBox 추가 함수
void CUDPServerDlg::InsertMsg(CString str)
{
m_msg_list.InsertString(-1, str);
m_msg_list.SetTopIndex(m_msg_list.GetCount() - 1);
}
반응형
'C++ > 예제' 카테고리의 다른 글
[C++][MFC] UDP 통신 (1) 채팅 프로그램 1대1 예제 (2) | 2022.09.19 |
---|---|
[C++][MFC] TCP/IP 통신 (2) 채팅 프로그램 클라이언트 예제 (1) | 2022.09.17 |
[C++][MFC] TCP/IP 통신 (1) 채팅 프로그램 서버 예제 (1) | 2022.09.17 |
[C][C++] 배열을 활용한 바이트 스왑 예제 (0) | 2022.04.07 |
댓글