요청 메시지인 경우 Type = 8

응답 메시지인 경우 Type = 0

 

 

ICMP 메시지 구성

ping 요청 패킷과 응답 패킷의 시간을 보고 패킷 전송에 소요된 시간을 출력한다.

40번째 줄에서 identifier에 들어갈 ID를 생성하고 있다.

 

 

58번째 줄에서 시그널 알람을 설정

65번째 줄에서 sig_alrm() 실행

sig_alrm() 함수

메시지 센드

121-127 ICMP 패킷 구성 

127 - gettimeofday() 현재 시각을 icmp_date에 할당하고 있다.

 

133에서 원격지로 sendto()로 보내고 있다.

 

107에서 1초마다 시각을 실어서 ICMP 메시지를 계속 보내고 있다.

다시 핑 프로그램으로 돌아와서 

69 - recvfrom()으로 메시지를 받는다.

77 - IP 헤더의 프로토콜 타입이 ICMP인지 확인한다.

80 - 자신이 보낸 메시지인지 확인한다. 

84 - 에코 응답인지 확인한다.

85 - identifier가 내가 보낸 것과 같은지 확인한다.

 

88 - 현재의 시각

89 - ICMP에 들어있는 시각

90 - 88번과 89번에서 잰 시각의 차이를 계산해서 94번에서 출력한다.

 

<ICMP(Internet Control Message Protocol)>

- ICMP는 TCP/IP에서 IP 패킷을 처리할 때 발생되는 문제를 알려주는 프로토콜이다.

- 우리 컴퓨터인 A에서 B컴퓨터의 상태를 보기위해 ping 을 찍으면 ICMP 프로토콜을 보내게 됩니다. 그렇게 되면 A 컴퓨터는 B의 네트워크 상태를 확인할 수 있습니다. 

 

<IGMP(Internet Group Management Protocol)>

멀티캐스팅 데이터의 수신을 위해서 IGMP프로토콜을 사용한다. IGMP는 인터넷 그룹 관리 규약이라고 불린다.

 

아래 사이트에서 정보를 입수 블로그를 확인해보니 네트워크 기초지식을 보기좋게 정리해두었다. 공부용으로 좋은 것 같다.

https://m.blog.naver.com/PostList.nhn?blogId=rbdi3222&categoryNo=14&logCode=0&categoryName=%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC

 

아는만큼 보인다 : 네이버 블로그

개인 복습용 자료들입니다.

blog.naver.com

 

 

https://www.youtube.com/watch?v=tVKp69n5YKk&list=PL7mmuO705dG265DxvC-oZgKViaRGt2-eS&index=34&ab_channel=%ED%94%84%EB%A6%AC%EB%A0%89

 

TCP 패킷 - 4계층에서 사용되며 포트번호를 통해 데이터 전송

 

control bit에 값중 SYN 값이 1이고 ACK 값이 0이면 src에서 dst로 보내는 값을 나타내고

SYN 값과 ACK 값이 둘다 1이면 dst 에서 src로 SYN_ACK 패킷임을 나타낸다.

 

TCP 헤더의 체크섬은 Presudo(슈도) header와 TCP header 정보를 통해 만들어지게 된다.

 

<Raw 소켓으로 패킷 수신>

22~23 번째 줄을 보면 tcp 헤더와 IP 헤더를 설정하고 있는 것을 볼 수 있다.

row 소켓 모드에서 tcp 프로토콜을 사용하겠다는 것을30번째 줄을 통해서 확인할 수 있다.

39번째 줄에있는 recvfrom()메소드를 통해서 데이터를 읽어 드린다.

44~45번째 줄을 보면 tcp 헤더와 IP 헤더를 출력한다.   

 

 

// read_packet.c
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/socket.h>

void print_ip(struct iphdr *);
void print_tcp(struct tcphdr *);

main( )
{
	int	sd;
	int	len;

	int	rx_packet_size = sizeof(struct iphdr) + sizeof(struct tcphdr);
	char *rx_packet = malloc(rx_packet_size);

	struct tcphdr  *rx_tcph;
	struct iphdr   *rx_iph;

	struct in_addr s_addr, d_addr;
	struct sockaddr_in local, remote;

	struct servent *serv;

	if((sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
		printf("socket open error\n");
		exit(-1);
	}

	while(1) {
		bzero(rx_packet, rx_packet_size);

		len = sizeof(local);
		if(recvfrom(sd,rx_packet,rx_packet_size,0x0,(struct sockaddr *)&local, &len)<0) {
			printf("recvfrom error\n");
			exit(-2);
		}

		rx_iph = (struct iphdr *)(rx_packet);
		rx_tcph = (struct tcphdr *)(rx_packet + rx_iph->ihl * 4);

		print_ip(rx_iph);
		print_tcp(rx_tcph);
	}
	
	close(sd);
}

void
print_ip(struct iphdr *iph)
{
	printf("[IP  HEADER] VER: %1u HL : %2u Protocol : %3u ", iph->version, iph->ihl, iph->protocol);
	printf("SRC  IP: %15s ", inet_ntoa(*(struct in_addr *)&iph->saddr));
	printf("DEST IP: %15s \n", inet_ntoa(*(struct in_addr *)&iph->daddr));

}

void
print_tcp(struct tcphdr *tcph)
{
	printf("[TCP HEADER] src port: %5u dest port : %5u  ", ntohs(tcph->source), ntohs(tcph->dest));

	(tcph->urg == 1)?printf("U"):printf("-");
	(tcph->ack == 1)?printf("A"):printf("-");
	(tcph->psh == 1)?printf("P"):printf("-");
	(tcph->rst == 1)?printf("R"):printf("-");
	(tcph->syn == 1)?printf("S"):printf("-");
	(tcph->fin == 1)?printf("F"):printf("-");
	printf("\n\n");
}

 

 

 

IP 헤더를 프린트 하는 함수를 보면 IP 버전, IP 헤더 length, 프로토콜 종류와 도착, 출발 Ip 주소를 출력한다.

TCP 헤더를 프린트 하는 함수를 보면 출발지 포트번호와 도착지 포트 번호를 출력하며 컨트롤 비트들에 관해 출력한다.

 

무차별 모드라면 dst가 본인 아이피가 아니더라도 수집하게 될 것이다.

[IP HEADER] 버전, 헤더길이, src IP, dst IP

[TCP HEADER] src port, dest port, 컨트롤 비트

 

 

14~15번 줄을 보면 자신의 아이피 주소와 포트 번호를 설정하고 있다.

11~12번 줄을 보면 상대 서버의 스캔할 포트번호를 확인할 수 있다. 80~90 사이의 포트번호이다.

35번 째 줄을 통해서 명령어와 도메인 주소를 함깨 입력해야 함을 알 수 있다.

39번째 줄을 보면 도메인 주소를 target에 저장하고 있다. 

48번째 줄을 보면 타켓 도메인 주소의 포트번호 80~90를 스캔하기 위해 반복문을 활용하고 있음을 확인할 수 있다.

scan_syn_port()함수를 살펴보겠다.

113~123번째 줄을 살펴보면 IP 헤더에 필요한 정보를 IP 헤더 구조체에 할당하고 있다. 

123번째 줄은 체크섬을 만들고 있는 것을 확인할 수 있다.

130번째 줄에 sendto를 통해 원격지로 데이터를 전송한다. SYN 메시지를 전송하는 것이다.

 

141번째 줄에서 recvfrom을 통해서 SYN_ACK이 오길 기다린다. 

147번째 줄과 149에서 상대방이 나에게 보낸 SYN_ACK인지 확인한다. 

158번째에서 SYN값과 ACK 값을 확인한다.

 

위 이미지에서 체크섬에 관한 내용도 확인해 볼 수 있다.

 

위 실행 결과를 통해서 80번 포트 정보를 스캔하고있는 2번째 줄을 보면 마지막에 port[80] open/http를 확인할 수 있다.

이 데이터는 해당 포드에서 제공하는 서비스이다. 나머지 포트를 보면 *모양이 나타나있는 것을 확인할 수 있다. 이는 해당 포트는 열려있지 않다는 것이다. 

위 정보는 SYN 패킷을직접 만들고 조정하고 SYN_ACK 패킷을 찾아내고 조사해서 얻은 결과라 할 수 있다.

 

scan_syn_port(unsigned long target, int port)
{
	int	sd;
	int	on = 1;
	int	len;

	int	tx_packet_size = sizeof(struct iphdr) + sizeof(struct tcphdr) + sizeof(struct pseudohdr);
	int	rx_packet_size = sizeof(struct iphdr) + sizeof(struct tcphdr);
	char	*rx_packet = (char *)malloc(rx_packet_size);
	char	*tx_packet = (char *)malloc(tx_packet_size);

	struct tcphdr		*tcph, *rx_tcph;
	struct iphdr		*iph, *rx_iph;
	struct pseudohdr	 *pseudoh;

	struct in_addr		s_addr, d_addr;
	struct sockaddr_in	local, remote;

	struct servent		*serv;

	iph = (struct iphdr *)(tx_packet);
	tcph = (struct tcphdr *)(tx_packet + sizeof(struct iphdr));
	pseudoh = (struct pseudohdr *)(tx_packet + sizeof(struct iphdr) + sizeof(struct tcphdr));
	78 
	if((sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
		printf("socket open error\n"); exit(-1);
	}

	if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {
		printf("set socket option error\n"); exit(-2);
	}

	memset(tx_packet, 0, tx_packet_size);

	d_addr.s_addr = target;
	s_addr.s_addr = inet_addr(LOCAL_IP);

	pseudoh->s_addr = s_addr.s_addr;
	pseudoh->d_addr = d_addr.s_addr;
	pseudoh->protocol = IPPROTO_TCP;
	pseudoh->zero = 0;
	pseudoh->length = htons(sizeof(struct tcphdr));

	tcph->source = htons(LOCAL_PORT);
	tcph->dest = htons(port);
	tcph->seq = htons(random()%time(NULL));
	tcph->ack_seq = 0;
	tcph->doff = 5;
	tcph->res1 = 0;
	tcph->fin = 0;
	tcph->syn = 1;
	tcph->rst = 0;
	tcph->psh = 0;
	tcph->ack = 0;
	tcph->urg = 0;
	tcph->window = htons(1024);
	tcph->check = (unsigned short)cksum_in((unsigned short *)tcph, (sizeof(struct tcphdr) + sizeof(struct pseudohdr)));

	iph->ihl = 5;
	iph->version = 4;
	iph->tos = 0;
	iph->tot_len = htons(tx_packet_size) - sizeof(struct pseudohdr);
	iph->id = 0;
	iph->frag_off = 0;
	iph->ttl = IPDEFTTL;
	iph->protocol = IPPROTO_TCP;
	iph->saddr = s_addr.s_addr;
	iph->daddr = d_addr.s_addr;
	iph->check = (unsigned short)cksum_in((unsigned short *)iph, sizeof(struct iphdr));

	remote.sin_family = PF_INET;
	remote.sin_addr   = d_addr;
	remote.sin_port   = htons(port);
	remote.sin_port   = 0;

		if(sendto(sd, tx_packet, (tx_packet_size - sizeof(struct pseudohdr)), 0x0, (struct sockaddr *)&remote, sizeof(remote)) < 0) {
			printf("send error\n"); exit(-3);
		}
		printf("[tx] %u->%u ", ntohs(tcph->source), ntohs(tcph->dest));
		(tcph->urg == 1)?printf("U"):printf("-");
		(tcph->ack == 1)?printf("A"):printf("-");
		(tcph->psh == 1)?printf("P"):printf("-");
		(tcph->rst == 1)?printf("R"):printf("-");
		(tcph->syn == 1)?printf("S"):printf("-");
		(tcph->fin == 1)?printf("F"):printf("-");
 
	while(recvfrom(sd, rx_packet, rx_packet_size, 0x0, (struct sockaddr *)&local, &len) >0 ) {
	
			rx_iph = (struct iphdr *)(rx_packet);
			rx_tcph = (struct tcphdr *)(rx_packet + rx_iph->ihl * 4);
	
	
			if( rx_iph->saddr != iph->daddr) continue;
	
			if((ntohs(tcph->source) == ntohs(rx_tcph->dest)) && (ntohs(tcph->dest) == ntohs(rx_tcph->source))){
				printf("[rx] %u->%u  ", ntohs(rx_tcph->source), ntohs(rx_tcph->dest));
				(rx_tcph->urg == 1)?printf("U"):printf("-");
				(rx_tcph->ack == 1)?printf("A"):printf("-");
				(rx_tcph->psh == 1)?printf("P"):printf("-");
				(rx_tcph->rst == 1)?printf("R"):printf("-");
				(rx_tcph->syn == 1)?printf("S"):printf("-");
				(rx_tcph->fin == 1)?printf("F"):printf("-");
 
				if(rx_tcph->syn == 1 && rx_tcph->ack == 1) {
					serv = getservbyport(htons(port), "tcp");
					printf(" port[%d] open/%s \n", ntohs(rx_tcph->source), serv->s_name);
				} else if (rx_tcph->rst == 1) {
					printf(" *\n");
				} else {
					printf("protocol error\n"); exit(-1);
				}
	
				break;
			}
		}
		
		close(sd);
	} 
		
	unsigned short
	cksum_in(unsigned short *addr, int len)
	{	
		unsigned long sum = 0;
		unsigned short answer = 0;
		unsigned short *w = addr;
		int	 nleft = len;
		
		while(nleft > 1) {
			sum += *w++;
			if(sum & 0x80000000)
				sum = (sum & 0xffff) + (sum >> 16);
				nleft -= 2;
		}
		
		if(nleft == 1) {
			*(unsigned char *)(&answer) = *(unsigned char *)w;
			sum += answer;
		}
		
		while(sum >> 16)
			sum = (sum & 0xffff) + (sum >> 16);
	
		return(sum==0xffff)?sum:~sum;
	}

 

떳던 오류 관련

https://devks.tistory.com/47

 

Linux. 권한설정 (Permission Denied)

파일을 업로드하는 과정에서 Permission Denied가 발생했습니다. 원인은 Upload 경로에 대한 권한 설정을 하지 않은 것. Linux는 아무래도 익숙하지 않기 때문에 권한 설정을 하면서 기본적인 부분을 정�

devks.tistory.com

su에서 빠저나가는 방법 exit

 

https://storycompiler.tistory.com/44

 

[Ubuntu/Linux] su와 su - 차이점을 정리해보기

사용자 계정으로 로그인한 후 간혹 관리자 root 권한이 필요할 때가 있습니다. 패키지를 설치하거나, 시스템 설정 파일을 수정하거나, 신규유저를 추가하거나, 다른 계정으로 파일권한을 변경하�

storycompiler.tistory.com

---------------------------------------------------------------------------------------

<소스 port_scan_sys.c>
//syn_port_scan.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/socket.h>

#define START_PORT	80
#define END_PORT		90

#define LOCAL_IP		"203.249.39.141"	// 자신의 IP 주소로 수정해야 함
#define LOCAL_PORT	9000				// 발신 포트 번호

unsigned short cksum_in(unsigned short *, int);

struct pseudohdr {
	unsigned long s_addr;
	unsigned long d_addr;
	char	zero;
	unsigned char protocol;
	unsigned short length;
};


main(int argc, char *argv[ ])
{
	unsigned long	target;
	int				portNum;
	struct hostent	*h;

	if(argc < 2) {
		printf("usage : portscan domain_name\n");
		exit(-1);
	}

	if((target = inet_addr(argv[1])) == -1) {
		h = gethostbyname(argv[1]);
		if(!h) {
			printf("gethostbyname error\n");
			return 4;
		}
		target = ((struct in_addr*)h->h_addr)->s_addr;
	}

	for(portNum = START_PORT; portNum <= END_PORT; portNum++) {
		printf("port %d scanning..", portNum);
		scan_syn_port(target, portNum);
	}
}

scan_syn_port(unsigned long target, int port)
{
	int	sd;

	int	on = 1;
	int	len;

	int	tx_packet_size = sizeof(struct iphdr) + sizeof(struct tcphdr) + sizeof(struct pseudohdr);
	int	rx_packet_size = sizeof(struct iphdr) + sizeof(struct tcphdr);
	char	*rx_packet = (char *)malloc(rx_packet_size);
	char	*tx_packet = (char *)malloc(tx_packet_size);

	struct tcphdr		*tcph, *rx_tcph;
	struct iphdr		*iph, *rx_iph;
	struct pseudohdr	 *pseudoh;

	struct in_addr		s_addr, d_addr;
	struct sockaddr_in	local, remote;

	struct servent		*serv;

	iph = (struct iphdr *)(tx_packet);
	tcph = (struct tcphdr *)(tx_packet + sizeof(struct iphdr));
	pseudoh = (struct pseudohdr *)(tx_packet + sizeof(struct iphdr) + sizeof(struct tcphdr));
	78 
	if((sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
		printf("socket open error\n"); exit(-1);
	}

	if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {
		printf("set socket option error\n"); exit(-2);
	}

	memset(tx_packet, 0, tx_packet_size);

	d_addr.s_addr = target;
	s_addr.s_addr = inet_addr(LOCAL_IP);

	pseudoh->s_addr = s_addr.s_addr;
	pseudoh->d_addr = d_addr.s_addr;
	pseudoh->protocol = IPPROTO_TCP;
	pseudoh->zero = 0;
	pseudoh->length = htons(sizeof(struct tcphdr));

	tcph->source = htons(LOCAL_PORT);
	tcph->dest = htons(port);
	tcph->seq = htons(random()%time(NULL));
	tcph->ack_seq = 0;
	tcph->doff = 5;
	tcph->res1 = 0;
	tcph->fin = 0;
	tcph->syn = 1;
	tcph->rst = 0;
	tcph->psh = 0;
	tcph->ack = 0;
	tcph->urg = 0;
	tcph->window = htons(1024);
	tcph->check = (unsigned short)cksum_in((unsigned short *)tcph, (sizeof(struct tcphdr) + sizeof(struct pseudohdr)));

	iph->ihl = 5;
	iph->version = 4;
	iph->tos = 0;
	iph->tot_len = htons(tx_packet_size) - sizeof(struct pseudohdr);
	iph->id = 0;
	iph->frag_off = 0;
	iph->ttl = IPDEFTTL;
	iph->protocol = IPPROTO_TCP;
	iph->saddr = s_addr.s_addr;
	iph->daddr = d_addr.s_addr;
	iph->check = (unsigned short)cksum_in((unsigned short *)iph, sizeof(struct iphdr));

	remote.sin_family = PF_INET;
	remote.sin_addr   = d_addr;
	remote.sin_port   = htons(port);
	remote.sin_port   = 0;

		if(sendto(sd, tx_packet, (tx_packet_size - sizeof(struct pseudohdr)), 0x0, (struct sockaddr *)&remote, sizeof(remote)) < 0) {
			printf("send error\n"); exit(-3);
		}
		printf("[tx] %u->%u ", ntohs(tcph->source), ntohs(tcph->dest));
		(tcph->urg == 1)?printf("U"):printf("-");
		(tcph->ack == 1)?printf("A"):printf("-");
		(tcph->psh == 1)?printf("P"):printf("-");
		(tcph->rst == 1)?printf("R"):printf("-");
		(tcph->syn == 1)?printf("S"):printf("-");
		(tcph->fin == 1)?printf("F"):printf("-");
 
	while(recvfrom(sd, rx_packet, rx_packet_size, 0x0, (struct sockaddr *)&local, &len) >0 ) {
	
			rx_iph = (struct iphdr *)(rx_packet);
			rx_tcph = (struct tcphdr *)(rx_packet + rx_iph->ihl * 4);
	
	
			if( rx_iph->saddr != iph->daddr) continue;
	
			if((ntohs(tcph->source) == ntohs(rx_tcph->dest)) && (ntohs(tcph->dest) == ntohs(rx_tcph->source))){
				printf("[rx] %u->%u  ", ntohs(rx_tcph->source), ntohs(rx_tcph->dest));
				(rx_tcph->urg == 1)?printf("U"):printf("-");
				(rx_tcph->ack == 1)?printf("A"):printf("-");
				(rx_tcph->psh == 1)?printf("P"):printf("-");
				(rx_tcph->rst == 1)?printf("R"):printf("-");
				(rx_tcph->syn == 1)?printf("S"):printf("-");
				(rx_tcph->fin == 1)?printf("F"):printf("-");
 
				if(rx_tcph->syn == 1 && rx_tcph->ack == 1) {
					serv = getservbyport(htons(port), "tcp");
					printf(" port[%d] open/%s \n", ntohs(rx_tcph->source), serv->s_name);
				} else if (rx_tcph->rst == 1) {
					printf(" *\n");
				} else {
					printf("protocol error\n"); exit(-1);
				}
	
				break;
			}
		}
		
		close(sd);
	} 
		
	unsigned short
	cksum_in(unsigned short *addr, int len)
	{	
		unsigned long sum = 0;
		unsigned short answer = 0;
		unsigned short *w = addr;
		int	 nleft = len;
		
		while(nleft > 1) {
			sum += *w++;
			if(sum & 0x80000000)
				sum = (sum & 0xffff) + (sum >> 16);
				nleft -= 2;
		}
		
		if(nleft == 1) {
			*(unsigned char *)(&answer) = *(unsigned char *)w;
			sum += answer;
		}
		
		while(sum >> 16)
			sum = (sum & 0xffff) + (sum >> 16);
	
		return(sum==0xffff)?sum:~sum;
	}
</소스>

 

<윈폼 재일 위에 보더 제거>

 

FormBoderStyle을 None으로 두면 

위에 보더가 제거된다.

 

<윈폼 생성 위치 설정>

 

이번에는 윈폼 생성 위치를 조정하겠다.

아래에 보면 속성 첫번째에 Form1 이 설정되어있다. 

그리고 Start라 치면 StartPosition이 나오게 된다. 이를 통해서 가운데에서 윈폼이 생성 되게끔 만들 수 있다.

 

<전체화면 설정하기>

속성창에 state라 검색하면 WindowState가 검색된다 Normal 상태를 Maximized 상태로 변경하면 된다.

 

<devexpress item 색상 변경>

 

변경을 원하는 아이템을 선택 후 back을 치면 AppearanceItem이 검색된다

그 자식 객체에 Normal을 클릭한다.

 

여기서 backColor와 BorderColor를 원하는 색상으로 변경해주어야 한다.

BorderColor는 테두리 색상이다.

 

 

<사용자 정의 객체 사용하기>

프로젝트 우클릭 -> 추가 -> 구성요소 클릭  

 

<사용자 정의 check box >

 

위와 같이 한뒤 

아래 영상을 보도록 하자

사용자 정의 클래스 정의가 끝나고 빌드를 하면 도구 상자에서 사용자 정의 클래스가 있는 것을 확인할 수 있다

드래그 & 드랍으로 사용하도록 하자 아래 코드를 이용하던 중

 

이 부분에서 오류가 발생하였었다. 오류 해결 방법은 몇글자만 치고 컴퓨터가 추천해주는 라이브러리를 선택하면서 천천히 하면 오류가 생기기지 않는다.

 

 

https://m.blog.naver.com/PostView.nhn?blogId=goldrushing&logNo=130143350386&proxyReferer=https:%2F%2Fwww.google.co.kr%2F

 

[C#]Large CustomCheckBox(체크박스 크게 만들기)

눈어두운 사람을 위한 체크박스 크게 만들기 위 Custom Check Box를 만드는 방법입니다. 소스 코드는 아...

blog.naver.com

 

<모서리가 동그란 버튼>

 

아래 영상을 참조

https://www.youtube.com/watch?v=n5jDXRDg070&ab_channel=thenewbostonthenewboston

 

 

<나가기 버튼>

1. Tool box에서 버튼 드래그엔드랍

2. border 키워드로 BorderSize를 찾은 후 크기를 0으로 변경한다. 

3. flat 키워드로 FlatStyle을 Flat으로 변경하여 준다. 

 

보통 여기서 버튼 색상이 투명해지던데 나는 투명해지지 않았다.

만약 투명해지지 않은 사람은 4번의 과정을 실행하고 그렇지 않으면 할 필요 없다.

4. BackColor 색상을 색상 추출 프로그램을 통해 배경 색상을 추출한뒤 색상을 맞춘다.

 

5. image를 검색한다.  image에 free icon을 검색하여 나오는 사이트에서 exit를 검색하여 나가기 아이콘 16x16 사이즈로

다운후 이미지에 넣는다.

 

 

 

 

 

 

 

 

raw 뜻 : 디지털 분야에서 RAW는 '날것의'라는 뜻의 raw라는 영어 형용사의 의미가 확장되어 쓰이는 전문 용어이다. 데이터 압축 뿐만 아니라 특정 프로토콜에 맞추기 위해 가공을 거치지 않은 디지털 데이터를 뜻하는 용어이다.

https://namu.wiki/w/RAW(%ED%8C%8C%EC%9D%BC)

 

RAW(파일) - 나무위키

RAW 이미지 및 영상 파일에는 이미지 프로세서에서 최소한의 후처리만 거친 색상 데이터와 촬영 당시에 ISO 감도, 조리개값, 노출 시간, 렌즈 정보 등이 포함된 메타데이터가 들어있다. 또한, RAW ��

namu.wiki

 

일반 소켓 같은 경우 write(socket, buffer, stream) 이런 식으로 데이터 크기정도만 조작할 수 있다. 하지만 raw 소켓 같은 경우는 응용 프로그램에서 직접 프로토콜 헤더의 전부나 일부를 생성하거나 읽고 쓸 수 있다.

 

raw 소켓을 사용하는 이유는 새로운 프로토콜을 생성하거나 소켓을 새밀하게 조작하기 위해서이다. 

 

 

 

특징 요약

- 세부적인 조작

- 해킹 등에 사용되기 때문에 오해 받지 않도록 세심한 주의 요청

- 신규 프로토콜 설계시 유용

 

Raw 소켓의 생성

#include <netinet/in.h>

 

int sd;

sd = socket(AF_INET, SOCK_RAW, protocol);

 

 

지금 까지는 tcp헤더를 조작하는 방법을 살펴봤다.

이번에는 IP 헤더를 조작하기 위한 방법이다. IP 헤더를 조작하기 위해서는 IP_HDRINCL 값을 on 상태로 두어야 된다.

 

 

promiscuous -> 무차별 

 

Promiscuous Mode 로 설정된 경우 자신의 맥주소와 다른 소켓도 가져오게 된다. lan에 흘러다니는 모든 소켓을 카피해버린다. 

 

LAN 카드는 맥주소를 보게된다. 자신의 맥주소랑 같은 것을 카피한다. 이때 소켓이 일반 소켓이면 IP 모듈로 보내게 된다.

IP 모듈은 패킷 헤더를 열어서 IP 주소를 확인하게 된다. 자신의 IP와 같다면 그 모듈을 TCP 모듈이나 UDP 모듈로 전송한다.

UDP 모듈 또는 TCP 모듈은 포트를 확인하게 된다. 포트를 통해 무슨 서비스를 이용하는지 확인 가능하며 일반 소켓으로 올라가게 된다. 

이때 raw 소켓을 사용하게 되면 IP 모듈 TCP, UDP 모듈 소켓 모듈을 거치지 않고 모조리 raw 소켓으로 바로 올려버린다.

 

raw 소켓은 자신의 맥주소와 상관없이 소켓을 다 모니터링 해볼 수 있다.

 

 

 

리눅스에서 ifconfig eth0 promisc 명령어를 통해서 무차별 모드를 활성화 할 수 있다.

여기서 et0 는 랜카드 번호이다. 

 

무차별 모드로 설정된 예제가 있다. 일단 일반 소켓 영역에서 말하자면 아래 그림을 보면 NIC(Network Interface Card) 에서 자신의 MAC주소와 같은 

 

 

SYN 메시지를 주고밭는 이유는 서버 포트가 열려 있는지 판단하기 위해서이다. 

https://www.youtube.com/watch?v=p3giFcDeCjE&list=PL7mmuO705dG265DxvC-oZgKViaRGt2-eS&ab_channel=%ED%94%84%EB%A6%AC%EB%A0%89

 

이번 글은 2개의 다른 마커 위에 각각 다른 가상화 객체를 만드는 방법을 다루었다. 기본적으로 nreal glasses에 대한 이해가 조금 필요하다.

 

1. NRCameraRig, NRInput 을 Hierarchy 창에 드래그&드랍

2. NRInput에서 Override Camera Center 부분을 NRCameraRig의 CenterCamera 드래그&드랍

 

3. 캔버스 추가 후 이벤트 시스템 제거

그리고 캔버스에서 Graphic Raycaster를 제거하고 Canvas Raycast Target을 추가한다.

캔버스에 버튼 등 인터렉션이 필요없으면 할필요 없다.

캔버스는 월드스페이스로 하고 nreal에서 제공하는 move with camera 스크립트를 컴포넌트로 주면 사용자 화면에 고정되게 된다. 거리 조정은 유니티 내에 실행하면서 해주어야 할 것이다. 

 

 

4. empty gameobject 추가후 Tracking Image Visualizer 이름의 속성으로 추가

 

Tracking Image Example Controller를 컴포넌트로 가져온다.

Text : 이미지의 인덱스를 화면에서 확인하기 위한 객체

tracking Image Visualize에서 위에는 엔진 아래는 자동차

Fit To Scan Overlay는 이미지를 탐색할 때 나오는 텍스트이다.

 

위 컨트롤러는 글쓴이가 수정한 코드이다. 월래라면 등록된 이미지가 탐지되면 그위에 가상화할 객체를 하나만 보여주는 것이지만 글쓴이는  2개의 이미지에 각각 서로 다른 객체를 가상화 하고 싶었다. 

아래는 수정된 코드이다. 

namespace NRKernal.NRExamples
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using UnityEngine;
    using UnityEngine.UI;


    /// <summary>
    /// Controller for TrackingImage example.
    /// </summary>
    [HelpURL("https://developer.nreal.ai/develop/unity/image-tracking")]
    public class TrackingImageExampleController : MonoBehaviour
    {


        public Text text;

        // A prefab for visualizing an TrackingImage.
        public TrackingImageVisualizer TrackingImageVisualizerPrefab_1;
        public TrackingImageVisualizer TrackingImageVisualizerPrefab_2;

        // The overlay containing the fit to scan user guide.
        public GameObject FitToScanOverlay;

        private Dictionary<int, TrackingImageVisualizer> m_Visualizers
            = new Dictionary<int, TrackingImageVisualizer>();

        private List<NRTrackableImage> m_TempTrackingImages = new List<NRTrackableImage>();

        public void Update()
        {
#if !UNITY_EDITOR
            // Check that motion tracking is tracking.
            if (NRFrame.SessionStatus != SessionState.Running)
            {
                return;
            }
#endif
            // Get updated augmented images for this frame.
            NRFrame.GetTrackables<NRTrackableImage>(m_TempTrackingImages, NRTrackableQueryFilter.New);

            // Create visualizers and anchors for updated augmented images that are tracking and do not previously
            // have a visualizer. Remove visualizers for stopped images.
            foreach (var image in m_TempTrackingImages)
            {
/*                UnityEngine.Debug.Log("image.GetDataBaseIndex() : " + image.GetDataBaseIndex());
                UnityEngine.Debug.Log("image.GetTrackableType() : " + image.GetTrackableType());
                UnityEngine.Debug.Log("image.GetTrackingState() : " + image.GetTrackingState());*/

                TrackingImageVisualizer visualizer = null;
                m_Visualizers.TryGetValue(image.GetDataBaseIndex(), out visualizer);
                if (image.GetTrackingState() == TrackingState.Tracking && visualizer == null)
                {
                    NRDebugger.Log("Create new TrackingImageVisualizer!");
                    // Create an anchor to ensure that NRSDK keeps tracking this augmented image.
                    
                    try
                    {
                        text.text = "Image Index : " + image.GetDataBaseIndex();
                        if (image.GetDataBaseIndex() == 4)
                        {
                            visualizer = (TrackingImageVisualizer)Instantiate(TrackingImageVisualizerPrefab_1, image.GetCenterPose().position, image.GetCenterPose().rotation);
                        }
                        else 
                        {
                            visualizer = (TrackingImageVisualizer)Instantiate(TrackingImageVisualizerPrefab_2, image.GetCenterPose().position, image.GetCenterPose().rotation);
                        }
                    }
                    catch(NullReferenceException e) {
                     }
                    visualizer.Image = image;
                    visualizer.transform.parent = transform;
                    m_Visualizers.Add(image.GetDataBaseIndex(), visualizer);
                }
                else if (image.GetTrackingState() == TrackingState.Stopped && visualizer != null)
                {
                    m_Visualizers.Remove(image.GetDataBaseIndex());
                    Destroy(visualizer.gameObject);
                }

                FitToScanOverlay.SetActive(false);
            }

        }

        public void EnableImageTracking()
        {
            var config = NRSessionManager.Instance.NRSessionBehaviour.SessionConfig;
            config.ImageTrackingMode = TrackableImageFindingMode.ENABLE;
            NRSessionManager.Instance.SetConfiguration(config);
        }

        public void DisableImageTracking()
        {
            var config = NRSessionManager.Instance.NRSessionBehaviour.SessionConfig;
            config.ImageTrackingMode = TrackableImageFindingMode.DISABLE;
            NRSessionManager.Instance.SetConfiguration(config);
        }
    }
}

 

여기서 추가한 핵심 코드는 image.GetDataBaseIndex() 코드이다. 

GetDataBaseIndex() 코드는 데이터베이스에 등록된 이미지의 인덱스 번호를 반환하는 메소드이다.

여기서 주의할 점이 있다. 유니티 내부에서 실행했을 때 이미지의 인덱스 번호와 AR 안경을 착용하고 봤을 때 이미지의 인덱스 번호가 다르다는 점이다 . 따라서 ar 안경을 착용하고 이미지의 인덱스 번호를 확인하기 위해 유니티 캔버스에 Text를 이용하여 직접 인덱스 번호를 GetDataBaseIndex를 사용하여 확인하길 바란다.

 

5. 트래킹 이미지 벌추얼라이저 만들기

위 이미지 처럼 엠티 오브젝트에 추적된 이미지 위에 가상화할 객체를 자식으로 넣어준다. 그리고 엠티 오브젝트에 컴포넌트로 

를 넣어주어야 한다. Axis 값은 자식으로 주었던 객체를 넣어주면 된다.

글쓴이는 예제 코드를 조금 수정하였다.

 

namespace NRKernal.NRExamples
{
    using UnityEngine;

    /// <summary>
    /// Uses 4 frame corner objects to visualize an TrackingImage.
    /// </summary>
    public class TrackingImageVisualizer : MonoBehaviour
    {
        // The TrackingImage to visualize.
        public NRTrackableImage Image;

/*        // A model for the lower left corner of the frame to place when an image is detected.
        public GameObject FrameLowerLeft;

        // A model for the lower right corner of the frame to place when an image is detected.
        public GameObject FrameLowerRight;

        /// A model for the upper left corner of the frame to place when an image is detected.
        public GameObject FrameUpperLeft;

        // A model for the upper right corner of the frame to place when an image is detected.
        public GameObject FrameUpperRight;*/

        public GameObject Axis;

        public void Update()
        {
            if (Image == null || Image.GetTrackingState() != TrackingState.Tracking)
            {
/*                FrameLowerLeft.SetActive(false);
                FrameLowerRight.SetActive(false);
                FrameUpperLeft.SetActive(false);
                FrameUpperRight.SetActive(false);*/
                Axis.SetActive(false);
                return;
            }

/*            float halfWidth = Image.ExtentX / 2;
            float halfHeight = Image.ExtentZ / 2;*/
/*            FrameLowerLeft.transform.localPosition = (halfWidth * Vector3.left) + (halfHeight * Vector3.back);
            FrameLowerRight.transform.localPosition = (halfWidth * Vector3.right) + (halfHeight * Vector3.back);
            FrameUpperLeft.transform.localPosition = (halfWidth * Vector3.left) + (halfHeight * Vector3.forward);
            FrameUpperRight.transform.localPosition = (halfWidth * Vector3.right) + (halfHeight * Vector3.forward);*/

            var center = Image.GetCenterPose();
            transform.position = center.position;
            transform.rotation = center.rotation;

/*            FrameLowerLeft.SetActive(true);
            FrameLowerRight.SetActive(true);
            FrameUpperLeft.SetActive(true);
            FrameUpperRight.SetActive(true);*/
            Axis.SetActive(true);
        }
    }
}

 글쓴이는 모서리 부분을 알 필요없이 이미지 중앙에다가 객체만 띠우고 싶어서 위와 같이 소스코드를 수정하였다. 

그러면 원하는 객체만 이미지 위에 나타나게 된다. 

 

 

그외 글쓴이는 엔진을 가상화 하였는데 그림자 때문에 엔진이 잘 보이지 않았다 그래서 아래 블로그를 참조 하였다.

<그림자 지우기>

https://mingyu0403.tistory.com/56

 

[Unity] 그림자 지우기

ㅇ 그림자가 그려지지 않게 하기 Cast Shadows - 그림자가 그려질 것인가 Receive Shadows - 다른 물체의 그림자가 나한테 그려질 것인가

mingyu0403.tistory.com

 

그리고 엔진이 나타나면서 좀더 집중력을 올리는 요소를 넣고자 소리도 아래와 같은 컴포넌트를 엔진 객체에 넣어주어 봤다.

Play On Awake에 체크 되어있으면 객체가 생성될 때 오디오가 재생된다. Loop를 하게 되면 객체가 일단 지워저도 계속 실행되는 것을 관찰하여 일단 Loop는 선택하지 않았다.

알아야 하는 것들

nm = nanometre

1nm = 10^-9m

 

기본 지식으로 참고한 영상

https://brunch.co.kr/@mascott/18

 

광케이블(Optical Fiber Cable)의 종류

광케이블의 세계로... | ◎ 광케이블(Optical Cable) ▸ 광신호로 변환된 레이저 신호를 멀리까지 전송하기 위해서 광섬유로 만들어진 전송 케이블 ▸ 광케이블은 구리선을 사용하는 통신방식보다

brunch.co.kr

광 케이블 모드

1. 싱글 모드

- 광케이블 코어 안에 토파되는 빛의 모드가 하나인 광섬유

- 장거리 고속 전송에 유리하다. (현재 신규로 구축되는 광선로에서는 single mode 케이블만 대부분 사용한다) 

2. 멀티 모드

- 광케이블 코어안에 3개의 빛이 사용되는 모드고 코어의 직경이 크다.

- 단거리 전송에 유리

 

 

 

 

참고 영상 

https://www.youtube.com/watch?v=cKCst1wccfo

 

SFP는 Optical Signal(광 신호) 를 Electrical Signal(전기 신호)로 변환해주는 역할을 한다.

SFP는 종류가 여러가지인데 종류는 wavelength(주파수) 와 거리 그리고 싱글 모드인지 멀티 모드인지에 따라 달라진다.

 

 

 

짧은 거에서는 1000BASE-T 그리고 1000BASE-SX SFP가 사용되는 것 같다. 1000BASE-T는 SFP가 붙어있지 않으니 광 케이블을 사용하지 않는 것 같다.

 

duplex : 이중

 

SX short-range

LX long-range 10km

EX 40km 

ZX 80km 

 

bidirectional : 양방향

BiDi SFP는 항상 같은 주파수를 가진 transceiver를 사용해야 한다.

 

type distance  wavelength mode
1000BASE-T 100M(Cat5)    
1000BASE-SX 550M 850nm multi
1000BASE-LX SFP 10KM 1310nm single, duplex LC cables
1000BASE-EX SFP 40KM   single, duplex LC cables
1000BASE-ZX SFP 80KM   single, duplex LC cables
BiDi SFP(one fiber)      

 

전송 속도에 따른 SFP 분류

종류 전송 속도
SFP 1~2.5Gbps
SFP+ 10Gbps
SFP28 25Gbps
QSFP+ 40Gbps
CFP 100Gbps

QSFP+ 는 전송속도가 40기가나 되니 크기도 크다.

 

 

<WDM>

저비용에 네트워크 capacity의 증가에 따라 WDM이라는 기술이 탄생

 

WDM 에는 CWDM SFPs 그리고 DWDM SFPs가 있다.

 

CWDM은 80KM 까지 지원 및 1270nm ~ 1610nm 주파수 대역을 사용한다.

 

DWDM 80~200KM 까지 가능하며 주파수폭이 작아지고 수가 많아졌다.

 

우분투를 통해 통신하는 프로그램을 살펴보던 중 이런 화면을 보게 되었다.

정보처리기사 자격증을 공부하던 중 한번 본 내용(ARP)이었지만 암기식으로 학습해서인지 생각이 나질 않아서 블로그 검색을 해야 내용을 이해하게 되었다.

 

ARP 프로토콜은 간단하게 말하자면 IP를 통해 MAC address를 알아내는 것이다. 

구체적으로 ARP 프로토콜에 대해 말해보겠다.

위 화면 내용중 아래와 같은 내용이 있다.

  Destination: Broadcast (ff:ff:ff:ff:ff:ff)

      Address: Broadcasst (ff:ff:ff:ff:ff:ff)

이는 src 단말 장치에서 망에 연결된 모든 단말 장치에게  연락을 하는 것이다. 

src 단말 장치에서 무슨 목적으로 브로드캐스트 메시지를 보내는가는 아래를 보면 알 수 있다.

Address Resolution Protocol (request/gratuitous ARP)

즉 IP 주소로 메시지를 보내기 위해 MAC 주소를 알아내기 위함이다.

그리고 자세히 보면 괄호에 gratuitous ARP 라는 구문이 있다.

gratuitous라는 의미는 쓸데없는 라는 의미가 있다. 

위 이미지를 다시 살펴보면 sender IP와 target IP가 같음을 알 수 있다. 따라서 MAC 주소를 확인할 필요가 없는

쓸데없는 ARP 라는 것이다. 그럼에도 이는 의미가 있는데 3가지 용도가 있다.

 

1) IP 주소 충돌 감지 -> 자신의 IP주소를 타깃으로 하여 ARP 요청을 보냈는데 만약 다른 호스트에서 이에 대한 응답이 있다면 IP 충돌을 감지한다.

2) ARP Table 갱신 -> 자신의 IP를 가지고있는 단말 장치가 있다면 MAC 주소를 갱신한다.

3) VRRP/HSRP -> VRRP/HSRP가 enable된 라우터 중 Master에 해당하는 라우터에 해당하는 자신과 연결된 L2 switch의 Mac address table 정보를 갱신(해당 MAC 주소에 대한 Port 정보 갱신) 하기 위해 GARP를 사용한다.

 

참조

https://www.netmanias.com/ko/post/blog/5402/arp-ethernet-ip-ip-routing-network-protocol/arp-and-garp-gratuitous-arp

 

ARP와 GARP(Gratuitous ARP)

IPoE(IP over Ethernet) 환경에서(현재 우리 모두가 사용하고 있는 환경이죠... L3 = IP, L2 = Ethernet) 상대방 호스트(PC/서버) 혹은 라우터의 MAC 주소를 알기 위해, 해당 노드(호스트/라우터)의 IP 주소를 타��

www.netmanias.com

https://blockdmask.tistory.com/189

 

[데통] ARP protocol 에 대해서

안녕하세요. BlockDMask입니다. 오늘은 ARP 프로토콜에 대해 알아보겠습니다. > ARP 프로토콜 이란? ARP Protocol (Address Resolution Protocol - 주소 결정 프로토콜) - 네트워크 상에서 IP 주소를 물리적 네..

blockdmask.tistory.com

 

 

기본적으로 이런 식으로 세팅하면 된다고 한다.

인터넷 검색 결과 Configuration에서 Scripting Runtime Version을 찾을 수 있다고 한다. 하지만 없었는데 상위 버전에선Api Compatibility Level로 변경되었다 한다. 

 

'NRealGlass' 카테고리의 다른 글

[NRealGlass] Video capture  (0) 2020.11.19
NrealGlass 개발환경 세팅  (0) 2020.10.30
2개의 다른 마커에 다른 가상화 객체  (0) 2020.09.04

c언어에서는 기본적으로 1,2,4,8바이트 순으로 2의 지수가 증가하는 흐름으로 값을 저장할 수 있는 변수가 있습니다.

하지만 필요에 따라 3바이트 6바이트 짜리 데이터가 필요할 때도 있습니다.

그래서 이번에는 3바이트, 6바이트 데이터를 만드는 것을 해보도록 하겠습니다.

 

https://dojang.io/mod/page/view.php?id=653

 

C 언어 코딩 도장: 75.3 여러 줄을 묶어서 매크로로 정의하기

지금까지 숫자 한 개나 코드 한 줄을 매크로로 정의해서 사용했습니다. 그럼 여러 줄을 매크로로 만들 수 있지 않을까요? #define은 줄바꿈이 일어날 때 \를 사용하여 여러 줄을 매크로로 만들 수 �

dojang.io

우선 제 코드를 이해하기 위한 기본적인 지식입니다.

매크로를 여러줄 사용하기 위해서 \를 줄 끝에 가져가면 다음줄에도 매크로 정의를 이어서 사용할 수 있습니다. 마지막 줄엔 하지 않아도 됩니다.

 

<3바이트 값>

우선 integer로 값을 입력 받은후 3바이트 값에 값을 넣어보도록 하겠습니다. 

저는 3바이트 값을 uint8_t arr[3] 와 같은 식으로 구현하였습니다.

uint8_t를 모르신다면 char와 같다 생각하시면 됩니다.

여기서 static inline은 함수 속도를 아주 조금 빠르게 만드는데

네트워크에서 몇초에 수천 수만번 사용되는 함수라면 그 조금의 차이도 크기 때문에 사용합니다.

static inline void
intTo3bytes(int num, uint8_t* arr)
{
	arr[0] = (uint8_t)(num & 0x000000ff);
	arr[1] = (uint8_t)((num & 0x0000ff00) >> 8);
	arr[2] = (uint8_t)((num & 0x00ff0000) >> 16);
}

num은 3바이트에 넣을 값 그리고 arr는 3바이트 배열입니다.

4바이트로 값을 입력 받지만 가장 높은 1바이트 쪽 데이터는 버려지게 됩니다.

논리 연산자를 이용해서 각각 바이트 값을 추출하여 배열에 담는 식으로 3바이트 값에 값을 저장합니다.

 

하지만 위와 같이 3바이트 값을 저장하면 읽을 때는 읽는 방법또한 구현해 주어야합니다.

다음은 3바이트를 인티저로 바꿔서 읽는 방법을 구현한 매크로입니다.

 

#define bytes3ToInt(n1, n2, n3) ((uint32_t)(n3 & 0xff) << 16) | \
				((uint32_t)(n2 & 0xff) << 8)  | \
				((uint32_t)(n1 & 0xff)) 

위와 같이 배열에 저장된 순서에 따라 값의 순서를 8바이트 부터 그리고 16바이트부터 준 다음 논리 연산자로 합치는 매크로를 통해서 3바이트 값을 정수처럼 읽을 수 있도록 만들어 보았습니다.

 

그리고 이번에는 6바이트 입니다 3바이트를 만들 수 있으면 6바이트는 거져먹기죠

 

#define bytes6ToInt(n1, n2, n3, n4, n5, n6) ((uint64_t)(n6 & 0xff) << 48)  | \
						((uint64_t)(n5 & 0xff) << 32)  | \
						(uint64_t)(n4 & 0xff) << 24)   | \
						((uint64_t)(n3 & 0xff) << 16)  | \
						((uint64_t)(n2 & 0xff) << 8)   | \
						((uint64_t)(n1 & 0xff)) 

 

static inline void
longTo6bytes(long long num, uint8_t* arr)
{
	arr[0] = (uint8_t)(num & 0x00000000000000ff);
	arr[1] = (uint8_t)((num & 0x000000000000ff00) >> 8);
	arr[2] = (uint8_t)((num & 0x0000000000ff0000) >> 16);
	arr[3] = (uint8_t)((num & 0x00000000ff000000) >> 24);
	arr[4] = (uint8_t)((num & 0x000000ff00000000) >> 32);
	arr[5] = (uint8_t)((num & 0x0000ff0000000000) >> 40);
}

매크로로 만들고 보니 실제 배열 하나하나 저렇게 값을 넣는건 불편하니 매크로를 활용한 함수를 사용하는게 좋을 것 같아서 추가적으로 소개하고 끝내겠습니다.

 

static inline uint64_t
bytes6Tolong(uint8_t * arr)
{
	uint64_t num = _bytes6ToInt(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]);
	return num;
}

static inline uint32_t
bytes3ToInt(uint8_t* arr)
{
	uint32_t num = _bytes3ToInt(arr[0], arr[1], arr[2]);
	return num;
}

 

 

이번 기회에 패킷을 네트워크로 보낼 때 사용하는 htonl에 대해 이야기하려 합니다.

여러분도 알듯이 이는 엔디안 타입을 빅 엔디안에서 리틀 엔디안으로 바꾸는 함수입니다.

htonl 나 htons 함수를 사용하려면 ws2_32.lib 함수가 필요합니다.

헤더 부분에 

#include <winsock2.h>
#pragma comment(lib, "ws2_32")

추가 해주시면 됩니다. 

 

winsock 헤더 파일에 htons(2 bytes)나 htonl(4 bytes) 이 포함돼있는 것으로 알고 있습니다. 

하지만 저는 8바이트 데이터도 보내야 되는 상황이였는데 지원하지 않는군요

그래서 직접 만들어 보았습니다.

 

uint64_t hton64(uint64_t num)
{
	uint8_t arr[8];
	uint8_t temp[8];
	uint64_t after=0;
	arr[0] = (uint8_t)(num & 0x00000000000000ff);
	arr[1] = (uint8_t)((num & 0x000000000000ff00) >> 8);
	arr[2] = (uint8_t)((num & 0x0000000000ff0000) >> 16);
	arr[3] = (uint8_t)((num & 0x00000000ff000000) >> 24);
	arr[4] = (uint8_t)((num & 0x000000ff00000000) >> 32);
	arr[5] = (uint8_t)((num & 0x0000ff0000000000) >> 40);
	arr[6] = (uint8_t)((num & 0x00ff000000000000) >> 48);
	arr[7] = (uint8_t)((num & 0xff00000000000000) >> 56);
	int i,k;
	for (i = 0; i < 8; i++)
	{
		temp[i] = arr[i];
	}
	for (i = 0, k=7; i < 8 && k>0; i++, k--)
	{
		arr[i] = temp[k];
	}
	after = ((uint64_t)arr[0]) | (((uint64_t)arr[1]) << 8) | (((uint64_t)arr[2]) << 16)
		| (((uint64_t)arr[3]) << 24) | (((uint64_t)arr[4]) << 32) | (((uint64_t)arr[5]) << 40)
		| (((uint64_t)arr[6]) << 48) | (((uint64_t)arr[7]) << 56);
	return after;


}

이런 식으로 8바이트 데이터도 엔디안 타입을 변경 할 수 있도록 하였습니다.

1바이트 8개 인수 배열에 값을 담고 배열 순서를 뒤집은 다음 다시 저장하고

그 값을 논리 연산자로 합 처서 8바이트로 다시 만드는 함수입니다.

 

이 함수를 제작한 뒤 이번엔 ntoh64 함수를 만들려는 중 의문이 들었습니다.

빅 엔디안에서 리틀 엔디안으로 가는 함수나 리틀 엔디안에서 빅 엔디안으로 가는 함수는 읽는 순서가 바뀌는 것이기 때문에 구분할 필요 없이 그냥 순서만 바꿔주면 되지 않을까라는 생각이 들었습니다. 

그래서 ntoh64 함수는 아래와 같이 만들어 봤습니다.

uint64_t ntonh64(uint64_t num)
{
	uint8_t arr[8];
	uint8_t temp[8];
	uint64_t after = 0;
	arr[0] = (uint8_t)(num & 0x00000000000000ff);
	arr[1] = (uint8_t)((num & 0x000000000000ff00) >> 8);
	arr[2] = (uint8_t)((num & 0x0000000000ff0000) >> 16);
	arr[3] = (uint8_t)((num & 0x00000000ff000000) >> 24);
	arr[4] = (uint8_t)((num & 0x000000ff00000000) >> 32);
	arr[5] = (uint8_t)((num & 0x0000ff0000000000) >> 40);
	arr[6] = (uint8_t)((num & 0x00ff000000000000) >> 48);
	arr[7] = (uint8_t)((num & 0xff00000000000000) >> 56);
	int i, k;
	for (i = 0; i < 8; i++)
	{
		temp[i] = arr[i];
	}
	for (i = 0, k = 7; i < 8 && k>0; i++, k--)
	{
		arr[i] = temp[k];
	}
	after = ((uint64_t)arr[0]) | (((uint64_t)arr[1]) << 8) | (((uint64_t)arr[2]) << 16)
		| (((uint64_t)arr[3]) << 24) | (((uint64_t)arr[4]) << 32) | (((uint64_t)arr[5]) << 40)
		| (((uint64_t)arr[6]) << 48) | (((uint64_t)arr[7]) << 56);
	return after;


}

맞습니다. 이름만 바꿨고 기능은 같습니다. 리틀 엔디안에서 빅 엔디안으로 가든 빅 엔디안에서 리틀 엔디안으로 가든 읽는 순서만 반대로 해주면 그만이기 때문에 내용은 바뀔 필요 없이 이름만 바꾸면 될 것 같습니다.  그래서 위와 같이 작성하였습니다.

 

그리고 구글링을 하던중

uint64_t hton64(uint64_t n)
{
	int backV = (int)(n & 0x00000000ffffffff);
	int frontV = (int)((n & 0xffffffff00000000) >> 32);
	uint64_t temp = (((uint64_t)htonl(frontV)) << 32) | ((uint64_t)htonl(backV));
	return temp;
}

 이런 느낌의 함수를 봤었는데 처음에는 그냥 쓰다가 문득 의문이 떠올랐습니다.

8바이트짜리 엔디안 변경 함수가 없으니 4바이트로 쪼갠다음 각각 htonl을 적용하는 함수였습니다.

하지만 이렇게 하면 문제가 생기지 않을까 생각합니다.

엔디안 변경 시 12345678의 값을 87654321로 변경해야 합니다.

하지만 위와 같이 사용하게 되면 43218765 이런 식으로 변경될 것이라 생각합니다.

엔디안 타입을 변경하길 원했지만 의미 없는 값이 나오게 되는 것입니다.

 

마지막으로 3바이트 htonl도 만들어 보았습니다.

#define hton24(n1, n2, n3)  ((uint32_t)(((n3 & 0xff) << 16) | ((n2 & 0xff) << 8) | (n1 & 0xff)))
#define ntoh24(n1, n2, n3)  ((uint32_t)(((n3 & 0xff) << 16) | ((n2 & 0xff) << 8) | (n1 & 0xff)))

저 매크로를 이용하여 함수를 만들어서 배열을 입력하지 않고 사용할 수도 있을 것입니다.

 

혹시 제가 쓴 글에 뭔가 추가해야 할 사항이 있다면 알려주세요

후기) htonll이란 메소드가 존재합니다. 이 메소드는 8바이트 변수값 변경을 지원합니다. 

 

 

  

+ Recent posts