[컴][파이썬] 간단한 python network programming

simple python network programming / python server programming / raw socket



python network programming

간단한 server / client code

ref. 1 의 server.py / client.py code


server.py

tcp
#!/usr/bin/python           # This is server.py file

import socket               # Import socket module

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)         # Create a socket object
host = socket.gethostname() # Get local machine name
port = 12345                # Reserve a port for your service.
s.bind((host, port))        # Bind to the port

s.listen(5)                 # Now wait for client connection. max acceptable connection is 5
while True:
   c, addr = s.accept()     # Establish connection with client.
   print 'Got connection from', addr
   c.send('Thank you for connecting')
   c.close()                # Close the connection

udp
import socket

UDP_IP = "127.0.0.1"
UDP_PORT = 5005
MESSAGE = "Hello, World!"

print "UDP target IP:", UDP_IP
print "UDP target port:", UDP_PORT
print "message:", MESSAGE

sock = socket.socket(socket.AF_INET, # Internet
                    socket.SOCK_DGRAM) # UDP
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))

client.py

tcp
#!/usr/bin/python           # This is client.py file

import socket               # Import socket module

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)         # Create a socket object
host = 'DESKTOP-P0EE8AG'    # Get local machine name, 'socket.gethostname()' is possible
port = 12345                # Reserve a port for your service.

s.connect((host, port))
print s.recv(1024)
s.close                     # Close the socket when done


udp
import socket UDP_IP = "127.0.0.1" UDP_PORT = 5005 sock = socket.socket(socket.AF_INET, # Internet socket.SOCK_DGRAM) # UDP sock.bind((UDP_IP, UDP_PORT)) while True: data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes print "received message:", data


socket 만들기

보통 컴퓨터 통신에서 socket API 를 사용해서 통신을 하게 된다. 이 socket 에서 어떤 방식으로 통신을 할 것인지(protocol ) 를 정하고, 어디로 통신할 것인지를 정하게 된다.(address)

아래에서 parameter 를 한 번 확인해 보자.
  • s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)         # Create a socket object

AF_INET

AF_INET 은 internet 을 사용하는 socket 을 열겠다는 뜻이다. AF_INET 은 ref.5 를 보면 알겠지만, 아래와 같은 뜻이다. address Family 는 큰 뜻을 두지 말자. 굳이 의미를 부여하자면, 주소를 사용하는 녀석들 뭐 이런 느낌이다. 그러니까 AF_INET 은 internet 주소 체계를 사용하겠다. 뭐 이정도 의미이다.

AF : address Family
INET : internet

그런데 socket 에게 주소체계를 이야기 하는 것은 좀 이상하다. AF_INET 은 주소체계와 함께 그 주소체계를 사용하는 protocol 도 같이 이야기 하는 것이라 생각하면 된다. 이 부분은 아래 "PF_INET" 부분을 읽어보자.


PF_INET

ref. 5 를 보면, PF_INET 이라는 녀석이 존재하는데, socket 의 parameter 로 원래는 PF_INET 을 주는 것이 의미상은 맞다고 이야기한다.

그러니까 원래 socket library 를 만들때는 AF_INET 은 sockaddr_in 이라는 struct 에 "address 가 어떤 종류이다." 를 표현하기 위해 정의 해 놓은 것이고, PF_INET 은 address 체계와는 분리돼서 사용될 줄 알고 이런식으로 만들어 놓았다고 한다.

하지만 실제로는 protocol 에 address(주소) 의 정의가 같이가는 식이기 때문에 그런식으로 사용되지 않는다.[ref. 5] 그러니까 127.0.0.1 같은 녀석을 TCP/UDP 가 사용하는데, TCP/UDP 말고 다른 protocol, 예를 들면 PF_UNIX , 에 쓸 수 있을 것이라 본 것이다.(제대로 이해한 건지 확실치 않으니, 자세한 이야기는 ref. 5 를 확인하자.)

여하튼 그래서 그냥 대부분의 경우 AF_INET 을 쓰면 된다고 한다.


SOCK_STREAM / SOCK_DGRAM

뒤에 sock_stream 은 AF_INET(PF_INET) 이라는 protocol 을 사용하는데, 그 중에 stream 처럼 사용할 수 있는 socket 을 사용하겠다는 의미다.  그러니 AF_INET 에서는 TCP/IP 를 구현했다. 그리고 SOCK_DGRAM 은 UDP 를 구현했다.(ref. 4 )



socket.sendto <----> socket.bind/socket.recvfrom

이제 socket 은 어떤 식으로 통신할지를 정했다. 이제는 그냥 socket 을 "전달통로" 로 이용해서 그냥 data 를 보내고 받으면 된다.

간단한 udp 부터 보자. udp 는 일단 흔히 알다시피 connection-less 이다. 굳이 둘간에 연결을 맺을 필요가 없다. 그냥 그 주소로 data 를 보내면 된다. 그래서 sendto(data, (host,port)) 로 단순하게 처리할 수 있다.

마찬가지로 server 쪽도 bind 를 한 이후에 바로 recvfrom 으로 간단하게 처리할 수 있다.


socket.connect/write<----> socket.bind/socket.listen/socket.accept/socket.read

tcp 는 조금 일이 더 있다. 이녀석은 일단 connection (연결)을 맺어야 한다. 그래서 client 에서 connect 를 해주게 된다.
  • s.connect((host, port))

서버에서는 연결을 맺기 위해서 bind 를 한 이후에 listen 을 하고 있는다. listen 을 하게 되면, listen queue 를 하나 만들게 되는데, 이 queue 에서 client 로 부터 오는 connection 들을 받게 된다.[ref. 8]

그리고 accept 에서 이 queue 에 있는 connection 을 하나 가져와서 그 녀석이 보낸 정보에 맞춰서 client 와 통신할 socket 을 만들어 주는 역할을 한다.[ref. 9]

이렇게 양쪽에서 작업이 끝나면 연결이 맺어진 상태가 된다. 이제는 양쪽에서 socket 을 이용해서  read/write 를 하면 된다.




select()

server.py

server socket 이 input 에 있고, 이녀석에서 accept 를 통해 client socket 을 만들면,
이 client socket 도 input 에 넣어서 select 로 event 를 기다리게 된다.(inputs.append(connection))
그리고 이 client socket 에서 data 를 받은 후에 이 client socket 을 output 에 넣어서 select 에 의해 처리될 수 있게 한다.


source from : https://pymotw.com/2/select/
import select
import socket
import sys
import Queue



# Create a TCP/IP socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(0)

# Bind the socket to the port
server_address = ('localhost', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
server.bind(server_address)

# Listen for incoming connections
server.listen(5)

# Sockets from which we expect to read
inputs = [ server ]

# Sockets to which we expect to write
outputs = [ ]

# Outgoing message queues (socket:Queue)
message_queues = {}

while inputs:

    # Wait for at least one of the sockets to be ready for processing
    print >>sys.stderr, '\nwaiting for the next event'

    #readable, writable, exceptional 중 어느 하나에서 event 가 발생하면 return 된다.
    #reading 할 준비가 되거, writing 할 준비가 되거나, exception condition 이 되거나
    #하면 event 가 발생한다.

    readable, writable, exceptional = select.select(inputs, outputs, inputs)

    # Handle inputs
    for s in readable:

        if s is server:
            # A "readable" server socket is ready to accept a connection
            connection, client_address = s.accept()
            print >>sys.stderr, 'new connection from', client_address
            connection.setblocking(0)
            inputs.append(connection)

            # Give the connection a queue for data we want to send
            message_queues[connection] = Queue.Queue()
        else:
            data = s.recv(1024)
            if data:
                # A readable client socket has data
                print >>sys.stderr, 'received "%s" from %s' % (data, s.getpeername())
                message_queues[s].put(data)
                # Add output channel for response
                if s not in outputs:
                    outputs.append(s)
            else:
                # Interpret empty result as closed connection
                print >>sys.stderr, 'closing', client_address, 'after reading no data'
                # Stop listening for input on the connection
                if s in outputs:
                    outputs.remove(s)
                inputs.remove(s)
                s.close()

                # Remove message queue
                del message_queues[s]

    # Handle outputs
    for s in writable:
        try:
            next_msg = message_queues[s].get_nowait()
        except Queue.Empty:
            # No messages waiting so stop checking for writability.
            print >>sys.stderr, 'output queue for', s.getpeername(), 'is empty'
            outputs.remove(s)
        else:
            print >>sys.stderr, 'sending "%s" to %s' % (next_msg, s.getpeername())
            s.send(next_msg)

    # Handle "exceptional conditions"
    for s in exceptional:
        print >>sys.stderr, 'handling exceptional condition for', s.getpeername()
        # Stop listening for input on the connection
        inputs.remove(s)
        if s in outputs:
            outputs.remove(s)
        s.close()

        # Remove message queue
        del message_queues[s]


See Also

References

  1. Python Networking Programming
  2. Socket Programming HOWTO — Python 2.7.12 documentation
  3. UdpCommunication - Python Wiki
  4. Socket Types and Protocols
  5. What is the difference between AF_INET and PF_INET in socket programming?, Stack Overflow
  6. 7.5. Essential Socket Functions
  7. include/linux/socket.h
  8. listen, The Open Group Base Specifications Issue 6
  9. accept, The Open Group Base Specifications Issue 6

댓글 없음:

댓글 쓰기