TCP/IP 4계층과 OSI 7계층

네트워크 2014. 10. 22. 22:12 Posted by Owen.K

------- TCP/IP 4계층과 OSI 7계층




- TCP/IP 4계층은 인터넷 모델이라고도 한다.


 레벨

 계층

 기능

 4계층

 Application

응용 계층

프로토콜 : HTTP, FTP, Telnet, DNS, SMTP

OSI 7계층의 5, 6, 7계층에 해당한다.

TCP/IP 기반의 응용프로그램을 구분할 때 사용한다.

 3계층

 Transport

전송 계층

프로토콜 : TCP, UDP

OSI 7계층의 4계층에 해당한다.

통신 노드 간의 연결을 제어하고, 자료의 송수신을 담당한다.

 2계층

 Internet

인터넷 계층

프로토콜 : IP, ARP, RARP, ICMP, OSPF

OSI 7계층의 3계층에 해당한다.

통신 노드 간의 IP 패킷을 전송하는 기능 및 라우팅 기능을 담당한다.

 1계층

 Network Interface

네트워크 인터페이스 계층

프로토콜 : Ehternet, Token Ring, PPP

OSI 7계층의 1, 2 계층에 해당한다.

CSMA/CD, MAC, LAN, X.25, 패킷망, 위성 통신, 다이얼 업 모뎀 등 전송에 사용된다.

 

Ethernet과 IP, TCP, UDP Header

네트워크 2014. 10. 22. 22:11 Posted by Owen.K

------- Ethernet과 IP, TCP, UDP Header









------- Ethernet Frame


Ethernet frame은 다음과 같이 몇몇 타입이 있다.

- Ethernet II

- IEEE 802.3

- IEEE 802.2 Logical Link Control (LLC) frame

- IEEE 802.2 Subnetwork Access Protocol (SNAP) frame



--- Ethernet II


DEC, Intel, and Xerox에 의해 만들어졌으며,

DIX Ethernet 이라고도 불린다


첫 번째 필드엔 목적 MAC주소, 

두 번재 필드엔 보낸 MAC주소,

세 번째 필드엔 Ether type이라고 Ethernet 프로토콜 타입,

네 번째 필드엔 정보,

다섯 번째 필드엔 CRC(cyclic redundancy check) 또는 FCS(Frame Check Sequence)라고

오류 검출에 쓰인다.


(출처 : http://en.wikipedia.org/wiki/Ethernet_frame)






--- IEEE 802.3


Novell 사에서 IPX protocol용으로 만들어졌다.

Ethernet II와 흡사한 구조로 세 번재 필드인 EtherType 대신 Length 이다.







--- IEEE 802.2 Logical Link Control (LLC) frame


OSI 계층 구조에서만 사용하는 Frame으로 거의 사용하지 않는다.

토큰링이 이 구조를 사용한다.







--- IEEE 802.2 Subnetwork Access Protocol (SNAP) frame


OSI 계층 구조에서 사용한다. TCP/IP가 주류므로 OSI는 잘 사용하지 않는다.

AppleTalk이 이 구조를 사용한다.









--- IEEE 802.1Q tag

VLAN (virtual local area network) 서비스에 대한 규약으로 Ethernet frame 들에 포함하고 있는데,

데이타 링크 계층(TCP/IP) or 네트워킹 계층(OSI)인 라우터나 스위치 등에서 운영되는 것으로,

브로드캐스트 필터링, 보안, 주소 축약, 트래픽 흐름 관리, QoS 등에 사용된다고 한다.


VLAN은 Ethernet frame에 Source Address 와 EtherType 또는 Length fields 사이에 위치하고 있거나,

EtherType 또는 Length fields 에 위치하고 있다.

처음 2 byte (2 octets)에는 Tag Protocol Identifier (TPID) 값이 0x8100으로 되어 있다.









------- Ethernet Frame 구조체


linux 환경

/usr/include/linux/if_ether.h

/usr/include/net/ethernet.h

위치에서 확인할 수 있다




struct ethhdr { unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ unsigned short h_proto; /* packet type ID field */ };

 



struct ether_header { u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */ u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */ u_int16_t ether_type; /* packet type ID field */ };

 





--- 데이터 타입은

/usr/include/i386-linux-gnu/sys/types.h 에 있다.





--- ETH_ALEN 값은

/usr/include/linux/if_ether.h 에 있다.






--- type의 종류는

/usr/include/net/ethernet.h 에 있다.

더 많은 type은 /usr/include/linux/if_ether.h를 참고...














------- IP Header 구조체


http://en.wikipedia.org/wiki/IPv4 참고

 


/usr/include/netinet/in.h 에 프로토콜에 타입

/usr/include/netinet/ip.h 에 IP구조체가 정의 되어 있다



struct iphdr

{

#if __BYTE_ORDER == __LITTLE_ENDIAN

unsigned int  ihl:4;        //헤더 길이

unsigned int version:4;        //IP version 4

#elif __BYTE_ORDER == __BIG_ENDIAN

unsigned int version:4;        //IP version 4

unsigned int  ihl:4;        //헤더 길이

#else

# error "Please fix <bits/endian.h>"

#endif

u_int8_t    tos;

u_int16_t    tot_len;

u_int16_t    id;

u_int16_t    frag_off;

u_int8_t    ttl;

u_int8_t    protocol;

u_int16_t    check;

u_int32_t    saddr;

u_int32_t    daddr;

/* The options start here. */

};







------- TCP Header 구조체



/usr/include/netinet/tcp.h

/usr/include/linux/tcp.h

에 위치해 있다.


struct tcphdr

{

u_int16_t    source;        //source port

u_int16_t    dest;        //destination port

u_int32_t    seq;          //sequence number

u_int32_t    ack_seq;          //acknowledgement number

#if __BYTE_ORDER == __LITTLE_ENDIAN

u_int16_t    res1:4;           //(unused)

u_int16_t    doff::4;           //data offset

u_int16_t    fin:1;

u_int16_t    syn:1;

u_int16_t    rst:1;

u_int16_t    psh:1;

u_int16_t    ack:1;

u_int16_t    urg:1;

u_int16_t    res2:2;

#elif __BYTE_ORDER == __BIG_ENDIAN

u_int16_t    doff::4;           //data offset

u_int16_t    res1:4;           //(unused)

u_int16_t    res2:2;           //(unused)

u_int16_t    urg:1;

u_int16_t    ack:1;

u_int16_t    psh:1;

u_int16_t    rst:1;

u_int16_t    syn:1;

u_int16_t    fin:1;

#else

#error "Adust your <bits/endian.h> defines"

#endif

u_int16_t    window;        //window

u_int16_t    check;       //checksum

u_int16_t    urg_ptr;        //urgent pointer

};

20140624 -

2014. 6. 24. 16:53

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

20140623

2014. 6. 23. 17:34

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.




 

  : 사용자의 명령을 받아들인 후 필요한 프로그램을 호출하여 실행되게끔 하는 것으로 명령어 해석기라 부르기도 한다.

리눅스 프롬프트에서 ls와 같은 명령을 입력하고 Enter키를 누르면 ls 명령어가 실행되고 프롬프트가 생기는데

이러한 역할을 담당하는 것이 셸이다.

 

프롬프트란 시스템이 다음 명령이나 메시지 , 다른 사용자의 행동을 받아들일 준비가 되었음을 사용자에게 알려 주는 메시지이다.

밑에 소스에서 printf문으로 출력되는 myshell: 이 프롬프트라 할 수 있다. 

더보기

 

s = strtok_r(buf,dekin, &save);

while(s)

{

arg[argv++] = s;

s= strtok(NULL,delim,&save);

}

strtok_r(buf,delim,&save)는 buf에 저장된 문자열에서 delim에서 설정된 문자가 나오면 

단어로 자르는 함수이다.

buf에 저장된 내용이 ls -al이라고 할때 arg[0]에는 ls가 저장되고 arg[1]은 -al이 저장된다.

 

시그널의 종류

더보기

 

 

시그널 처리하기

 

각 프로세스는 시그널과 관련해서 크게 세 가지 일을 한다.

 

첫째 시그널이 도착했을 때 그에 대한 응답을 한다.

 

응답에 대한 세 가지 방법

① 시스템에서 기본적으로 설정한 동작

② 시그널을 무시한다. 단 SIGKILL과 SIGSTOP 시그널은 무시할 수 없다.

③ 특정 루틴(함수)을 실행

 

둘째 시그널이 도착했을 때 시그널을 블록화한다.

중요요한 부분을 실행하고 있을 때 시그널이 도착하면 이를 블록화해 뒤로 미룰 수가 있는데 블록을 해제하면

블록화되었던 시그널이 전달된다.

 

셋째 프로세스에 시그널을 보낸다.

kill,raise와 같은 함수를 이용하여 다른 또는 자기 자신의 프로세스에 시그널을 보낼 수 있다.

 

관련함수

void (*signal(int signum, void (*sighandler)(int)))(int);

int sigaction(int signum , const struct sigaction   *act , struct sigaction *oldact);

 

sleep 함수

sleep함수는 seconds초 동안 정지하며 함수 호출 후 seconds가 지나거나 시그널을 받으면 블록에서

깨어난다.

기능 : 일정 시간 동안 정지한다.

기본형 : unsigned int sleep(unsigned int seconds);

            seconds  : 정지할 시강로 단위는 초

반환값 : 성공 : 남은시간

헤더파일 : <unistd.h> 

 

반환값이 남은 시간이며 , 시간이 경과되면 0이 반환되고 다른 시그널에 의해 중간에 깨어나면 남아있는

시간을 반환한다.

 

SIGINT(시그널)

ctrl + c는 실행화면에서 SIGINT 시그널을 보내 프로세스를 종료시키는 동작키이다.

 

signal 함수

시그널을 받았을 때 종료되지 않고 무시하거나 특정 함수가 실행되도록 할 수 있는 함수이다.

 

기능 : 시그널 처리를 설정한다.

기본형 : void (*signal(int signum,void(*sighandler)(int)))(int);

            signum : 시그널 번호

            sighandler : 설정할 시그널 핸들러

반환값 : 성공 : 이전의 시그널 핸들러 포인터

            실패 : -1(SIG_ERR)

헤더파일 : signal.h 

 

signal함수를 호출하면 signum 시그널을 받게되고 sighandler에 설정한 일을 하게된다.

sighandler 인수로는 동작할 함수 이름, SIG_IGN , SIG_DFL이 있다.

 

 시그널 핸들러의 세가지 유형

 시그널 핸들러

의미 

 함수 이름

 시그널을 받으면 "함수이름" 함수가 실행된다.

 SIG_IGN

시그널을 받으면 무시한다. 

 SIG_DFL

 시그널을 받으면 시스템에서 기본적으로 설정한 동작을 한다.

 

 

EX)

더보기

 

9번 라인에서 SIGINT 시그널신호가 오면 signalHandler함수를 실행 하도록 설정

SIGINT 시그널을 받아 signalHandler 함수가 실행되면

20번 라인의 signal에 의해 SIGINT를 다시 설정되고 SIGINT 시그널을 받으면 종료하게 된다.

 

함수의 타입찾기

4월 1일 필기참조

 

///////////////////////////////////////////////////////////////////////////////////////

다수의 클라이언트와의 네트워킹

6월17일에 작성한 서버/클라이언트는 1:1 통신이라 서버에서 한명만 통신이 가능했다.

1:1에서는 서버가 직접 클라이언트와 연결을 했지만  다수의 클라이언트와 연결을 하기위해서는 fork함수를

이용하면된다.

fork함수를 사용하면 서버와 동일한 자식프로세스가 생성되고 이 자식프로세스와 접속을 요청한 클라이언트와

연결을 시켜주면 통신을 할 수 있다.

그러면 서버는 또 다른 클라이언트의 요청을 받아들이고 다시 자식프로세스를 생성하여 다른 클라이언트와

연결을 시켜주면 다중네트워킹이 가능해진다.

 

 다중 네트워크 소스

서버

더보기

 

 

 클라이언트

더보기

 

 결과

더보기

 

 

일대일 채팅 프로그램

동작과정

① 서버는 socket으로 servsockfd 소켓을 생성하고 bind 로 주소 정보를 연결한다.

② 클라이언트가 sockfd 소켓을 생성하고 connect에 의해 서버에게 통신을 요청한다.

③ 서버가 accept로 요청을 받아들이고 새로운 소켓인 clntSockfd를 통해 클라이언트와 연결한다.

④ 서버와 클라이언트 모두 자식 프로세스를 생성한다.

⑤ 서버의 자식 프로세스는 servSpclfd 소켓의 연결을 끊는다.

⑥ 서버와 클라이언트의 부모 프로세스는 터미널로 입력되는 데이터를 읽어 상대방에게 보내고

    자식 프로세스는 데이터를 받아 터미널에 출력한다.

⑦ 이러한 동작을 반복하다가 클라이언트의 터미널로 입력되는 애용이 quit면 클라이언트의 부모 프로세스는

    자식 프로세스에게 SIGINT 시그널을 보내 종료시키고 자신도 종료한다.

⑧ 다시 서버는 또 다른 클라이언트와의 일대일 채팅을 기다린다.

 

소스파일(리눅스용)

서버

server.c

 

클라이언트 

client.c

 

실행화면

 

 

20140619 - 프로세스, 함수 포인터

네트워크 2014. 6. 19. 13:57 Posted by Owen.K



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

프로세스

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



--- exit

프로세스를 정상적으로 종료시키는 함수.


#include <stdlib.h>

void exit(int status);

status : main 함수의 리턴값

리턴값

없음





--- atexit

프로그램을 정상적으로 종료할 때 실행될 함수를 등록한다.


#include <stdlib.h>

int atexit( void (*function)(void) );

function : 등록할 함수 이름으로 인수르 값는 함수

리턴값

성공시

0

실패시

-1

--- wait

자식 프로세스가 종료될 때까지 기다린다.

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int *status);

status : 자식 프로세스가 종료될 때의 상태 정보.

status 4byte 중에 하위 2byte만 사용하는데

정상 종료시

하위 2byte 중 상위 1byte에 return 값, exit(값)이 들어가고

하위에는 0이 채워짐.


비정상 종료시

최하위 1byte에 프로세스를 종료시킨 시그널 번호가 저장된다.

리턴값

성공시

종료된 자식 프로세스의 프로세스 ID

실패시

-1


--- waitpid

(특정) 자식 프로세스가 종료될 때까지 기다린다.

#include <sys/types.h>

#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);

pid : 종료를 기다리는 자식 프로세스의 ID

-1을 주면 여러 자식중 하나라도 종료되기를 기다린다.

0을 주면 호출한 프로세스의 그룹 ID와 같은 그룹 ID를 가지는 자식 프로세스가 종료되기를 기다린다.

양수를 주면 그에 해당하는 자식 프로세스가 종료되기를 기다린다.

status : 자식 프로세스가 종료될 때의 상태 정보

options : 옵션

WNOHANG 옵션을 주면 자식이 종료되지 않더라도 부모 프로세스는 계속 실행한다.

0 옵션을 주면 wait과 동일하게 자식이 종료할 때가지 부모는 블록화된다.

리턴값

성공시

종료된 자식 PID

실패시

-1


--- 프로세스를 대체하는 함수들

하나의 프로세스가 실행 중에 이 함수를 사용하여 다른 프로세스를 실행시키면

다른 프로세스를 실행시키고 자신은 종료한다.

#include <unistd.h>

int execl( const char *path, const char *arg, ... , NULL);

int execlp( const char *file, const char *arg, ... , NULL);

int execle( const char *path, const char *arg , ... , char * const envp[]);

int execv( const char *path, char *const argv[]);

int execvp( const char *file, char *const argv[]);

int execve( const char *path, char *const argv[], char *const envp[]);


path : 실행될 파일의 경로와 이름

file : 실행될 파일 이름

arg : 실행될 인수들

argv : 실행 파일의 인수 배열

envp : 환경변수

리턴값

성공시

없음

실패시

-1

#include <stdio.h>

#include <unistd.h>

int main()

{

     printf("Running ls with execl\n");


     if(-1 == (execl("/bin/ls", "ls", "-al", NULL)))

     {

         printf("error\n");

         return 0;

     }

     printf("execl failed to run ls\n");

     return 0;

 }

--------------
함수 포인터
--------------

--- 인자로 함수를 받을 때

void test1 ( int (*fp)(const char *, ... ) )

{

fp("test\n");

}


--- 리턴값을 함수로 할 때

int (* test2 (void) )(const char *, ... )

{

return printf;

}


--- 인자도 받고 리턴값도 함수

int (* test3 (int (*fp)(const char *, ... )) )(const char *, ...)

{

return fp;

}

int main()

{

int (*fp)(const char *, ... );


test1(printf);


fp = test2;

fp("test2\n");


fp = test3(printf);

fp("test3\n");


return 0;

}


 

 

 

프로세스 종료하기


exit 함수

프로세스가 정상적으로 종료된다. status의 하위 8비트는 main 함수의 반환값이 된다. 그 반환값을

보고 어떠한 상태로 종료되었는지를 알 수 있게 된다.

기능

정상적으로 프로세스를 종료한다.

기본형

void exit(int status);

status : main 함수의 반환값

반환값

없음

헤더 파일

<stdlib.h>







#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

int main()

{

      pid_t pid;

if((pid=fork())==-1)

{   /*fork 호출에 실패하면 1을 반환하고 종료*/

      perror("fork failed");

     exit(1);

}

else if(pid != 0)

{   /*부모 프로세스는 2를 반환하고 종료*/

printf("parent process\n");

      exit(2);

}

else

{   /*자식 프로세스는 3을 반환하고 종료*/

         printf("child process\n");

         exit(3);

}

return 0;

}



atexit 함수

exit 함수가 호출되거나 main 함수로부터 리턴될 때 실행될 함수를 등록하는 함수로, exit 호출을

하면 atexit에 의해 등록된 함수가 실행된다. 만약 등록된 함수가 여러 개일 경우 최근에 등록된

함수순으로 실행되고 인수에 함수 이름을 위치한다.

기능

exit 호출 때 실행될 함수를 등록한다.

기본형

int atexit(void (*function)(void));

function : 등록할 함수 이름으로 인수가 없는 함수

반환값

성공 : 0

실패 : -1

헤더 파일

<stdlib.h>




      1 #include <stdio.h>

      2 #include <stdlib.h>

      3

      4 void func1(void);

      5 void func2(void);

      6 void func3(void);

      7

      8 int main()

      9 {

     10     /*exit 함수 호출 때 실행될 함수를 func1,func2,func3 순으로 등록*/

     11     atexit(func1);

     12     atexit(func2);

     13     atexit(func3);

     14

     15     exit(0);

     16     return 0;

     17 }

     18

     19 void func1(void)

     20 {

     21     printf("run function1\n");

     22 }

     23

     24 void func2(void)

     25 {

     26     printf("run function2\n");

     27 }

     28

     29 void func3(void)

     30 {

     31     printf("run function3\n");

     32 }





abort에 의해 비정상적으로 종료될 때 atexit에 의해 등록된 함수가 실행되지 않음을 보여줄 것이다.

argv[1]이 0이면 if조건이 참이 되어 abort에 의해 비정상적으로 종료되어 atexit에 의해 동록된 함수가

실행되지 않는다.




      1 #include <stdio.h>

      2 #include <stdlib.h>

      3

      4 void func(void);

      5

      6 int main(int argc, char *argv[])

      7 {

      8     atexit(func);

      9     /*atoi는 정수 형태의 문자열을 정수로 변환하는 함수로 argv[1]이

     10      * 1이면 1로 변환. argv[1]이 0이 되어야 if 조건이 참이 됨.*/

     11     if(!atoi(argv[1]))

     12     {   /*비정상적으로 프로세스를 종료하는데 atexit에 의해 등록된

     13          함수는 실행되지 않음.*/

     14         abort();

     15         exit(0);

     16         /*정상적으로 프로세스를 종료하는데 atexit에 의해 등록된

     17          * 함수가 실행됨.*/

     18     }

     19     return 0;

     20 }

     21

     22 void func(void)

     23 {

     24     printf("run function\n");

     25 }



프로세스 종료를 기다림


wait 함수 

자식 프로세스가 종료될 때까지 아무 일도 하지 않고 기다리는 함수.

기능

자식 프로세스가 종료될 때까지 기다린다.

기본형

pid_t wait(int *status);

status : 자식 프로세스가 종료될 때의 상태 정보

반환값

성공 : 종료된 자식 프로세스의 프로세스 ID

실패 : -1

헤더 파일

<sys/types.h>

<sys/wait.h>


status에는 자식 프로세스가 종료될 때의 상태 정보가 저장되는데, 자식 프로세스가 정상적으로 

종료되면 status의 하위 8비트에는 0이 저장되고 상위 8비트에는 exit(번호)의 '번호'가 저장된다.


비정상적으로 종료되면 status의 우측 8비트에 프로세스를 종료시킨 시그널의 번호가 저장된다.





함수이름 test

TCP 3 Way Handshake




연결 설정 상태 SEQ는 처음에 랜덤값이 걸린다.

클라이언트가 connect함수 호출 시 진행

데이터 송,수신을 하기 전에 서로 준비가 되었는지 묻고 답함 서로 간의 데이터를 송,수신 할 준비가 되어있다는 것을 확인

①SYN

SEQ (SEQuence)

1000 

 비트(FLAGS-비트를 구분함)

     SYN(1)          ACK(0)      

ACK 

1. SYN 세그먼트

클라이언트가 SYN 플래그 = 1로 설정하고, 초기 순서번호(ISN)을 랜덤하게 선택한 후

서버로 전송 (순서번호 초기화 목적) 확인응답 번호,윈도우 크기 필드 미정의됨. 

데이터 미 전송하지만, 하나의 순서번호는 소비함. SYN-SENT TCP상태로 전이.


 

②ACK + SYN

ACK 

1001 

 비트(FLAGS-비트를 구분함)

     SYN(1)          ACK(1)      

SEQ (SEQuence)

2000 

 

2. SYN+ACK 세그먼트

  서버에서 초기 순서번호(ISN)을 랜덤하게 선택하고 클라이언트로 전송 ACK 플래그 = 1로 

설정하고, 클라이언트로부터 수신을 기대하는 다음 순서번호를 전송 (확인응답).

윈도우 크기 필드 정의됨. 데이터 미 전송하지만, 하나의 순서번호는 소비함.

서버는 LISTEN → RECEIVED-SYN TCP상태로 전이 (절반 개방,Half Open : 75초 대기)


 

③ACK

SEQ (SEQuence)

1001 

 비트(FLAGS-비트를 구분함)

     SYN(0)          ACK(1)      

ACK 

2001 


 

3. ACK 세그먼트

  단순히 ACK 전송 만을 위함. 순서번호는 세그먼트 2(SYN+ACK 세그먼트)의 

확인응답번호를 그대로 복사 사용. 일반적으로, 데이터 미 전송하며, 어떠한 순서번호도 

소비하지 않음. 양단 모두 ESTABLISHED TCP상태로 들어감.




TCP Four-way handshaking


연결 종료

클라이언트나 서버가 close함수 호출 시 진행

종료해도 되냐고 물어보고 난 후 종료 할 준비가 되었다고 하면 ACK를 전송해서 종료

만약 종료할 상황이 아니면 남아있는 데이터를 전송하고 종료할 준비가 되었다고 함.

close는 클라이언트 서버와 관계 없음.


①FIN

SEQ (SEQuence) 

1000 

ACK

0

 비트(FLAGS-비트를 구분함)

     FIN(1)          ACK(0)     

 

1. 클라이언트 TCP는 첫번째 세그먼트로서 FIN 세그먼트를 전송한다..


 ②ACK

SEQ (SEQuence) 

 5000 

ACK

1001

 비트(FLAGS-비트를 구분함)

     FIN(0)          ACK(1)


 

2. 서버 TCP는 클라이언트로부터의 FIN세그먼트의 수신을 확인하기 위하여 두 번째 세그먼트인 ACK 세그먼트를 전송한다이 세그먼트에서 TCP FIN 세그먼트로부터 수신한 순서 번호에 1을 더한 값을 확인응답 번호로서 사용한다

 

③FIN

SEQ (SEQuence) 

5001 

ACK

1001 

 비트(FLAGS-비트를 구분함)

FIN(1)          ACK(0)

 

3. 서버 TCP는 서버-클라이언트 방향으로서 데이터 전송을 계속할 수 있다.만일 더 이상 보낼 데이터가 없으면서버 TCP는 세 번째 세그먼트를 전송한다이 세그먼트는 FIN 세그먼트 이다.

 

④ACK

SEQ (SEQuence) 

1001 

 ACK

     5002      

 비트(FLAGS-비트를 구분함)

FIN(0)          ACK(1)


4. 클라이언트 TCP TCP서버로부터의FIN 세그먼트 수신을 확인하기 위하여 네 번째 세그먼트인 ACK 세그먼트를 전송한다이 세그먼트에는 서버로부터의 FIN 세그먼트로부터 수신된 순서 번호에 1을 더한 값으로 설정되는 확인응답 번호가 포함된다.

연결 종료 절차는 클라이언트로부터 시작한다클라이언트 프로그램은 자신이 TCP에게 데이터 전송이 종료되었고 따라서 연결을 종료하고자 한다는 것을 알린다이것을 능동 종료 요구라고 한다.

능동 종료 요구를 수신한 후에클라이언트 TCP는 클라이언트-서버 방향의 연결을 종료한다그러나 다른 방향으로서의 통신은 여전히 개방되어 있다.

서버 프로그램이 서버-클라이언트 방향으로의 데이터 전송을 끝마치게 되면서버는 서버-클라이언트 방향의 연결 해지를 자신의 TCP에게 요구할 수 있다. (수동 종료)

이 연결 종료를 4-단계 핸드쉐이킹(four-way handshaking)라고 한다.

 




멀티 프로세스 생성하기


메모리에 들어가서 실행중에 있는 프로그램을 프로세스라 한다. 

프로그램을 실행하려면 메모리에 있어야 한다.

PID(Process ID) : 프로세스마다 고유 번호가 붙는다.

      윈도우나 리눅스 등에서 확인할 수 있다.

 

이렇게 같은 프로그램이 돌아가도 PID는 틀리다는 것을 확인 할 수 있다.


 

 

fork 함수 : 현재 프로세스와 동일한 프로세스를 복제하여 생성한다.

fork에 의해 생성되는 새로운 프로세스를 자식 프로세스라 하며,

fork를 호출한 프로세스를 부모 프로세스라 한다.

기능

자식 프로세스를 생성한다.

기본형

pid_t fork(void);

반환값

성공 : 

부모 프로세스 : 자식 프로세스의 프로세스 ID

자식 프로세스 : 0

실패 : -1

헤더 파일

<sys/types.h>

<unistd.h>

 

fork함수

fork에 의해 반환되는 값

 


 


fork 부모 프로세스가 자식 프로세스에 클론 됨.

fork.c


fork가 호출되어서 return 되는 시점에 -1과 비교되기 직전에 return 되면 자식 프로세스 하나가 

생성되어서 2개가 되는 것이다. 둘 다 똑같이 존재하며 둘 다 -1과 비교를 하고 두 개가 같이

pritnf가 실행되어 출력되고 종료된다. 하지만 이러면 무엇이 부모 프로세스인지 무엇이

자식 프로세스인지 분간이 어렵다. 원본과 완전히 똑같이 복사되기 때문이다.



fork 부모 프로세스와 자식 프로세스 구분 방법

fork1.c



pid에 fork()가 호출된 함수의 반환값을 집어 넣으면, 자식 프로세스는 반환값이 0이고

부모는 0과 -1을 제외한 숫자가 생성된다. 이렇게 부모 프로세스와 자식 프로세스를 구분할 수 있다.

 


 


getpid, getppid 함수 : getpid는 자신의 프로세스 ID를 반환하는 함수

getppid는 부모 프로세스의 프로세스 ID를 반환하는 함수.

기능

getpid는 자신의 프로세스 ID, getppid는 부모 프로세스의 프로세스 ID를 얻는다.

기본형

pid_t getpid(void);

pid_t getppid(void);

반환값

성공 : 프로세스 ID

실패 : 발생하지 않음

헤더 파일

<unistd.h>

 

 

getpid 

getpid.c

pid에 fork함수의 반환값을 저장하고, 0이 아니면 getpid는 부모 프로세스의 pid를 가진다. 그리고

현재 pid는 fork의 반환값이므로 새롭게 생성된 자식 프로세스의 pid를 가지고 있다.

pid가 0이 아니라면, getpid는 자식 프로세스의 pid이고, getppid는 부모 프로세스의 pid를 가진다.

 

 


 

vfork 함수 : fork와 같이 새로운 프로세스를 생성하며, 반환하는 값도 동일하다.

 다른 점은 자식 프로세스가 exit나 exec를 호출할 때까지 부모 프로세스는

 실행되지 않고 기다린다.

기능

자식 프로세스를 생성하고 부모 프로세스는 기다린다.

기본형

pid_t vfork(void);

반환값

성공 : 부모 프로세스 : 자식 프로세스의 프로세스 ID

   자식 프로세스 : 0

실패 : -1

헤더 파일

<sys/types.h>

<unistd.h>


 

 

 

 


 

 

 

 

 

vfork.c

위의 소스와 비슷하지만 vfork는 자식 프로세스가 끝나고 나서 실행되므로

자식 프로세스의 출력문이 먼저 출력되고, 부모 프로세스의 출력문은 자식 프로세스가 종료되면,

실행되어 출력이 완료된다. 


 


 


 





소켓 정보보내기(서버, 클라이언트)


 

여기서 말하는 데이터를 문자열이다.

 

 

 

소켓 번호로 read를 했을 때 

 

저수준 입력을 사용 했을 때

 


server.c

#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <linux/types.h>

#include <netinet/in.h>

#include <errno.h>


int main()

{

     struct sockaddr_in stAddr;      //server addr

     struct sockaddr_in stClient1;

     int iSoc;                       //socket descripter

     int iCle1;

     int iRet;

     unsigned int uiSize;

     unsigned char ucBuff[250];

     iSoc = socket(AF_INET, SOCK_STREAM, 0);


     if(-1==iSoc)

     {

         perror("socket() ");

         return 0;

     }

     stAddr.sin_family = AF_INET;

     //소켓의 첫 번째 인자를 넣는다.

     stAddr.sin_port = htons(PORT);

     stAddr.sin_addr.s_addr = inet_addr("192.168.10.250");


     iRet=bind(iSoc, (struct sockaddr *)(&stAddr),sizeof(stAddr));

     if(-1==iRet)

     {

         perror("bind() ");

         close(iSoc);

         return 0;

     }


     iRet=listen(iSoc,5);

     if(-1==iRet)

     {

         perror("listen() ");

         close(iSoc);

         return 0;

     }


     uiSize=sizeof(stClient1);

     iCle1=accept(iSoc,(struct sockaddr *)(&stClient1),&uiSize);

 //클라이언트의 인적사항 조사 accept가 리턴되면 새로운 소켓을 생성한다.

     if(iCle1==-1)

     {

         perror("accept() ");

         close(iSoc);

         return 0;

     }


     printf("iSoc : %d\niRet : %d \n",iSoc, iRet);

     printf("iCle1 : %d\n",iCle1);

     write(iCle1,"welcome",7);


     iRet=read(iCle1,ucBuff,sizeof(ucBuff));

     ucBuff[iRet]=0;

     printf("%s\n",ucBuff);


     iRet=read(iCle1,ucBuff,sizeof(ucBuff));

     ucBuff[iRet]=0;

     printf("%s\n",ucBuff);


     close(iSoc);

     close(iCle1);

     return 0;

}                                                     


client.c

#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <linux/types.h>

#include <netinet/in.h>

#include <errno.h>


int main()

{

     struct          sockaddr_in stAddr;

     int             iSoc;

     int             iRet;

     unsigned char   ucBuff[250];

     iSoc = socket(AF_INET, SOCK_STREAM, 0);

     if(-1==iSoc)

     {

         perror("socket() ");

         return 0;

     }

     stAddr.sin_family = AF_INET;

     //소켓의 첫 번째 인자를 넣는다.

     stAddr.sin_port = htons(PORT);

     stAddr.sin_addr.s_addr = inet_addr("192.168.10.250");

     iRet=connect(iSoc, (struct sockaddr *)(&stAddr),sizeof(stAddr));

     if(-1==iRet)

     {

         perror("connect() ");   //나머지 에러 내용을 peeror이 출력해줌.

         close(iSoc);

         return 0;

     }

     printf("iSoc : %d\n iRet : %d \n",iSoc, iRet);

     iRet=read(iSoc,ucBuff,sizeof(ucBuff));

     //read함수는 받은 byte만큼 리턴해줌.


     ucBuff[iRet]=0;

     printf("%s\n",ucBuff);//받기


     write(iSoc,"shutup",6);


     iRet=read(1,ucBuff,sizeof(ucBuff));

     //1은 키보드 입력.

     write(iSoc,ucBuff,iRet-1);

     close(iSoc);
     return 0;
}





#include <stdio.h>

#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT	4090
#define BUFSIZE	1024

int main(int argc, char *argv[])
{
	int sockfd;
	struct sockaddr_in servAddr;
	char sendBuffer[BUFSIZE];
	char recvBuffer[BUFSIZE];
	int recvLen;

	if(argc!=2)
	{
		fprintf(stderr, "Usage:%s IP_address\n", argv[0]);
		exit(1);
	}	

	if((sockfd=socket(AF_INET, SOCK_STREAM, 0))== -1)
	{
		perror("sock failed");
		exit(1);
	}	
		
	memset(&servAddr, 0, sizeof(servAddr));

	servAddr.sin_family 		= AF_INET;
	servAddr.sin_addr.s_addr 	= inet_addr(argv[1]);
	servAddr.sin_port 			= htons(PORT);

	if(connect(sockfd, (struct sockaddr*)&servAddr, 
				sizeof(servAddr))== -1)
	{
		perror("connect failed");
		exit(1);
	}	

	while(1)
	{
		printf("Input sending message ==> ");
		fgets(sendBuffer, BUFSIZE, stdin);

		if(!strncmp(sendBuffer, "quit", 4))
		break;	

		if(send(sockfd, sendBuffer, 
					strlen(sendBuffer), 0)!=strlen(sendBuffer))
		{
			perror("send failed");
			exit(1);
		}	

		if((recvLen=recv(sockfd, recvBuffer,
						BUFSIZE-1, 0))<=0)
		{
			perror("recv failed");
			exit(1);
		}	

		recvBuffer[recvLen]='\0';

		printf("Received : %s\n",recvBuffer);
	}

	close(sockfd);
	exit(0);
	
	return 0;
}	




#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 		4090
#define MAXPENDING 	5
#define BUFSIZE		1024

int main()
{
	int servSockfd;
	int clntSockfd;
	struct sockaddr_in servAddr;
	struct sockaddr_in clntAddr;
	char recvBuffer[BUFSIZE];
	int clntLen;
	int recvLen;

	if((servSockfd=socket(AF_INET, SOCK_STREAM, 0))== -1)
	{
		perror("sock failed");
		exit(1);
	}	

	memset(&servAddr, 0, sizeof(servAddr));

	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servAddr.sin_port = htons(PORT);

	if(bind(servSockfd, (struct sockaddr*)&servAddr, 
				sizeof(servAddr))==-1)
	{
		perror("bind failed");
		exit(1);
	}	
	
	if(listen(servSockfd, MAXPENDING)==-1)
	{
		perror("listen failed");
		exit(1);
	}	
	
	while(1)
	{
		clntLen = sizeof(clntAddr);

		if((clntSockfd=accept(servSockfd,(struct sockaddr*)&clntAddr,
						&clntLen))==-1)
		{
			perror("accept failed");
			exit(1);
		}	

		while(1)
		{
			if((recvLen=recv(clntSockfd, recvBuffer,
							BUFSIZE-1,0))== -1)
			{
				perror("recv failed");
				exit(1);
			}	
			if(recvLen==0)
				break;

			recvBuffer[recvLen]='\0';

			printf("Received : %s\n",recvBuffer);

			if(send(clntSockfd, recvBuffer,recvLen,0)!=recvLen)
			{
				perror("send failed");
				exit(1);
			}	
		}	

		close(clntSockfd);
	}
	return 0;
} 



errno



errno - 에러가 나면 에러에 대한 번호를 리턴해줌.




bind(소켓 번호, 구조체주소(내 주소 및 server 주소))

소켓과 내 주소를 결합시킬 때 쓰이는 함수가 bind함수이다.




listen(소켓번호, 대기큐, ) - 첫 번째 인자 소켓번호, 대기큐는 두 번째 인자 보통 5라고 적는다.



accept()


blocking함수는 scanf도 포함.(키보드를 입력받을 때까지 대기타니까.)

non blocking함수는 printf등등


server



#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <linux/types.h>

#include <netinet/in.h>

#include <errno.h>

int main()

{

struct sockaddr_in stAddr;      //server addr

      struct sockaddr_in stClient1;

      int iSoc;                       //socket descripter

      int iCle1;

      int iRet;

      unsigned int uiSize;

      iSoc = socket(AF_INET, SOCK_STREAM, 0);

      if(-1==iSoc)

      {

             perror("socket() ");

             return 0;

      }

      stAddr.sin_family = AF_INET;

      //소켓의 첫 번째 인자를 넣는다.

      stAddr.sin_port = htons(PORT);

      stAddr.sin_addr.s_addr = inet_addr("192.168.10.250");

      iRet=bind(iSoc, (struct sockaddr *)(&stAddr),sizeof(stAddr));

//bind() 함수는 소켓 IP주소와 포트번호를 지정해 준다.

//이로써 소켓을 통신에 사용할 수 있도록 준비됨.

      if(iRet!=0)

      {

             perror("bind() ");

             close(iSoc);

             return 0;

       }

       iRet=listen(iSoc,5);

//두 번째 인자는 대기자 인원 나머지는 refuse connect

//listen()함수는 소켓을 통해 클라이언트의 접속 요청을 기다리도록 설정.

       if(iRet!=0)

         {

              perror("listen() ");

              close(iSoc);

              return 0;

        }

        uiSize=sizeof(stClient1);

        iCle1=accept(iSoc,(struct sockaddr *)(&stClient1),&uiSize);

//클라이언트의 인적사항 조사 accept가 리턴되면 새로운 소켓을 생성한다.

//클라이언트의 접속 요청을 받아들이고 클라이언트와 통신하는 전용 소켓을 생성한다.

//반환값으로 새로운 소켓을 생성.

        if(iCle1!=0)

       {

             perror("accept() ");

             close(iSoc);

             return 0;

      }

      printf("iSoc : %d\niRet : %d \n",iSoc, iRet);

      printf("iCle1 : %d\n",iCle1);

      close(iSoc);

      close(iCle1);

      return 0;

}


client


 #include <stdio.h>

 #include <sys/types.h>

 #include <sys/socket.h>

 #include <arpa/inet.h>

 #include <stdio.h>

 #include <sys/types.h>

 #include <sys/socket.h>

 #include <arpa/inet.h>

 #include <linux/types.h>

 #include <netinet/in.h>

 #include <errno.h>

 int main()

 {

         struct sockaddr_in stAddr;

         int iSoc;

         int iRet;

         iSoc = socket(AF_INET, SOCK_STREAM, 0);

         if(-1==iSoc)

         {

                 perror("socket() ");

                 return 0;

         }

         stAddr.sin_family = AF_INET;

         //소켓의 첫 번째 인자를 넣는다.

         stAddr.sin_port = htons(PORT);

         stAddr.sin_addr.s_addr = inet_addr("192.168.10.250");

         iRet=connect(iSoc, (struct sockaddr *)(&stAddr),sizeof(stAddr));

         if(iRet!=0)

         {

                 perror("connect() ");   //나머지 에러 내용을 peeror이 출력해줌.

                 close(iSoc);

                 return 0;

         }

         printf("iSoc : %d\n iRet : %d \n",iSoc, iRet);

         close(iSoc);

         return 0;

     }


소켓 생성


socket() 함수는 소켓을 생성하여 반환합니다.

헤더#include <sys/types.h> 
#include <sys/socket.h>
형태int socket(int domain, int type, int protocol);
인수
int domain

: 인터넷을 통해 통신할 지, 같은 시스템 내에서 프로세스 끼리 통신할 지의 여부를 설정합니다.

 
domaindomain 내용
PF_INET, AF_INET

IPv4 인터넷 프로토콜을 사용합니다.

PF_INET6

IPv6 인터넷 프로토콜을 사용합니다.

PF_LOCAL, AF_UNIX같은 시스템 내에서 프로세스 끼리 통신합니다.
PF_PACKETLow level socket 을 인터페이스를 이용합니다.
PF_IPX

IPX 노벨 프로토콜을 사용합니다.

int type: 데이터의 전송 형태를 지정하며 아래와 같은 값을 사용할 수 있습니다. 
typetype 내용
SOCK_STREAM

TCP/IP 프로토콜을 이용합니다.

SOCK_DGRAMUDP/IP 프로토콜을 이용합니다.
int protocol: 통신에 있어 특정 프로토콜을 사용을 지정하기 위한 변수이며, 보통 0 값을 사용합니다.
반환
-1 이외: 소켓 식별자
-1: 실패

TCP/IP 소켓 프로그램을 작성해 보겠습니다.

TCP/IP 통신 함수 사용 순서

domain : 소켓이 사용되는 네트워크의 영역을 정의

다른프로그램끼리 통신하는 것을 IPX 흔히 스타크래프트 할 때 사용된다.


type & protocol

type : 통신에 사용할 패킷의 타입을 지정

protocol : 통신에 사용할 프로토콜 지정

type에 따라서 protocol이 정해짐


SOCK_STREAM & IPPROTO_TCP : TCP기반의 통신에 사용

SOCK_DGRAM & IPPROTO_UDP : UDP기반의 통신에 사용

SOCK_RAW & (원하는 프로토콜) : RAW Socket으로 저수준에서 프로토콜을 직접 다룰 때 사용


socket 함수 반환 값

성공적으로 소켓을 만들면 0보다 큰 int 값을 반환

소켓지정번호, socket descriptor(소켓 서술자, 소켓의 정보를 담고 있다.)라고 부른다.

소켓을 지시하며, 이를 이용해서 소켓을 제어한다.

일반적으로 소켓을 생성하지 못하면 0보다 작은 값을 반환한다.


IP주소 : IP -> 아파트 위치

Port번호 : TCP -> 호실        = TCP/IP

통신하려면 포트번호와 보관용 메모리 필요 이를 구조체로 선언




INET : TCP/IP 를 이용한 인터넷 주소 패밀리

데이타 연결지향의 신뢰성이 높은 Stream(흔히 TCP 라고 하는)

서버-클라이언트 환경을 만들기 위한 과정을 서버측에서 보자면 다음의 과정을 거치게 된다.

    Socket 생성 -> Socket 에 이름연결 (bind)
    -> 클라이언트의 연결을 기다림(listen)
    -> 클라이언트를 받아들임 (Accept)
    -> 클라이언트의 명령을 받아서 적절한 서비스를 수행

클라이언트측에서 서버에 접근하기 위해서는 단순히 소켓을 생성후 서버에 연결(connect) 하기만 하면 된다.

    Socket 생성 -> 서버에 연결 시도(connect) -> 서버에 각종 명령을 전달가장 먼저 소켓을 생성해야 하는데 이는 socket(2) 함수를 이용하게 된다. 여기에는 3개의 매개 변수가 전달되는데, 각각 통신 도메인의 종류, 통신타입, 사용할 프로토콜을 지원하게 된다. 일반적인 인터넷 어플리케이션의경우 도메인종류로 AF_INET, 그리고 연결지향의 신뢰성 있는 통신을 위해서 SOCK_STREAM 타입을 사용한다.프로토콜은 특별히 지정된게 없으며, 그냥 0을 사용하도록 한다. socket 를 만들었으면 통신 환경에 맞게, sockaddr_in 구조체를 체워주게 된다. 이 구조체의 내용은 다음과 같다.
struct in_addr  
{           
    short int           sin_family;
    unsigned short int  sin_port;
    struct in_addr      sin_addr;
}           
sin_family 는 소켓타입이며, sin_port 는 연결에 사용되는 port(:12) 번호이고, sin_addr 은 연결을 받아들일 IP 어드레스이다. 예제에서는 INADDR_ANY 를 사용했는데, 이는 모든 IP에 대해서 연결을 받아들이라는 뜻이다.

socket() 이용해서 만든 소켓에 이름을 할당하여 실지로 어플리케이션이 사용가능한 상태로 만들어 줘야 하는데 이를 "소켓에 이름을 할당한다" 라고 하며 bind(2) 함수를 이용해서 구현한다. <br> 그다음에 listen(2)를 이용해서 연결을 기다리고, accetp(2) 를 이용해서 연결을 받게 된다. accept 를 이용해서 연결이 완성되면 accept(2) 는소켓과 연결되는 "파일 지시자"를 돌려주고 이 "파일 지시자" 를 통해서 클라이언트와 서버간의 메시지를 주고 받게 된다.

일단 연결이 이루어진다음에 서버가 하는일은 간단하다. 클라이언트의 문자열을 읽어들이고(지역이름), 파일에서 이 지역을 포함한 주소가 있는지 확인해서, 이를 클라이언트측에 전송해주면 된다. 주소검색이 모두 끝났다면 "end" 문자열을 클라이언트에 돌려줌으로써, 모든 검색이 끝났음을 클라이언트에게 알려준다.







connect 함수 메뉴얼



connect 함수의 사용 예

struct socketaddr_in_serveraddr


server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

serveraddr.sin_family = AF_INET;

serveraddr.sin_addr.s_addr = inet_addr("218.234.19.87");

serveraddr.sin_port = htons(8080);

//htons -> (host to network) int    (s) short

//ntohs -> (network to host) int    (s) short

client_len = sizeof(serveraddr);

connect(server_sockfd, (struct sockaddr *)&serveraddr, client_len);


Internet TCP/IP 통신

218.234.19.87 주소로 연결 요청

8080 포트에 연결된 프로그램을 요청

메모리의 크기도 알고있어야 한다.


연결 해보기!!!!


htons에 대한 메뉴얼

여러 가지가 나와 있는데 우리는 지금 htons를 쓰기 때문에 

//htons -> (host to network) int    (s) short

//ntohs -> (network to host) int    (s) short

자세한 설명은 나중에 하도록 하겠다.


inet_addr에 대한 메뉴얼

원래 stAddr.sin_addr.s_addr의 주소를 넣을 때 192.168.10.19 주소를 넣는다면 문자를 16진수로 바꿔서 넣기 때문에 


socket에 대한 메뉴얼



      1 #include <stdio.h>

      2 #include <sys/types.h>

      3 #include <sys/socket.h>

      4 #include <arpa/inet.h>

      5 #include <linux/types.h>

      6 #include <netinet/in.h>

      7 int main()

      8 {

      9         struct sockaddr_in stAddr;

     10         int iSoc;

     11         int iRet;

     12         iSoc = socket(AF_INET, SOCK_STREAM, 0);

     13         if(-1==iSoc)

     14         {

     15             printf("소켓을 생성할 수 없습니다.\n");

     16             return 0;

     17         }

     18         stAddr.sin_family = AF_INET;

     19 //소켓의 첫 번째 인자를 넣는다.

     20         stAddr.sin_port = htons(4000);

     21         stAddr.sin_addr.s_addr = inet_addr("192.168.10.19");

     22         iRet=connect(AF_INET,(struct sockaddr *)&stAddr,sizeof(stAddr));

     23         if(iRet!=0)

     24         {
     25             printf("연결에 실패하였습니다.\n");
     26             close(iSoc);
     27             return 0;
     28         }
     29         printf("iSoc : %d\n iRet : %d \n",iSoc, iRet);
     30         close(iSoc);
     31         return 0;
     32 }      

네트워크 서버에 접속이 되었음!!!!!!!(클라이언트 성공!!!!)