UDP 프로토콜 특징
- 연결, 상태 개념이 없음
- 흐름제어 , 송/수신 보장 혹은 확인에 관한 기능 없음
- 개발자 스스로 tcp 구현가능하다
영상 및 게임에서 많이 쓰이게 된다.
//소켓 생성. SOCK_DGRAM 타입을 사용한다!
SOCKET hSocket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (hSocket == INVALID_SOCKET)
ErrorHandler("UDP 소켓을 생성할 수 없습니다.");
//원격지로 메시지를 전송하는 스레드 함수.
DWORD WINAPI ThreadSendto(LPVOID pParam)
{
//송신을 위한 UDP 소켓을 하나 더 개방한다.
SOCKET hSocket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (hSocket == INVALID_SOCKET)
ErrorHandler("UDP 소켓을 생성할 수 없습니다.");
char szBuffer[128];
SOCKADDR_IN remoteaddr = { 0 };
remoteaddr.sin_family = AF_INET;
remoteaddr.sin_port = htons(g_nRemotePort);
remoteaddr.sin_addr.S_un.S_addr = inet_addr(g_szRemoteAddress);
while (1)
{
gets_s(szBuffer);
if (strcmp(szBuffer, "EXIT") == 0)
break;
//사용자가 입력한 메시지를 원격지로 전송한다.
::sendto(hSocket, szBuffer, strlen(szBuffer) + 1, 0,
(sockaddr*)&remoteaddr, sizeof(remoteaddr));
}
//수신을 위한 UDP 소켓을 닫는다. _tmain() 함수의 while문이 끝난다.
::closesocket((SOCKET)pParam);
::closesocket(hSocket);
return 0;
}
별도의 스레드에서 send만 하는 예제이다.
tcp 처럼 메인 스레드에서 accept 된 cilent socket을 들고 있는것이 아니라
매번 remoteaddr에 ip, port를 작성해서 써줘야한다.
UDP 브로드캐스트 송/수신
브로드캐스트가 발생되면 L2 switch에 부하가 많이 발생되게 된다.
tcp에서는 브로드캐스팅이 없다
//소켓 생성
SOCKET hSocket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (hSocket == INVALID_SOCKET)
ErrorHandler("UDP 소켓을 생성할 수 없습니다.");
//포트 바인딩
SOCKADDR_IN addr = { 0 };
addr.sin_family = AF_INET;
addr.sin_port = htons(25000);
addr.sin_addr.S_un.S_addr = htonl(INADDR_BROADCAST);
address 를 braodcast로 설정한다. 255 (0xFF)
wireshark 로 패킷을 보면 destination은 255.255.255.255 , mac 은ff로 되어있다.
DNS 쿼리 예시
#include "stdafx.h"
#include <winsock2.h>
#pragma comment(lib, "ws2_32")
int _tmain(int argc, _TCHAR* argv[])
{
//윈속 초기화
WSADATA wsa = { 0 };
if (::WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
puts("ERROR: 윈속을 초기화 할 수 없습니다.");
return 0;
}
//현재 DNS 설정을 근거로 Naaver의 IP주소를 질의한다.
hostent *pHost = ::gethostbyname("www.naver.com");
if (pHost == NULL)
{
puts("ERROR: Naver의 IP주소를 알 수 없습니다.");
::WSACleanup();
return 0;
}
printf("Official name: %s\n", pHost->h_name);
for (int i = 0; pHost->h_aliases[i] != NULL; ++i)
printf("\t별칭: %s\n", pHost->h_aliases[i]);
for (int i = 0; pHost->h_addr_list[i] != NULL; ++i)
printf("\tIP주소: %s\n",
inet_ntoa(*(in_addr*)pHost->h_addr_list[i]));
//윈속 해제
::WSACleanup();
return 0;
}
NIC Adapter 정보 소스로 확인하기
#include "stdafx.h"
#include <winsock2.h>
//IP Helper API 사용을 위한 헤더 및 라이브러리 설정
#include <iphlpapi.h>
#pragma comment(lib, "Iphlpapi.lib")
#include <stdio.h>
#include <stdlib.h>
int _tmain(int argc, _TCHAR* argv[])
{
//한 NIC에 대한 설정이 저장될 수 있는 메모리 확보.
ULONG uBufferLength = sizeof (IP_ADAPTER_INFO);
PIP_ADAPTER_INFO pNicInfo = (IP_ADAPTER_INFO*)malloc(uBufferLength);
//설정 정보를 확인한다.
DWORD dwResult = ::GetAdaptersInfo(pNicInfo, &uBufferLength);
if (dwResult == ERROR_BUFFER_OVERFLOW)
{
//NIC이 2개 이상이라 모든 정보를 담을 수 없는 경우이므로
//메모리를 다시 할당 한다.
free(pNicInfo);
pNicInfo = (IP_ADAPTER_INFO *)malloc(uBufferLength);
//버퍼 크기 조정 후 다시 설정 정보를 확인 한다.
if (::GetAdaptersInfo(pNicInfo, &uBufferLength) != NO_ERROR)
{
free(pNicInfo);
puts("ERROR: 어댑터 정보를 알 수 없습니다.");
return 0;
}
}
//연결 리스트 형태로 설정 정보에 접근한다.
PIP_ADAPTER_INFO pNicInfoTmp = pNicInfo;
while (pNicInfoTmp != NULL)
{
printf("설명: %s\n", pNicInfoTmp->Description);
printf("MAC 주소: %02X-%02X-%02X-%02X-%02X-%02X\n",
pNicInfoTmp->Address[0], pNicInfoTmp->Address[1],
pNicInfoTmp->Address[2], pNicInfoTmp->Address[3],
pNicInfoTmp->Address[4], pNicInfoTmp->Address[5]);
printf("IP 주소: %s\n\n",
pNicInfo->IpAddressList.IpAddress.String);
pNicInfoTmp = pNicInfoTmp->Next;
}
free(pNicInfo);
return 0;
}
ipconfig를 소스로 보는 방법
'Network > Windows 소켓 프로그래밍 입문에서 고성능 서버까지!' 카테고리의 다른 글
서버 성능 개선 (0) | 2023.11.28 |
---|---|
파일 송/수신과 프로토콜 설계 (0) | 2023.11.27 |
파일 송/수신 (0) | 2023.11.23 |
TCP 채팅 서버 - 성능 개선 (0) | 2023.11.22 |
TCP 채팅 서버 - 기본 이론 (0) | 2023.11.20 |