python network programming
간단한 server / client code
ref. 1 의 server.py / client.py codeserver.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)
- 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
- Getting Started: Writing Your Own HTTP/2 Server — hyper-h2 2.5.1 documentation
- [컴][파이썬] python 에서 간단한 socket programming
- Raw socket programming in python (Linux) – BinaryTides : raw socket programming 예제
- https://github.com/i5on9i/raw_socket/blob/master/src/raw_socket.py
References
- Python Networking Programming
- Socket Programming HOWTO — Python 2.7.12 documentation
- UdpCommunication - Python Wiki
- Socket Types and Protocols
- What is the difference between AF_INET and PF_INET in socket programming?, Stack Overflow
- 7.5. Essential Socket Functions
- include/linux/socket.h
- listen, The Open Group Base Specifications Issue 6
- accept, The Open Group Base Specifications Issue 6
댓글 없음:
댓글 쓰기