레이블이 socket인 게시물을 표시합니다. 모든 게시물 표시
레이블이 socket인 게시물을 표시합니다. 모든 게시물 표시

Python socket programming #4 - multiprocessing #1

Python에서 thread는 GIL(Global Interpreter Lock)을 따르게 됩니다.
간단하게 설명하면 보통은 다수의 쓰레드가 작동할 때에 아래와 같은 모델을 상상하게 됩니다.

하지만 GIL에 의해서 python은 한 Thread씩 Lock이 걸리는게 아니라 전체에 걸리게 되면서 여러개의 Thread가 Single Processor에서 동작 하는 것 처럼 동작하게 됩니다.



프로그램을 통해 살펴보면

위는 한 개의 Thread를 동작시켜 총 작업 시간을 출력한 프로그램입니다.
100000000의 숫자를 하나씩 빼면서 while문을 돌리는 간단한 프로그램입니다.
출력 결과는 아래와 같습니다.

---------------------------------------------------
Starting Thread_1
Thread_1 의 작업 시간 8
Exiting Thread_1

Exiting Main Thread
총 작업 시간 8
---------------------------------------------------

또 다른 프로그램을 살펴보면

위는 두 개의 Thread를 동작시켜 총 작업 시간을 출력한 프로그램입니다.
기본적으로 생각했을 때엔 위에서 살펴본 하나의 Thread의 작업 시간인 8초와
동일한 시간을 생각 해 볼 수 있습니다. 하지만 결과는 아래와 같습니다.

---------------------------------------------------
Starting Thread_1
Starting Thread_2
Thread_2 의 작업 시간 15
Exiting Thread_2

Thread_1 의 작업 시간 16
Exiting Thread_1

Exiting Main Thread
총 작업 시간 16
---------------------------------------------------

생각했던 것과는 다르게 하나의 Thread의 작업 시간 8초의 두 배인 16초가 출력 되는 것을 보실 수 있습니다.

위와 같은 이유로 python에서는 thread보단 multiprocessing이 권장되어집니다.

1. Multiprocessing

python에서 사용하는 multiprocessing은 fork()와 같게 생각하시면 됩니다.
하나 이상의 자식 process를 생성하여 이를 병렬구조로 처리하는 것을 지칭합니다.

import multiprocessing
위와같이 모듈을 불러와 사용 할 수 있습니다.

2. Pool

p = Pool(n)
위의 Pool(n)을 통해 미리 process를 생성 할 수 있게 됩니다.

p.map(function, values)
위의 Pool.map을 통해 각 process들이 함수를 동작 시키게 할 수 있습니다.

multiprocessing의 pool을 이용한 예제를 통해 살펴보겠습니다.

위의 예제는 pool을 통해 3개의 process를 생성하고, f(x)함수를 불러와 1초씩 함수에서 대기하게 됩니다. 결론적으로 3개의 process가 10개의 값을 연산하려면 아래와 같이 총 4초가 걸리게 됩니다.

프로세서     값        연산시간      누적 연산시간
process 1 -> 0 -------- 1초  -------- 1초
process 2 -> 1 -------- 1초  -------- 1초
process 3 -> 2 -------- 1초  -------- 1초
------------------------------------------------------
process 1 -> 3 -------- 1초  -------- 2초
process 2 -> 4 -------- 1초  -------- 2초
process 3 -> 5 -------- 1초  -------- 2초
------------------------------------------------------
process 1 -> 6 -------- 1초  -------- 3초
process 2 -> 7 -------- 1초  -------- 3초
process 3 -> 8 -------- 1초  -------- 3초
------------------------------------------------------
process 1 -> 9 -------- 1초  -------- 4초


process를 4개로 늘리거나, range를 (0,9)로 수정한다면 누적 연산시간이 3초가 걸리는 것을
확인 하실 수 있습니다.

아래는 출력 결과입니다.

값 0 에 대한 작업 Pid =  3712
값 1 에 대한 작업 Pid =  3740
값 2 에 대한 작업 Pid =  2568
값 3 에 대한 작업 Pid =  3712
값 4 에 대한 작업 Pid =  3740
값 5 에 대한 작업 Pid =  2568
값 6 에 대한 작업 Pid =  3712
값 7 에 대한 작업 Pid =  3740
값 8 에 대한 작업 Pid =  2568
값 9 에 대한 작업 Pid =  3712
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
총 작업 시간 4

Python socket programming #3 - MultiThreading (Threading module)

2. threading


두 번째로 threading 모듈은 thread 클래스를 상속받아 사용하는 모듈입니다.

클래스가 threading.Thread를 상속받아 사용하게 됩니다.

java와 마찬가지로 run 메소드를 오버라이드 하여 각 각 선언된 클래스를 통해

Class_name.start()를 작동시키면 run 메소드가 작동하게 됩니다.

위의 코드를 보면

class myThread (threading.thread):를 통해 클래스를 선언 한 뒤

생성자를 통해 thread의 name과 delay time을 인자로 받습니다.

이 후 thread1.start()를 통해 run 메소드를 작동시킵니다.

run 메소듣 내부에서는 print_count함수를 호출하여 5회 각 각 1초와 2초의 딜레이를 부여해 내용을 출력합니다.

아래는 출력 내용입니다.

Starting Thread_1
Exiting Main Thread
Starting Thread_2
Thread_1: 5
Thread_2: 5Thread_1: 4

Thread_1: 3
Thread_2: 4
Thread_1: 2
Thread_1: 1
Exiting Thread_1
Thread_2: 3
Thread_2: 2
Thread_2: 1
Exiting Thread_2

Python socket programming #2 - MultiThreading (Thread module)

파이썬에서의 멀티 쓰레딩은 두 가지의 모듈을 사용해 만들 수 있습니다.

각각의 모듈은 아래와 같습니다.


  • Thread
  • Threading


먼저 Thread는 C와 같이 함수를 이용해 Thread를 run 시키는 방식으로 MultiThreading을 구현합니다.

Threading은 흔히 자바에서 보던 방식처럼 Thread관련 클래스를 상속받아 run을 override 하여 사용합니다.

이 포스팅에선 먼저 Thread만 다루도록 하겠습니다.

1. Thread


기본적으로 Thread에서 thread를 run시키는 함수는 아래와 같습니다.

thread.start_new_thread( function, (argument))

아래 소스코드를 확인 해 보겠습니다.

위의 print_num함수에 threadName과 delay인자를 준 뒤

delay 시간만큼 텀을 두고 숫자를 1~5까지 출력하는 함수 입니다.

이 함수를 각 각 1초와 2초로 설정하여 두개의 쓰레드를 작동 시키는 프로그램입니다.

출력값은 synchronizing이 되어있지 않아 값이 겹쳐 나올 수 있게 됩니다.

마지막의 while은 Main Thread를 종료시키게 되면 생성된 2개의 쓰레드도 죽게 되므로

이를 막기위해 Main Thread에 무한루프를 걸어줍니다.

아래는 출력값 입니다.

Thread_1: 1
Thread_2: 1Thread_1: 2

Thread_1: 3
Thread_2: 2
Thread_1: 4
Thread_1: 5
Thread_2: 3
Thread_2: 4
Thread_2: 5

Python socket programming #1 - basic server and client

python의 소켓은 C나 자바에 비해 비교적 단순한 모습을 취하고 있습니다.

Socket의 선언은 아래와 같습니다.

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

여기서 각각 PF(프로토콜 체계)와 AF(주소 체계)를 살펴보면

A. PF

  • PF_INET - IPv4 프로토콜
  • PF_INET6 - IPv6 프로토콜
  • PF_LOCAL - Local 통신 UNIX 프로토콜
B. AF
  • AF_INET - IPv4 프로토콜
  • AF_INET6 - IPv6 프로토콜
  • AF_LOCAL - Local 통신 UNIX 프로토콜
이를 통해 소켓을 만들 때에 사용하고자 하는 프로토콜을 사용할땐 PF를 주소 구조체 안에 주소 체계를 정의할 때엔 AF를 사용합니다.

기본적으로 C에서 socket을 생성 할 때 PF_INET을 사용하지만, Python에는 PF가 없습니다.

그리고 뒤에오는 SOCK_STREAM과 SOCK_DGRAM을 통해 각 각 TCP UDP를 결정하게 됩니다.


Socket에 대한 binding은 아래와 같이 선언합니다.

s.bind((host, port))

위와 같이 소켓 선언과 바인딩이 타 언어에 비해 아주 심플한 모습을 취하고 있습니다.

소켓을 통해 간단하게 값을 한 번만 주고 받는 에코서버를 작성해보도록 하겠습니다.

1. Server


서버에서 원격지의 소켓을 받기 위한 listen은 아래와 같이 선언합니다. 이 부분은 C의 서버 프로그래밍과 유사합니다.

s.listen(5)

listen을 통해 소켓이 개통이 되는데 뒤의 인자 backlog는 커널이 관리하는 대기 큐에서
연결 요청을 하고 기다리는 총 클라이언트의 수 입니다.

위 큐에 연결 요청한 소켓은 SYN_RCVD나 ESTABLISHED 상태로 대기합니다. 이 함수를 호

출하면 커널은 내부적으로 소켓의 상태를 CLOSED에서 LISTEN으로 바꿔줍니다.

Status

  • SYN_RCVD - 클라이언트가 connect 함수로 서버에 요청한 연결을 처리하기 위해 SYN 패킷을 통한 3-Way Handshaking을 진행하는 중입니다.
  • ESTABLISHED - 3-Way Handshaking이 모두 완료되면 이 상태가 됩니다.

마지막으로 클라이언트의 연결을 위한 accept 함수는 아래와 같습니다.

c = s.accept()

위를통해 클라이언트 소켓을 Accept 할 수 있습니다.

이제 send와 recv를 통해 값을 주고 받을 수 있습니다.

socket.send("connect success")

socket.recv(1024)

서버 소스는 아래와 같습니다.


먼저 socket.socket()를 통해 소켓을 생성 한 뒤

9999번 포트의 루프백 ip 127.0.0.1로 bind 해줍니다.

s.listen(5)와 s.accept()를 통해 클라이언트 소켓을 연결 한 뒤

send를 통해 'Connection success'를 전송 해 줍니다.

클라이언트가 들어오기 전 c, addr = s.accept()에서 대기중인 실행 모습은 아래와 같습니다.

listen ok


2. Client


Client 또한 마찬가지로 socket.socket()를 통해 소켓을 생성 해 줍니다.

그리고 아래와 같이 connect() 함수를 통해 해당 ip 127.0.0.1과 9999번 포트에 소켓을 연결 해 줍니다.

s.connect((host, port))

그리고 recv를 통해 상대방의 send를 기다립니다.

s.recv(1024)

서버와 클라이언트의 실행 결과는 아래와 같습니다.

-----------------------Server-----------------------
listen ok
connection ('127.0.0.1', 2617)
-----------------------Client------------------------
Connection success
-----------------------------------------------------