어제 하던 통신프로그램을 보완해서

그럴싸한 통신을 하는 프로그램으로 업그레이드 했다.



(while 문을 이용해 메세지를 종료할 때 까지 통신하는 코드)


(메세지를 주고받는 모습)


(맨 아래줄에 보면 외부 IP주소와 포트번호가 나와있다.)



위 화면에서 외부주소 맨 아래를 보면 상대 IP주소와 포트번호가 나와있다.

코드에 적힌 대로 IP주소와 포트번호로 연결된것을 확인할 수 있다.

현재, 이 프로그램은 동기식 통신이다. 즉 한번 보내고 한번 받고 해야하는 방식이다.

여기서,

상대와 보통 채팅처럼 비동기식으로도 만들 수 있다.
내가 보내지않아도 상대가 마음껏 보낼 수 있는 상태로.

그러려면
Thread를 이용해야한다.

하나는 send만 하고 하나는 recv만 하도록 쓰레드를 열어서 실행해야한다.

하지만 현재 우리는 네트워크를 이해하는 차원에서
여기 까지 하겠다.

우리가 패킷을 분석하기 위해 만드는 프로그램이기 때문이다.

우리가 위에서 보낸 메세지나
네트워크에서 주고받는 데이터는
전달하기위해 여러가지가 붙어서 패킷을 만들어 전달된다.

이런 규칙들이 프로토콜인데
4계층의 TCP, UDP를 알아보겠다.

먼저 파이썬 코드로 어떻게 동작되는지 두 프로토콜의 차이점을 봐야한다.

동작 과정

* TCP server
 1. socket()
 2. bind()
 3. listen()
 4. accept()
 5., send(), recv()

* TCP Client
 1. socket()
 2. connect()    -> server의 accept와 연결된다.
 3. send(), recv()

* UDP server
 1. socket()
 2. bind()
 3. sendto() ,recvfrom()

* UDP server
 1. socket()
 2. sendto(), recvfrom()
-> send()를 못쓰는 이유는 send는 연결되어있는 곳으로 보내는건데
UDP는 연결된게 없으므로 sendto()로 그때그때 어디로 보내는지 적어줘야한다.

UDP의 특징
-> 연결과정 없이 바로 보내고 받는다.
-> 데이터가 갈수도있고 안갈 수도 있다. 그래서 TCP는 신회성이 있는 통신이다. 왜냐하면 안갔는지 갔는지 체크하기 때문이다. TCP는 체크하지만 UDP는 체크하지 않는다.
-> UDP는 신뢰할 수 없는 통신이다. ( 이말은 상대가 받았는지 체크할 수 없다는 뜻)
-> 인증 절차도 없다. 그냥 보내고 그냥 받는다.
보통 UDP를 사용하는 프로그램은 보통 인증과정이 없다.
UDP자체로는 인증을 할 수 없다.

그렇다면 왜 쓸까?
  위 말만 들으면 왜쓰는 걸까 싶지만 UDP의 큰 장점은 복잡하지않고 빨리빨리 보낼 수 있다는 것이다. 보내는 데이터가 크지 않다면 UDP가 적절하다. UDP는 에러체크등이 없고 그냥 보내기만 하는 것이다. 그래서 사실 예전에는 많이 쓰이지 않았다. 하지만 요즘 많이 쓰이고 있는것이 실시간 통신이 필요할 때 특히 UDP를 많이 쓴다. 빨리보낼 수 있고 에러 났다고 다시 보낼 필요가 없는 실시간 통신에 적합하다. 혹은 짧은 데이터 같은 경우가 적합하다.

- netstat -anp UDP
입력하면 UDP 포트 정보를 볼 수 있다. 여기에 상태 정보가 없다. 연결과정이 없기 때문이다.


(상태 부분이 없다.)


파이썬에서 UDP에 사용되는
recvfrom() 함수.
-> 받아온 내용보면 클라이언트 정보도 같이 출력된다.


(맨 아래 데이터와 데이터가 날라온 IP주소와 포트정보)


-> 튜블정보이므로 ip주소와 포트번호를 따로 입력받아서 출력해 봤다.


(튜플정보를 따로 받아와 출력한 모습)


TCP, UDP  -> Layer 4 (4계층)에 속한다.
-> 전송계층 (Transport Layer)

* PDU : Protocol Data Unit


-> packet : 패킷, 3계층에서 사용되는 단위
    근데 그냥 보내는 데이터 덩어리를 패킷이라고도 한다.


L4에서는 pdu가 segment 다.

-> 주소 체계 : Port번호

-> 각 계층별 사용하는 단위와 주소가 다르다.


패키징
-> Hello 데이터를 패키지화한다. 포장하듯이.
-> UDP 헤더를 붙인다. 데이터 앞에

헤더에 어떤 정보들이 들어가있는지 정해논다 -> 규약 Protocol

프로토콜에 맞게 데이터를 패키징해서 다음 계층으로 보낸다.



UDP 헤더에는 어떤 정보가 들어있을가?


UDP Header

 - dst Port 번호, src Port 번호
 - data length, checksum



-> 직접 확인해본다.

(먼저 패킷 데이터를 덤프받을 프로그램을 만들었다.)


socket.gethostname() 을 이용하면 컴퓨터의 이름을 얻어올 수 있다.

gethostbyname() 을 이용하면 이름을 가지고 호스트 정보를 가져올 수 있다. (IP정보)


-> 여기서 포트번호에 0을 쓴이유는 특정포트를 보기위해 하는게 아니라 특정 IP에 모든 포트들을 확인하는 것이다.


(여러번 실행한 경우)


뭔지 모를 데이터들이 오고가는 모습이다.


그러면 우리가 오늘 바로 작성한 UDP로 패킷을 보내고 이 패킷의 내용을 확인해보자.


(코드)


위 코드를 보면 info[0]에는 IP정보가 들어가므로 IP주소가 내꺼인 주소만 보겠다는 것이다.

이걸 안하면 이것저것 위에서 본거 처럼 구별하기가 힘들어서 그렇다.


이제 내가 UDP로 패킷을 보내고 이 프로그램으로 확인 해 볼 것이다.

보낼 데이터 : Hello


(출력 화면)


그림은 클릭하면 크게 보인다.


출력화면을 보면 끝에 Hello라고 적혀있는것이 보인다.

그리고 그 앞부분은 헤더의 내용이다.

UDP는 헤더에 많은 내용이 들어간다고 안햇는데 왜이렇게 길지???..


그건 UDP헤더만 붙는게 아니기 때문이다.


아래 계층으로 내려가면서 패키징되는데 점점 헤더가 붙어서 늘어나기 때문이다.


이제 앞으로 이 헤더 정보, 데이터 정보를 구별하여 읽는 법을 공부할 것이다.


다른 패킷을 분석하여 어떤 정보가 오고가는지 확인할 수 있게 될 것이다.







네트워크란


- 떨어져 있는 Host들간의 연결 혹은 연결되어있는 집단을 말한다.


네트워크의 기원


ALPANET -> 정부, 학교, 기업들간에 서로 데이터공유를 원활하게 하고자 해서 시작된 프로젝트이다.
              ->  이 때, router등 네트워크 장비들이 개발되었다.


그렇다면
네트워크에서 어떻게 데이터를 주고받을까?


-> 여러가지 프로토콜(규칙들)을 통해서 주고받는다.


네트워크에서 통신하는 모습을 오늘 확인해 볼 것이다.


네트워크의 구조부터 봐야한다.


* 네트워크 구조는 OSI 7 Layer (표준)로 이루어져있다.


크게 보면 아래와 같다.


1~4 계층
 - 하위 4계층이라고 한다.
 - 물리적인 전송 방법에 관해 역할을 담당한다.
 - 통신규약(프로토콜)
     -> TCP, UDP, IP, ARP, ETHERNET, ...  (데이터를 전달하는 방법들)


5~7 계층
 - 상위 3계층이라고 한다.
 - 논리적인 규칙에 따라 전송된다.
 - HTTP, FTP, Mail,... (프로토콜)
 - kakao talk, game.. (비표준프로토콜) -> 외부에 공개되지 않는 규약들

먼저, 하위 4계층을 이용해 문자를 전송해보자.
파이썬을 이용해 1~4계층으로 메세지를 직접 주고받는 통신을 해볼 것이다.
파이썬에서 통신을 하기 위해 필요한 내용들이다.


* 파이썬 소켓 프로그래밍

-> 우리가 하려는 것이 네트워크 상에서 데이터를 주고받는 모습을 확인하려는 것이다.
    그러기 위해서는 네트워크 프로그래밍을 해야한다. 네트워크 프로그래밍을 소켓 프로그래밍이라고도한다.
-> 네트워크 프로그래밍? : 통신을 위한 프로그래밍으로 소켓을 이용한다. 그래서 소켓프로그래밍이라고 하는 것이다.

 - socket? 소켓을 이해하기 위해 다음것들을 먼저 보는 것이 도움이 된다. 


* 입/출력 방식

 - 표준 입/출력 : input(), print()   (키보드, 모니터) (키보드, 모니터로 이루어지기 때문에 다른 식별자가 없다.)
 
- 파일 입/출력 : 파일 식별자가 필요하다. (어떤 파일을 열었는지 알아야하기 때문에 식별자가 필요하다.)

  - open ( 파일을 가져온다 )
   a, w, r    (추가, 쓰기 읽기전용)
   a -> 있으면 거기다 쓰고 없으면 새로 만든다.



(open() 을 이용해 파일입력해보는 코드)


(이렇게 바탕화면에 text 파일이 생긴다.)



핸들러 -> 파일 변수. 파일을 다룰수있는 변수


(핸들러 내용)


open 되있으면 파일이 열려있는것이다. 이 때 삭제를 누르면 열려있다고 삭제가 안된다. (열려있다는거 확인할 수 있다.)
close로 닫을 수 있다.


(Python 에서 close()를 하지 않은 경우)


(close() 를 해야 열려있던 파일이 종료된다.)


write -> 화면에는 입력한 문자의 길이가 표시된다.

* 파일을 다룰 때는 식별자를 주의해야한다.


(파일에 문자를 입력해본 결과(11이라고 적힌건 입력된 문자수를 출력한다.)


파일 접근 방법
ex) fd = open("파일 경로", "모드")

네트워크 입/출력도 파일 입/출력과 비슷하다고 생각하면 된다.
파일은 open으로 접근했다면 네트워크는 socket으로 접근한다고 생각하면 된다.

 - 네트워크 입/출력 : socket (파일의 디스크립터와 같은 맥락) 소켓은 식별자이다.
  ex) 서버는 여러개의 소켓꽂을 자리를 준비하고있다. 우리는 소켓을 들고가서 콕 꼽는다.
 -> 네트워크 프로그래밍을 소켓프로그래밍이라고도 한다.
 먼저 소켓을 생성해줘야한다. (파일입출력할때 파일을 생성한것처럼.)

1. socket 생성
 - socket module
 - 소켓 인자 : (1. 인터넷 타입, 2 통신 타입,  3. 프로토콜)

*인터넷 타입
 - AF_INET  (IPv4)
 - AF_INET6
 ...
 - AF_PACKET

* 통신 타입
 - 통신하는 타입
 - SOCKET_STREAM  (TCP)
 - SOCK_DGRAM     (UDP)
 - SOCK_RAW

* 프로토콜 -> defalt 값은 0이다. (안적어도 된다.)

출력해보면 볼수 있는데 fd가 중요하다. 소켓마다 번호가 다르다.
하나 더 만들면 번호가 다르게 생성된다.


(소켓 생성 모습)


* 네트워크 타입

 1. 서버-클라이언트 ( 1: N )
 2. Peer To Peer ( 1 : 1 )  (카톡, 채팅)
    -> 알고보면 이것도 서버-클라이언트다
    -> 누군가는 서버가 되야하고 클라이언트되야한다.
     -> 누군가는 반드시 전화를 먼저 걸어야한다. (전화를 예로)

윈도우즈 위에 여러개의 프로그램이 있다. 그래서 포트라는 개념이 필요하다.
Port 번호를 가지고 프로그램을 식별한다.
포트 번호는 ( 1~ 65535번 ) 까지 사용한다.
1~1024번 포트는 well-known 포트번호라고 한다. 조금 공식적인 포트 번호
1~10000번 까지는 비우는게 좋다. (요새는 통신하는게 많아져서)

* IPv4, IPv6 
 - IP Address : 네트워크상에서 호스트를 식별할 수 있는 식별자(식별번호)

ipv4
 - 4자리의 정수를 '.' 으로 구분해서 표시한다.
 - 각 정수의 범위는 (0~255) 이다.

ipv6
 - 6자리의 16진수 정수를 가지고 주소를 표현한다.
 - 0x00 ~ 0xff (255개)

커맨드창에 아래와 같이 입력하면
netstat -anp tcp
-> 현재 열려있는 포트들을 보여준다.



(netstat -anp tcp 명령어)


(포트 번호 50000번을 열어둔 상태)



(포트번호 50000번이 열려있는 것을 확인할 수 있다.)


이제 간단한 통신을 하는 프로그램을 만들어 볼것이다.

동작 방식은

* 서버 프로그램의 동작방식
1. 소켓을 생성한다.  -> socket()
2. 포트를 오픈한다.  ->bind()   : 튜플 형태로 하나의 인자. 튜플안에 뒤는 포트번호
3. 네트워크의 상태를 Listen 상태로 변경한다.  -> listen()  괄호 안에 연결 개수를 적기도한다.
4. 클라이언트의 연결 요청을 기다린다.     -> accept()  : 클라이언트 소켓을 리턴해준다.

* 클라이언트 프로그램의 동작방식
1. 소켓을 생성한다.   -> socket()
2. 서버에 연결한다.   -> connect()  : IP주소와 포트번호를 튜플형태로 적어준다.



(통신 프로그램 코드, connect()는 빠져있다.)


(서버 측 출력)


위 코드를 보면 서버 모드일 때 연결이 되면 연결되어있는 정보가 출력되는 것을 확인 할 수 있다.
리턴되는 값은 클라이언트 소켓 정보이다.

연결된 이후에는 send, recv 함수를 통해서 데이터를 송/수신 할 수 있다.

그러면 이제 메세지를 주고받겠다.



(문자열 abcde를 보내는 클라이언트 모드)



(에러가 났다.)

문자열을 그대로 보내면 파이썬은 처리를 못한다.
그래서 .encode()를 하면 파이썬이 적절하게 인코딩을 해서 보내주게 된다.


(서버에 입력이 들어온 모습)


내가 클라이언트 모드일때 abcde를 보냈고
내가 서버모드로 하고 옆사람이 클라이언트모드로 내게 Hello를 보낸 것을 확인 할 수있다.

출력 결과를보면 첫번째 꺼는
b' Hello라고 적혀있고
아래꺼는
Hello 라고 적혀있다.

첫번째꺼는 데이터 받은 것을 디코딩하지 않고 바로 출력한것이다.
보면 바이트 표시가 되있는 것을 볼 수 있다.

두번째 꺼는 .decode()를 한 모습이다.










함수를 정의하고 내용을 입력하지 않으면 에러가 난다.

돌아가는지 확인해보고싶으면 pass를 쓰면 넘어간다.


(함수 내용이 없는 경우 에러)


(pass를 적으면 에러없이 넘어간다.)


* 파이썬에서의 함수
1. 입력된 문자열이 실수로 이루어져있는지를 판단하는 함수

def isfloat( data ):

입력 : 임의의 문자열
출력 : True of False
           - 실수로 변경가능한 문자열이라면 True
           - 그렇지 않으면 False

2. 입력된 문자열이 음수로 이루어져있는지를 판단하는 함수

def isneg( data ):

입력 : 임의의 문자열
출력 : True of False
         - 음수로 변경가능한 문자열이라면 True
         - 그렇지 않으면 False


(함수 코드)


(결과)


*라이브러리 : 이미 만들어져있는 함수이다.

* 파이썬 모듈
 - 파이썬 코드로 이루어진 파이썬 파일
 - 사용하고싶은 함수들을 하나의 파일에 만들어 두는 것이다.
   그 후 사용하고 싶으면 import한다.


 - 어디서나 import 하고 싶으면
 -> 파이썬3의 라이브러리 파일들의 경로가 따로 있다. 이 곳에 넣어둬야한다.


(내가 만든 모듈을 라이브러리에 추가한 모습)


import 방법
import 파일
from 파일 import *      -> 이렇게 적으면 모듈 이름을 안적어도 된다.
from 파일 import 특정함수    -> 특정 함수만 import할 수 있다.

왠만하면 import 파일   형식으로 하는게 좋다.
왜냐하면 두세번째로 하다가 함수 이름이 겹칠 가능성이 있을 수 있어서 그렇다.
두세번째는 편하기는 하지만 충동할 수 있다는걸 주의하고 해야한다.
보통 모듈파일에는 실행코드는 적지 않는다.
실행코드가 있다면 import 되는 순간 파일이 실행된다.

만약 모듈을 만들고
이 모듈이 잘 돌아가는지 테스트코드를 넣어볼 수 있다.

만약 실행코드를 안삭제하고 import하고 실행시키면
import 될때 모듈파일의 테스트코드가 실행된다.
만약 지우지않고 쓰고싶다면
if __name_- == "__main__":
    main()
이코드를 넣으면 이 해당파일이 단독으로 실행되면 실행되지만
import되는 경우에는 코드가 실행되지 않는다.
-> 단독으로 실행하는 경우 실행코드들이 실행되고
   import되는 경우에는 실행되지 않는다.



(모듈 파일에 테스트코드를 추가한 모습)



(모듈을 import 한 순간 테스트코드가 실행되는 모습)



(__name__ 을 모듈에 추가해서 실행시 테스트코드가 실행되지 않는 모습)



* 프로그램 작성

1) 안전한 제곱근을 구하는 함수

math 모듈에 sqrt() 함수로 제곱근을 구할 수 있다.
 - 숫자가 아니거나 음수가 입력되면 에러가된다.
 
알고리즘
1. 사용자로부터 값을 입력받는다.
 
 - 입력값의 형태는 실수여야 한다.

2. safe_sqrt 함수 인자로 입력값 전달
 3. 입력된 값이 허용범위의 값인지 판별 허용범위라면 실수로 변환 후에
    sqrt 함수를 이용해서 제곱근을 구한 후 출력
 4. 허용된 입력값의 범위가 아니라면 다시 입력받을 수 있도록 한다. 
 5. 3번으로 돌아간다.


(만들어본 코드(좌), 실행화면(우))


2)  대출 상환금 계산 프로그램

- 입력 :
 대출 원금
 대출 기간
 대출 이율

- 출력 :
 연상환금
 월상환금
 총상환금

- 연상환금 계산 공식

                   (1 + 대출 이율)^대출 기간 + 대출 원금 + 대출 이율
연상환금 = ---------------------------------------------------------------
                                  (1 + 대출 이율)^대출기간 - 1

1) 대출 원금과 대출 기간, 대출 이율을 각각 입력 받는다.
 - 대출원금과 대출 기간은 자연수의 입력만 허용하며 대출 이율은 실수 입력만 허용할 수 있도록 한다.
2) 입력값에 맞춰서 연상환금을 계산한다.
3) 연상환금과 월상환금, 총상환금을 출력
4) 프로그램을 계속 진행할 것인지를 입력받는다.
 a-1) "yes"를 입력한 경우 1)번으로 돌아가서
       대출 원금과 대출 기간, 대출 이율을 새로 입력
 b-2) no 를 입력한 경우 프로그램 종료
 4-3) yes no 이외의 값이면 새로 입력을 받는다.
* 소수점이하를 버리고 싶으면 int 형으로 형변환 하면한다.
* 반올림 하고 싶으면 round 함수를 이용하여 형변환한다.



(소수점 그대로 출력)



(소수점까지 출력되는 모습)




(작성해본 코드, 결과를 int형으로 형변환 한 모습 : 소수점 자리 버리는 모습)




(소수점 자리 버림 모습)












+ Recent posts