UDP 통신 (채팅)프로그램 질문입니다.

익명 사용자의 이미지

현재 MFC랑 C를 이용해서 UDP 채팅 프로그램을 제작하고 있습니다.
서버 및 클라이언트에서 수신 송신을 둘다 해야하기에 둘다 bind를 해주고 작업중인데
recvfrom과 sendto에서 -1을 반환하면서 오류가 나옵니다.
어떤게 문제인지 잘 모르겠습니다 검색해서 하는대로 한건데 되질 않네요

서버 코드입니다

static UINT UdpServerFunc(LPVOID pVoid)
{
	CServerDlg *dlg = (CServerDlg *)AfxGetApp()->m_pMainWnd;
 
	//------------------------------------------------------
	CString slniPath;
	slniPath.Format(_T("./NetworkPath.ini"));
	TCHAR ServerPort[MAX_PATH];
	int ServerPort_Num;
	GetPrivateProfileString(_T("ServerInfo"), _T("Port"), _T(""), ServerPort, MAX_PATH, slniPath);
	ServerPort_Num = _ttoi(ServerPort);
	//------------------------------------------------------
 
	//Startup Winsock 
	WSADATA wsaData; //WinSock을 위한 내부 자료구조
	int retval = WSAStartup(MAKEWORD(2, 2), &wsaData); 
	if (retval != 0) 
	{
		AfxMessageBox("WSAStartup() Error\n");
		return 0;
	}
 
	//SOCKET
	dlg->server_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);//IPPROTO_UDP or PPROTO_HOPOPTS
	if (dlg->server_sock == SOCKET_ERROR)
	{
		AfxMessageBox("Socket() Error\n");
		return 0;
	}
 
	//BIND server_addr : socket에 주소를 할당하고 port번호를 할당하기 위함
	ZeroMemory(&dlg->server_addr, sizeof(dlg-> server_addr));
	dlg->server_addr.sin_family = AF_INET;
	dlg->server_addr.sin_port = htons(ServerPort_Num); //30112
	dlg->server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //32bit IPV4 주소
 
	dlg->client_addr.sin_family = AF_INET;
	dlg->client_addr.sin_port = htons(ServerPort_Num);
	dlg->client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 
	//recvfrom을 받기위해 
	retval = bind(dlg->server_sock, (SOCKADDR*)&dlg->server_addr, sizeof(dlg->server_addr));
 
	AfxMessageBox("server start");
	if (retval == SOCKET_ERROR) {
		AfxMessageBox("bind() ERROR\n");
		return -1;
	}
	//Data Communication
	int addrlength;
	char buf[BUFFER_SIZE];
	int recv_size;
	while (1)
	{		
		//recvfrom()
		addrlength = sizeof(dlg->client_addr);  //데이터를 받을 정보를 같고있는 ---
		recv_size = recvfrom(dlg->server_sock, reinterpret_cast<char*>(buf), BUFFER_SIZE, 0, (SOCKADDR*)&dlg->client_addr, &addrlength);
		if (recv_size == SOCKET_ERROR)
		{
			AfxMessageBox("recvfrom() Error");
			break;
		}
		//Data print
		buf[recv_size] = '\0';
		CString strMsg;
		strMsg = CString(buf, recv_size);
		dlg->m_ListChat.AddString(strMsg);
		strMsg = "";
 
	}
	closesocket(dlg->server_sock);
	WSACleanup;
}

송신관련 코드입니다

void CServerDlg::OnBnClickedButton2()
{
	CString strText = _T("");
	char Text[BUFFER_SIZE];
	dlg->m_edit_chat.GetWindowText(strText);
	strcpy(Text, strText);
	int send_size = sendto(dlg->server_sock, Text, strlen(Text), 0, (sockaddr*)&dlg->client_addr, sizeof(dlg->client_addr));
	if (send_size == SOCKET_ERROR)
	{
		AfxMessageBox("sendto() Error");
	}
	else
	{
		dlg->m_ListChat.AddString(strText);
	}
 
}

클라이언트 코드입니다

static UINT ClientUdpThreadFunc(LPVOID pVOID) {
 
	CClientTestDlg *dlg = (CClientTestDlg *)AfxGetApp()->m_pMainWnd;
	//----Edit Box에 입력된 정보를 포트/IP 값 가져오기----
	CString slniPath;
	slniPath.Format(_T("./NetworkPath.ini"));
 
	TCHAR ServerPort[MAX_PATH];
	int ServerPort_Num;
	GetPrivateProfileString(_T("ServerInfo"), _T("Port"), _T(""), ServerPort, MAX_PATH, slniPath);
	ServerPort_Num = _ttoi(ServerPort); //30112
 
	TCHAR ServerIp[MAX_PATH];
	int ServerIp_Num;
	GetPrivateProfileString(_T("ServerInfo"), _T("IP"), _T(""), ServerIp, MAX_PATH, slniPath);
	ServerIp_Num = _ttoi(ServerIp); //127.0.0.1
	//--------------------------------------------------
 
 
	//StartUp Winsock
	WSADATA wsaData;
	int retval = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (retval != 0)
	{
		AfxMessageBox("WSAStartup() Error\n");
		return 0;
	}
 
	//socket
	dlg->clnt_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 
	if (dlg->clnt_sock == SOCKET_ERROR) {
		AfxMessageBox(_T("socket() Error"));
		return 0;
	}
 
	// 소켓 주소 구조체 초기화 (상대 시스템 주소 정보)
	ZeroMemory(&dlg->ServerAddr, sizeof(dlg->ServerAddr));
	dlg->ServerAddr.sin_family = AF_INET; //IP 사용 - IPv4
	dlg->ServerAddr.sin_port = htons(ServerPort_Num); //Port = EditBox에 쓴 값
	dlg->ServerAddr.sin_addr.s_addr = inet_addr(SERVERIP); //SERVERIP = 127.0.0.1
	/*
	dlg->FromServer.sin_family = AF_INET;
	dlg->FromServer.sin_port = htons(ServerPort_Num);
	dlg->FromServer.sin_addr.s_addr = inet_addr(SERVERIP);
	*/
	retval = bind(dlg->clnt_sock, (SOCKADDR*)&dlg->ServerAddr, sizeof(dlg->ServerAddr));
 
	if (retval == SOCKET_ERROR) {
		AfxMessageBox("bind() ERROR\n");
		return -1;
	}
 
	//Data Communication
	CString strMsg;
	int addrlength;
	char buf[BUF_SIZE];
	int recv_size;
 
	while (1) {
		addrlength = sizeof(dlg->FromServer);
		recv_size = recvfrom(dlg->clnt_sock, reinterpret_cast<char*>(buf), BUF_SIZE, 0, (sockaddr*)&dlg->FromServer, &addrlength);
		if (recv_size == SOCKET_ERROR)
		{
			AfxMessageBox("recvfrom() ERROR");
			break;
		}
		else 
		{
			//Data print
			buf[recv_size] = '\0';
			strMsg = CString(buf, recv_size);
			dlg->m_listChat.AddString(strMsg);
			strMsg = "";
		}
 
 
	}		
 
	closesocket(dlg->clnt_sock);
	WSACleanup;
}
라스코니의 이미지

자세히 보지는 않았습니다.
서버나 클라이언트 모두 소켓을 생성한 후에는 recvfrom()를 하고 있네요. 클라이언트는 서버 포트를 알고 있지만, 서버는 뭔가를 클라이언트로부터 받기 전까지는 클라이언트 어떤 포트로 붙을지 모릅니다. 그래서 클라이언트가 맨 처음에 뭔가를 sendto()를 통해 서버로 보내야 합니다. 그래야지 서버는 접속한 클라이언트 포트를 통해 뭔가를 클라이언트에게 되돌려 보낼 수 있습니다.

구글링으로 echo 서버 예제를 살펴 보세요.

l595659의 이미지

서버단에서 뭘 recvfrom 할지를 모르니까 못받는가보네요... sendto했을때 알아서 포트랑 가져온다길ㄹ래 이래 해놨는데 이러면 안되는거군요

라스코니의 이미지

서버에서 client_addr()를 설정할 때 (특정한 경우가 아니면 설정할 필요가 없음) client port number로 다시 서버 포트 번호를 넣네요. recvfrom()을 받아서 client_addr를 업데이트하기 전에는 결국 서버와 클라이언트가 같은 포트 번호를 가지고 있으니 만약 sendto()를 한다면 동작하지 않을 것이구요.

서버에서 server port 번호만 넣고 bind() 하시고, recvfrom()을 하세요. 클라이언트에서 뭔가 보내면 recvfrom()의 인자로 들어가 있는 client_addr에 그 주소, port 번호가 들어가 있을 겁니다. 그 걸로 sendto() 같은 걸 하시면 됩니다. 요약하자면 클라이언트에 접근할 수 있는 정보가 확인되기 전(서버에서 recvfrom()이 지난 뒤에)에는 클라이언트에 보낼 수 없다는 것입니다.

구글링해서 udp echo server 예제를 찾아 참고하시기 바랍니다.

l595659의 이미지

말씀해주신 부분 잘 이해했습니다 어차피 recvfrom에서 클라이언트에서 port번호가 들어와있으니까 따로 client_addr를 설정 할 필요가 없으니 지웠습니다
한번 검색해서 해보고 답글 달겠습니다

l595659의 이미지

UDP ECHO SERVER 검색해서 예제대로 해보니까 잘 붙고 잘 되네요 처음에 sendto를 해줘서 recvfrom으로 받아올 값이 없어서 안되는거더라구요 이제 여러 클라이언트가 붙는쪽을 해야겟네요
클라이언트가 여러개 붙을라니까 마지막에 들어온쪽에만 붙네요..

라스코니의 이미지

마지막에 붙은 정보만 client_addr에 기록되어서 그렇습니다.
이때 list, vector, 또는 map이 필요한 시점이죠. 개인적으로 map이 가장 적합하다고 생각합니다.

일단 서버와 클라이언트 IP가 같다고 가정하면 map(port number, 클라이언트 정보) 식으로 구성하시면 될 것으로 보이네요.

l595659의 이미지

덕분에 급한건 다 끝냈네요...이젠 공부하면서 다른거 만들어보는것만 남았어요..... 감사합니다~

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.