보내기 측 코드
다운로드 시 참고: Dev-C++로 작성되었음.
/* 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;
}
함수의 호출 과정은 socket
→ setsockopt
→ sendto
→ closesocket
이며, WinSock은 WSAStartup
→ socket
→ setsockopt
→ sendto
→ closesocket
→ WSACleanup
이다.
socket
데이터그램(Datagram)은 전송 단위의 순서와 무관하게 각 송수신 단위가 독립적인 데이터이며 수신자와 송신자 상호간 확인 과정이 없다. (데이터그램이나 UDP나 그 말이 그 말이다.)
UDP 브로드캐스팅을 위한 소켓의 생성은
SOCK_DGRAM
옵션을 사용한다.SOCK_DGRAM
옵션은 프로토콜로IPPROTO_UDP
가 동반되며,SOCK_DGRAM
와IPPROTO_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++로 작성되었음.
/* 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;
}
함수의 호출 과정은 socket
→ setsockopt
→ bind
→ recvfrom
→ closesocket
이며, WinSock은 WSAStartup
→ socket
→ setsockopt
→ bind
→ recvfrom
→ closesocket
→ WSACleanup
이다.
'Programming Language > C&C++' 카테고리의 다른 글
SAFEARRAY의 간단한 사용법 정리 (0) | 2017.08.06 |
---|---|
소켓 통신 #3 - ICMP 클라이언트 구현하기(3) (0) | 2014.11.14 |
윈도우에서 IP 주소 얻는 방법 (0) | 2014.11.13 |
소켓 통신 #3 - ICMP 클라이언트 구현하기(2) (0) | 2014.11.11 |
소켓 통신 #3 - ICMP 클라이언트 구현하기(1) (0) | 2014.11.11 |