UDP 통신 (채팅)프로그램 질문입니다.
글쓴이: 익명 사용자 / 작성시간: 일, 2022/06/12 - 12:41오후
현재 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; }
Forums:
자세히 보지는 않았습니다.
자세히 보지는 않았습니다.
서버나 클라이언트 모두 소켓을 생성한 후에는 recvfrom()를 하고 있네요. 클라이언트는 서버 포트를 알고 있지만, 서버는 뭔가를 클라이언트로부터 받기 전까지는 클라이언트 어떤 포트로 붙을지 모릅니다. 그래서 클라이언트가 맨 처음에 뭔가를 sendto()를 통해 서버로 보내야 합니다. 그래야지 서버는 접속한 클라이언트 포트를 통해 뭔가를 클라이언트에게 되돌려 보낼 수 있습니다.
구글링으로 echo 서버 예제를 살펴 보세요.
순서문젠가보네요
서버단에서 뭘 recvfrom 할지를 모르니까 못받는가보네요... sendto했을때 알아서 포트랑 가져온다길ㄹ래 이래 해놨는데 이러면 안되는거군요
서버에서 client_addr()를 설정할 때
서버에서 client_addr()를 설정할 때 (특정한 경우가 아니면 설정할 필요가 없음) client port number로 다시 서버 포트 번호를 넣네요. recvfrom()을 받아서 client_addr를 업데이트하기 전에는 결국 서버와 클라이언트가 같은 포트 번호를 가지고 있으니 만약 sendto()를 한다면 동작하지 않을 것이구요.
서버에서 server port 번호만 넣고 bind() 하시고, recvfrom()을 하세요. 클라이언트에서 뭔가 보내면 recvfrom()의 인자로 들어가 있는 client_addr에 그 주소, port 번호가 들어가 있을 겁니다. 그 걸로 sendto() 같은 걸 하시면 됩니다. 요약하자면 클라이언트에 접근할 수 있는 정보가 확인되기 전(서버에서 recvfrom()이 지난 뒤에)에는 클라이언트에 보낼 수 없다는 것입니다.
구글링해서 udp echo server 예제를 찾아 참고하시기 바랍니다.
구글링 해보겠습니다
말씀해주신 부분 잘 이해했습니다 어차피 recvfrom에서 클라이언트에서 port번호가 들어와있으니까 따로 client_addr를 설정 할 필요가 없으니 지웠습니다
한번 검색해서 해보고 답글 달겠습니다
구글링해서 테스트해보니
UDP ECHO SERVER 검색해서 예제대로 해보니까 잘 붙고 잘 되네요 처음에 sendto를 해줘서 recvfrom으로 받아올 값이 없어서 안되는거더라구요 이제 여러 클라이언트가 붙는쪽을 해야겟네요
클라이언트가 여러개 붙을라니까 마지막에 들어온쪽에만 붙네요..
마지막에 붙은 정보만 client_addr에
마지막에 붙은 정보만 client_addr에 기록되어서 그렇습니다.
이때 list, vector, 또는 map이 필요한 시점이죠. 개인적으로 map이 가장 적합하다고 생각합니다.
일단 서버와 클라이언트 IP가 같다고 가정하면 map(port number, 클라이언트 정보) 식으로 구성하시면 될 것으로 보이네요.
말씀해주신 힌트에서 해결했네요
덕분에 급한건 다 끝냈네요...이젠 공부하면서 다른거 만들어보는것만 남았어요..... 감사합니다~
댓글 달기