노트 필기 잡동사니
- 서버는 PORT번호를 보통 고정시킨다. 일반적으로 0~1023 포트는 관리자 권한 없이 포트를 열 수 없다. 그러므로 될 수 있으면 0~1023번 포트는 사용하지 않도록 한다. 단 클라이언트의 경우 안쓰는 포트 중 하나를 랜덤으로 사용한다.
- 네트워크는 이기종 끼리도 통신이 가능해야 한다.
- 포트가 끊기는 데에는 어느 정도 시간이 필요하다.
- TCP/IP의 표준 데이터 저장 방식은 Big-Endian이다.(함수들도 해당) 인텔의 대부분 컴퓨터는 Little-Endian 구조이므로 데이터를 처리할 때 뒤집어 주어야 하고, 통신해 받은 데이터도 뒤집어 주어야 한다. 이들은 함수로 제공되어 있다.
- htonl : Host to Network Long, Long은 데이터 타입을 말하며, ip 주소를 처리해 줄 때 사용한다. Little -> Big
-> INADDR_ANY는 0의 값을 갖고 있다. htonl에 0의 값(혹은 INADDR_ANY)을 적으면 자동으로 유저의 ip가 들어간다.
서버는 IP가 고정되어 있어야 하므로 해당 함수를 이용함에 주의해야 하지만, 클라이언트는 유저마다 ip가 다르므로 클라이언트의 주소를 처리할 때 쓸 수 있다.
- htons : Host to Network Short, Short는 데이터 타입을 말하며, 포트 번호를 처리할 때 사용한다. Little -> Big
- ntoa : Network to ASCII, Big-Endian으로 저장된 ip주소를 문자열로 읽기 좋게 해준다.
-> C0 0A A8 63 -> 192.10.168.99
- ntohl : htonl의 반대 개념이다. Big -> Little
- ntohs : htons의 반대 개념이다. Little -> Big
Dos : Denial of Service Attack( 서비스 거부 공격 )
DDoS : Distributed Dos( 분산 서비스 거부 공격 )
근데 일반적으로 최근 컴퓨터의 성능이 매우 향상되어 DDoS공격은 잘 먹히지 않는다
Protocol Family : 프로토콜 계열, 통신 규약( TCP, UDP, 등 여러 프로토콜이 존재한다. )
Address Family : 주소 계열( IP )
INET : Inter Network(인터넷)의 약자.
grep : Linux 명령어. 파일이나 폴더에서 해당 문자열을 검색한다.
/ : vi에서 문자열을 검색.
wine : 윈도우 프로그램을 리눅스에서 구동되게 하는 유틸리티.
신뢰성 : 파일의 100% 전송을 확신하는 것. Stream을 이용한다.
-> 받았는지 확인하여 안받았으면 다시 보내고, 받았으면 다음을 보낸다. TCP는 신뢰성 통신이다. IP는 TCP에 밀접되어 있어 보통 TCP는 IP와 붙여 TCP/IP라고 한다.
비신뢰성 : 파일이 100%전송되는 것을 보장하지 못한다. Data Gram을 이용한다.
-> 받건 안받건 다음 내용을 보낸다. UDP는 비신뢰성 통신이라고 한다.
랑데부 소켓 : 외부에서 클라이언트를 받아오는 소켓(주쌤 표현으로는 얼굴마담...)
커뮤니케이션 소켓 : 내부에서 클라이언트의 데이터를 처리하는 소켓
서버의 동작 과정
번호 |
이름 |
설명 |
1 |
소켓 생성 |
socket 함수를 이용해 server socket을 생성하고 연다. |
2 |
구조체 설정 |
소켓에 대한 정보들을 입력한다. |
3 |
바인딩 |
구조체를 소켓에 적용시킨다. |
4 |
Listen |
접속 전에 대기, 2번째 인자는 대기 최대 인원이다.(매우 빠르므로 거의 의미가 없음) //함수 원형 설명 추가하기 |
5 |
Accept |
접속을 수락한다 |
TCP는 3 Handshake 방식으로 접속한다. 단 이 방법은 도중에 무언가 사라지면 작업에 장애를 초래할 수 있다. 이 허점을 파고들어 공격하는 것이 DDoS이다.
접속하는 방법 : VMware에서 서버를 가동한 상태에서 윈도우의 명령프롬프트를 열고 텔넷으로 리눅스에서 설정한 ip와 소스에서 설정한 포트를 입력하여(telnet xxx.xxx.xxx.xxx 9999) 접속한다.
서버 소스(server.c)
#include <stdio.h> #include <sys/socket.h> // 소켓 함수용 라이브러리. #include <arpa/inet.h> #include <stdlib.h> // C 표준 라이브러리. #include <string.h> #include <unistd.h>
#define MAXPENDING 5 // 최대 허용 대기 가능 인원
int main() { int servSock; // 서버 소켓번호는 정수형으로 이루어져 있다. int clntSock; // 클라이언트 소켓. struct sockaddr_in echoServAddr; // 서버 소켓 구조체(신버전) struct sockaddr_in echoClntAddr; // 클라이언트 주소... unsigned short echoServPort; // 서버 포트.... 9999... unsigned int clntLen; //
int iRet; // 잡동사니를 넣어두는 변수.
unsigned char ucBuffer[500]; // 클라이이언트로부터 메시지를 저장하는 버퍼.
echoServPort = 9999; // 사용할 포트 번호.
servSock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); // Socket을 생성한다. // Protocol Family
if( servSock < 0 ) // 위의 구문에서 에러가 발생했다면 { printf( "Socket Function Error!\n" ); return( 0 ); }
memset( &echoServAddr, 0, sizeof( echoServAddr ) ); //해당 메모리를 전부 0으로 초기화. echoServAddr.sin_family = PF_INET; // Internet Address Family echoServAddr.sin_addr.s_addr = htonl( INADDR_ANY ); // INADDR_ANY는 자동으로 주소를 입력시켜 준다. 직접 입력해도 되나, 메모리에 저장되는 방식에 유의해야 한다.(Little->Big(표준)) // 서버 주소는 반드시 고정되어야 하지만 클라이언트에서는 클라이언트의 주소를 자동할당하는 것이 좋다. 컴퓨터가 바뀌어도 자동 할당됨. echoServAddr.sin_port = htons( echoServPort ); // 포트 번호도 Big-Endian으로 바꿔주어야 한다. short형이므로 htons iRet = bind( servSock, ( struct sockaddr * )&echoServAddr, sizeof( echoServAddr ) ); // 열려 있는 소켓에 포트 번호, IP등을 부여한다. 바인딩 // bind함수에 쓰이는 인자는 구형 구조체이므로 캐스팅한다. // 블로킹 함수가 아니다. if( iRet < 0 ) // 위의 구문에서 에러가 발생했다면 { close( servSock ); // 소켓을 연 이후 에러가 났으므로 닫아주도록 한다. printf( "Bind Failed Error!\n" ); return( 0 ); }
iRet = listen( servSock, MAXPENDING ); // MAX PENDING : 5
if( iRet < 0 ) // 위의 구문에서 에러가 발생했다면... { close( servSock ); // 소켓을 연 이후 에러가 났으므로 닫아주도록 한다. printf( "Listen Failed Error!\n" ); return( 0 ); }
clntLen = sizeof( echoClntAddr ); clntSock = accept( servSock, ( struct sockaddr * )&echoClntAddr, &clntLen ); // 들어오는 사람의 IP 및 정보가 요구되므로 2번째 인자가 클라이언트 정보. // accept는 구 자료형 형식의 함수이기 때문에 강제 캐스팅 // clntLen : 크기. // accept에서는 접속이 될 때 까지 그대로 대기상태가 된다. 블로킹 함수. // servSock : 랑데부 소켓(외부에서 받아들이기만 한다) // clntSock : 커뮤니케이션 소켓. 내용을 처리할 때 쓰인다. if( clntSock < 0 ) { close( servSock ); // 서버 소켓은 열려 있으므로 닫아준다. printf( "Accept Function Error!\n" ); return( 0 ); } printf( "Handling client IP : %s\n", inet_ntoa( echoClntAddr.sin_addr ) ); // Network to ASCII Code(수치로만 되어있는 IP주소를 사용자가 알아볼 수 있게 문자열로 바꾸어준다.) printf( "Handling client PORT : %d\n", ntohs( echoClntAddr.sin_port ) ); // Big-Endian으로 저장된 클라이언트의 포트 정보를 서버에 맞게 Little-Endian으로 변환하여 출력한다. while( 1 ) { iRet = read( clntSock, ucBuffer, sizeof( clntSock ) ); // 커뮤니케이션 소켓으로부터 값을 읽어온다. // read함수는 글자 수를 반환하므로 iRet에 글자수를 임시로 저장한다. ucBuffer[ iRet ] = 0; // 마지막 글자는 \n(개행문자)이므로 NULL로 처리한다. write( 1, ucBuffer, iRet ); // 버퍼에 있는 내용을 stdout(화면)에 출력한다.
if( 'q' == ucBuffer[0] ) // 첫 글자가 'q'이면 서버 프로그램을 종료한다. { break; } }
close( servSock ); // 서버 소켓을 종료한다. close( clntSock ); // 클라이언트 소켓을 종료한다. return( 0 ); // 빠빠이 }
|