본문 바로가기
C++/예제

[C++][MFC] UDP 통신 (2) 채팅 프로그램 멀티캐스트 예제

by 부먹짱 2022. 9. 19.
반응형

UDP 통신 (1) 채팅 프로그램 1대1 예제

 

[C++][MFC] UDP 통신 (1) 채팅 프로그램 1대1 예제

UDP 통신으로 서버, 클라이언트의 구분이 없는 1대1 채팅 프로그램을 구현하였다. UDP_Server.h public: CListBox m_msg_list; CEdit msgEdit; CEdit rIpEdit; CEdit rPortEdit; CEdit mPortEdit; void InsertMsg..

bumukisbest.tistory.com

 

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);
}

 

결과 화면

 

 

UDP_ServerDlg.h
0.00MB
UDP_ServerDlg.cpp
0.01MB

 

반응형

댓글