본문 바로가기

Programming Language/C&C++

[소켓 프로그래밍]UDP 브로드캐스트에 의한 데이터 보내기 및 받기

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

보내기 측 코드

다운로드 시 참고: Dev-C++로 작성되었음.

UDP Broadcast Send.rar

/* Example: main.c */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <assert.h>
#include <string.h>

#include <winsock2.h>
#include <ws2tcpip.h>

#define PORT 65429

int main(int argc, char *argv[])
{
	int temp;
	char message[] = "Hello, World!";
	
	WSADATA wsaData;
	SOCKET sock;
	int fBroadcast = 1;
	struct sockaddr_in sockaddr1;
	struct sockaddr_in sockaddr2;
	
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	
	/* 소켓을 생성한다. socket([IPv4 인터넷 사용], [데이터그램 방식의 패킷 사용], [데이터그램이므로 UDP 프로토콜 사용]); */
	printf("trying: socket...\n");
	if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
	{
		temp = WSAGetLastError();
		printf("Socket Error: %x(%d)\n", temp, temp);
		assert(sock != INVALID_SOCKET);
	}
	
	/* 소켓에 옵션을 지정한다. setsockopt([소켓], [옵션 지정 대상], [옵션 값], [옵션 값의 크기]); */
	printf("trying: setsockopt...\n");
	if ((temp = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char *)&fBroadcast, sizeof(fBroadcast))) == SOCKET_ERROR)
	{
		temp = WSAGetLastError();
		printf("Socket Error: %x(%d)\n", temp, temp);
		
		if ((temp = closesocket(sock)) == SOCKET_ERROR)
		{
			temp = WSAGetLastError();
			printf("Socket Error: %x(%d)\n", temp, temp);
			assert(temp != SOCKET_ERROR);
		}
		
		assert(temp != SOCKET_ERROR);
	}
	
	/* UDP 패킷을 전송할 대상을 지정한다. { 인터넷 사용, 255.255.255.255를 대상으로 함, 포트 번호 } */
	memset(&sockaddr1, 0, sizeof(struct sockaddr_in));
	sockaddr1.sin_family = AF_INET;
	sockaddr1.sin_addr.S_un.S_addr = inet_addr("255.255.255.255");
	sockaddr1.sin_port = htons(PORT);
	
	/* 패킷을 전송한다. sendto([소켓], [보낼 값], [보낼 값의 크기], [전송 모드인데 WinSock에서는 그냥 0], [보낼 주소], [보낼 주소의 길이]); */
	printf("trying: sendto...\n");
	if ((temp = sendto(sock, message, strlen(message), 0, (const struct sockaddr *)&sockaddr1, sizeof(struct sockaddr_in))) == SOCKET_ERROR)
	{
		temp = WSAGetLastError();
		printf("Socket Error: %x(%d)\n", temp, temp);
		
		if ((temp = closesocket(sock)) == SOCKET_ERROR)
		{
			temp = WSAGetLastError();
			printf("Socket Error: %x(%d)\n", temp, temp);
			assert(temp != SOCKET_ERROR);
		}
		
		assert(temp != SOCKET_ERROR);
	}
	
	
	/* 소켓을 닫는다. */
	printf("trying: closesocket...\n");
	if ((temp = closesocket(sock)) == SOCKET_ERROR)
	{
		temp = WSAGetLastError();
		printf("Socket Error: %x(%d)\n", temp, temp);
		assert(temp != SOCKET_ERROR);
	}
		
	WSACleanup();
	
	printf("END");
	
	return 0;
}

함수의 호출 과정은 socketsetsockoptsendtoclosesocket이며, WinSock은 WSAStartupsocketsetsockoptsendtoclosesocketWSACleanup이다.

socket

데이터그램(Datagram)은 전송 단위의 순서와 무관하게 각 송수신 단위가 독립적인 데이터이며 수신자와 송신자 상호간 확인 과정이 없다. (데이터그램이나 UDP나 그 말이 그 말이다.)

UDP 브로드캐스팅을 위한 소켓의 생성은 SOCK_DGRAM 옵션을 사용한다. SOCK_DGRAM 옵션은 프로토콜로 IPPROTO_UDP가 동반되며, SOCK_DGRAMIPPROTO_UDP의 조합은 접속 방식으로 AF_INET(IPv4 프로토콜) 또는 AF_INET6(IPv6 프로토콜)을 필요로 한다.

Datagram에 의한 패킷의 전송을 하고 싶을 때는 (AF_INET, SOCK_DGRAM, IPPROTO_UDP) 또는 (AF_INET6, SOCK_DGRAM, IPPROTO_UDP)가 한 세트로 지정된다.

(참조: https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx)

setsockopt

두 번째 매개변수인 level과 세 번째 매개변수인 optname이 중요한데 지정할 수 있는 옵션이 너무 많으니 https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx를 참조한다.

사용되는 구조체 또는 자료형은 다음과 같다.

  • WSADATA
  • SOCKET
  • sockaddr_in

 

받기 측 코드

다운로드 시 참고: Dev-C++로 작성되었음.

UDP Broadcast Receive.rar

/* Example: main.c */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <assert.h>
#include <string.h>

#include <winsock2.h>
#include <ws2tcpip.h>

#define BUFFER_SIZE 256
#define PORT 65429

int main(int argc, char *argv[])
{
	int temp;
	char buffer[BUFFER_SIZE];
	ssize_t bufferSize;
	
	WSADATA wsaData;
	SOCKET sock;
	struct timeval timeout;
	socklen_t socklen1;
	struct sockaddr_in sockaddr1;
	socklen_t socklen2;
	struct sockaddr_in sockaddr2;
	
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	
	/* 소켓을 생성한다. socket([IPv4 인터넷 사용], [데이터그램 방식의 패킷 사용], [데이터그램이므로 UDP 프로토콜 사용]); */
	printf("trying: socket...\n");
	if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
	{
		temp = WSAGetLastError();
		printf("Socket Error: %x(%d)\n", temp, temp);
		assert(sock != INVALID_SOCKET);
	}
	
	memset(&timeout, 0, sizeof(struct timeval));
	timeout.tv_sec = 10;
	timeout.tv_usec = 0;
	
	/* 소켓에 옵션을 지정한다. setsockopt([소켓], [옵션 지정 대상], [옵션 값], [옵션 값의 크기]); */
	printf("trying: setsockopt...\n");
	if ((temp = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(struct timeval))) == SOCKET_ERROR)
	{
		temp = WSAGetLastError();
		printf("Socket Error: %x(%d)\n", temp, temp);
		
		if ((temp = closesocket(sock)) == SOCKET_ERROR)
		{
			temp = WSAGetLastError();
			printf("Socket Error: %x(%d)\n", temp, temp);
			assert(temp != SOCKET_ERROR);
		}
		
		assert(temp != SOCKET_ERROR);
	}
	
	/* UDP 패킷을 수신할 대상을 지정한다. 인터넷(AF_INET)을 통해 지정된 포트(sin_port)로 들어오는 아무 발신지(INADDR_ANY)나 대기한다. */
	memset(&sockaddr1, 0, sizeof(struct sockaddr_in));
	socklen1 = sizeof(struct sockaddr_in);
	sockaddr1.sin_family = AF_INET;
	sockaddr1.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	sockaddr1.sin_port = htons(PORT);
	
	/* 연결 대기한다. 위의 setsockopt에서 지정한 제한시간이 지나면 타임아웃되어 SOCKET_ERROR를 반환한다. */
	printf("trying: bind [timeout = %d]...\n", timeout.tv_sec);
	if ((temp = bind(sock, (struct sockaddr *)&sockaddr1, sizeof(struct sockaddr_in))) == SOCKET_ERROR)
	{
		temp = WSAGetLastError();
		printf("Socket Error: %x(%d)\n", temp, temp);
		
		if ((temp = closesocket(sock)) == SOCKET_ERROR)
		{
			temp = WSAGetLastError();
			printf("Socket Error: %x(%d)\n", temp, temp);
			assert(temp != SOCKET_ERROR);
		}
		
		assert(temp != SOCKET_ERROR);
	}

	/* 연결되면 데이터를 수신한다. recvfrom([소켓], [데이터 담을 버퍼], [버퍼의 최대 크기], [수신 시 옵션인데 그냥 0], [발신지 IP 주소 저장할 구조체], [구조체의 크기]); */	
	memset(&buffer, 0, BUFFER_SIZE);
	memset(&sockaddr2, 0, sizeof(struct sockaddr_in));
	socklen2 = sizeof(struct sockaddr_in);
	if ((bufferSize = recvfrom(sock, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&sockaddr2, (int *)&socklen2)) < 0)
	{
		temp = WSAGetLastError();
		printf("Socket Error: %x(%d)\n", temp, temp);
		
		if ((temp = closesocket(sock)) == SOCKET_ERROR)
		{
			temp = WSAGetLastError();
			printf("Socket Error: %x(%d)\n", temp, temp);
			assert(temp != SOCKET_ERROR);
		}
		
		assert(temp != SOCKET_ERROR);
	}
	
	if ((temp = closesocket(sock)) == SOCKET_ERROR)
	{
		temp = WSAGetLastError();
		printf("Socket Error: %x(%d)\n", temp, temp);
		assert(temp != SOCKET_ERROR);
	}
		
	assert(temp != SOCKET_ERROR);
		
	printf("message is : %s\n", buffer);
	printf("size is: %d byte(s)\n", bufferSize);
	
	WSACleanup();
	
	printf("END");
	
	return 0;
}

함수의 호출 과정은 socketsetsockoptbindrecvfromclosesocket이며, WinSock은 WSAStartupsocketsetsockoptbindrecvfromclosesocketWSACleanup이다.