뭔가 제목이 이상하지만 이렇게 밖에 못짓겠더라.
오늘은 엄청 거창한 것은 아니고 메시지에 관해 공부했던 간단한 글을 써보고자 한다

우리가 HTTP(OSI Layer 7:Application Layer) 메시지를 보면 보낸 사람이 올린 데이터(body)만 있는 것이 아니라 메시지 헤더라는 것이 있다. 메시지 내용 뿐만 아니라 뭔가 부가적인 정보를 전송하기 위함이다.
오늘의 목표는 HTTP 헤더 살펴보기...
같은 거창한 것이 아니라 TCP 메시지 body 에 야매 header를 올려 서버 프로그램이 받아서 내용을 출력한 프로그램을 올려보고자 한다.
해당 프로그램을 작성하면서 사용하는 포인트는 세가지다.
그냥 요즘 어쩌다 쓸일이 있어서 만들었는데 재미있어서 그냥 포스트 하는 것이다.
시나리오는 이렇다.
1. 클라이언트
1. 클라이언트 소켓을 생성 설정해준다.
2. 메시지 바디와 헤더에 헤더 정보를 추가해준다.
3. 소켓을 서버와 연결한다.
4. 서버에게 메시지를 보내준다.
5. 서버로부터 받았다는 메시지를 받는다.
6. 종료
이렇게 프로그램의 순서를 작성해보았다.
다음은 위에서 말했던 포인트 세가지에 맞춰 코드를 작성했던 것에 대해 말하고자 한다.
보통 소켓통신, 오늘 이 페이지에서는 TCP 의 경우 프로그래밍을 할때 아래와 같은 과정을 거친다.
일단 이 페이지에서는 syn니 ack니 하는 이야기는 제쳐두고 프로그래밍 할 떄의 이야기를 하겠다.
1. 클라이언트
1. 소켓 생성
2. 소켓 연결
3. 메시지 전송/수신
2. 서버
1. 소켓 생성
2. 소켓 bind
3. 소켓 listen
4. 소켓 accept
5. 메시지 전송/수신
아래의 사진이 더 잘 보여준다.

파이썬의 경우에는 사실 연결이 간단하게 할 수있어서 그냥 공식문서를 한번 보면 소켓 통신하는 것을 쉽게 알 수 있었다. 아래의 코드는 server와 client의 코드이다 아마 한번 보면 대충 어떤 흐름으로 흘러가는지 감이 온다.
# server
import socket
# 서버 정보
HOST = '127.0.0.1'
PORT = 1234
# 소켓 설정 IPv4와 TCP 를 쓴다
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# bind
server_socket.bind((HOST, PORT))
# listen
server_socket.listen()
# accept
client_socket, addr = server_socket.accept()
#~~~~
#~~~~
#~~~~
# 통신이 종료하면 소켓을 닫는다.
client_socket.close()
server_socket.close()
exit(0)
import socket
~~~~
~~~~
# server IP, PORT
HOST = '127.0.0.1'
PORT = 1234
# address family : IPv4 , Socket type : TCP(Stream)
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 얀걀
client_socket.connect((HOST, PORT))
# 메시지 전송 어떤 메시지는 아래에서 설명하겠다.
client_socket.sendall(message)
# 메시지 수신
data = client_socket.recv(1024)
print('Server Received my Message~!')
# 소켓 닫기.
client_socket.close()
굉장히 간단한 예제로 하나 만들어 보았다. 멀티 쓰레드나 멀티 프로세스로 통신하는 프로그램은 다른 페이지에 만들었으니 참고하면 좋을 것 같다.
소켓프로그래밍
파이썬에서는 Dictionary 와 JSON 간의 변환이 되게 쉽다.
그냥 한줄에 끝난다
import json
di = {"a":1, "b":2}
result = json.dumps(di)
다시 되돌리는거는 loads를 쓰라고 하는데 이게 가끔 " 와 ' 때문에 문제가 나는지 그냥 eval을 사용해서 변환하는 것이 마음편하다.
j = "~~~~ 암튼json형식~~~~"
di = eval(j)
파이썬에서 소켓 통신에서 데이터 보내주기 위해서는 Bytes로 변환해 주고 보내줘야 하는데 (파이선 2.X 버전에선 byte와 str이 따로 구분되지는 않는거 같다)
Python 3.1 이후 버전에서는 굉장히 변환하는게 쉽다
# 254를 2바이트 little endian 순서로 표시하겠다.
(254).to_bytes(2, byteorder='little')
# 254를 2바이트 big Endian 순서로 표시하겠다.
(254).to_bytes(2, byteorder='big')
이러면 되는데 2.X 버전은 to_bytes가 안되서 struct 라는 것을 사용해서 되게 귀찮게 변환했다.
2바이트 안내에 표시하기 위해서 나는 이런 방식을 사용했다.
import struct
# big endian 의 경우
struct.pack(">H", 254)
'\x00\x02'
뭐 이렇게 나온다. 4byte로 출력하고 싶거나 little endian 으로 출력하고 싶다면 아래의 문서를 보면 좋을 거라 생각한다. 여기서는 생략하겠다.
문서
코드 및 결과다.
import socket
import struct
import json
# server IP, PORT
HOST = '127.0.0.1'
PORT = 1234
# header
### header type = 1byte (0x20)[10진법:36]을 보낸다고 가정하자
### message length = 2byte
# body
### json
header = []
header.append(0x20)
# message
di = {}
di["name"] = "Hamji"
di["old"] = 27
body = json.dumps(di)
leng = len(body)
message = bytearray(header)
## 보낼 때는 python 3.1 ~에서 슬 수 있는 .to_bytes사용
message += bytearray(leng.to_bytes(2, byteorder="big"))
message += bytes(body,'utf-8')
print("send Message: ",message)
# address family : IPv4 , Socket type : TCP(Stream)
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((HOST, PORT))
client_socket.sendall(message)
data = client_socket.recv(1024)
print('Server Received my Message~!')
client_socket.close()
import server
import json
import struct
import socket
HOST = '127.0.0.1'
PORT = 1234
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# bind
server_socket.bind((HOST, PORT))
# listen
server_socket.listen()
# accept
client_socket, addr = server_socket.accept()
print('Connect~!')
data = client_socket.recv(1024)
print ("Raw data: ", data)
header = data[:3]
body = data[3:]
print("header type: ", data[0])
print("message leng: ", 256 * data[1] + data[2])
# json to dict
di = eval(str(body))
print("read-bodylen: ", len(data[3:]))
print("body : ", di)
client_socket.sendall(bytes("OK", 'utf-8'))
client_socket.close()
server_socket.close()
exit(0)
