반응형

주제 : socket 통신

언어 : python

개발환경 : IDLE 또는 VScode

목적 : 게임 개발


소켓 통신이란

소켓 통신이란 서버와 클라이언트로 이루어진 양방향 통신이다. 소켓 통신의 장점은 HTTP 통신과 같이 서버에 요청하면 응답하는 단방향 통신와는 다르게 양쪽에서 데이터를 전송할 수 있다는 점이다. 나는 이 소켓 통신을 멀티플레이어 게임을 개발하는 데에 사용하기 위해 공부하려고 한다. 멀티 플레이어 게임은 여러 사용자, 즉 여러 대의 컴퓨터가 접속할 수 있어야 한다. 여러 사용자는 각자의 위치나 정보를 주고받기 위해 통신을 사용해야 하는데, 반응 속도가 빠르고 실시간으로 상태를 업데이트해줘야 하기 때문에 소켓 통신을 선택하였다.


소켓 통신의 구조

먼저 소켓은 프로토콜, IP 주소, 포트 넘버로 정의된다. 이 소켓을 통해 서버를 생성하고, 클라이언트가 서버에 접속한다. 소켓의 종류로는 TCP와 UDP가 있다.

서버 소켓은 다음과 같은 방식으로 진행된다.

  • socket() : 소켓 만들기
  • bind() : IP주소와 소켓을 묶기
  • listen() : 클라이언트의 소켓 기다리기
  • accept() : 클라이언트의 접속 요청 수락하기
  • send() / recv() : 데이터를 전송하거나 받기
  • close() : 소켓 닫기

클라이언트 소켓은 다음과 같은 방식으로 진행된다.

  • socket() : 소켓 만들기
  • connect() : 서버 소켓에 연결 시도하기
  • send() / recv() : 데이터를 전송하거나 받기
  • close() : 소켓 닫기

더 자세하게 알고 싶다면 다음 블로그를 참조하길 바란다.

 

[기본] 소켓(SOCKET)통신 이란?

소켓통신 소켓(SOCKET)이란? 소켓(Socket)은 프로세스가 드넓은 네트워크 세계로 데이터를 내보내거나 혹은 그 세계로부터 데이터를 받기 위한 실제적인 창구 역할을 한다. 그러므로 프로세스가 데

helloworld-88.tistory.com


코드

이제 소스코드를 설명하겠다. 이번에는 설명을 위해 소켓 통신을 이용한 간단한 채팅 앱을 만들어 볼 것이다.

 

서버 : 

먼저 소켓 통신을 하기 위해 socket이라는 모듈을 import해준다. 또한 서버에 접속하는 사용자마다 thread를 추가해 줄 것이므로 _thread 모듈을 import 해주었다. 그리고 클라이언트들의 소켓 정보를 보관할 수 있는 리스트를 만들어 주었다.

import socket
from _thread import *

client_sockets = []

다음으로 서버를 열기 위해 IP주소를 알아내는 코드와 포트를 지정해 주었다. 이번에는 임시로 9999포트를 설정해 주었다.

HOST = socket.gethostbyname(socket.gethostname())
PORT = 9999

이제 소켓 설정이 끝났다. 위의 설정들을 합쳐서 서버를 만들어 보겠다. 먼저 client에게 IP주소를 쉽게 알려주기 위해 터미널에 출력할 수 있도록 하였다. 그리고 socket()을 이용하여 소켓을 만들었고, IP주소와 포트를 통신할 수 있도록 묶어주었다.

print('>> Server Start with ip :', HOST)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))

그리고 listen()을 사용하여 client의 접속 요청을 기다리도록 만들었다.

server_socket.listen()

client의 접속 요청이 들어왔을 때 수락하는 accept()함수를 넣었고, 그 client의 소켓 정보를 저장하도록 하였다. 그리고 마지막으로 서버를 닫을 때 소켓이 닫히도록 프로그래밍했다.

try:
    while True:
        print('>> Wait')

        client_socket, addr = server_socket.accept()
        client_sockets.append(client_socket)
        start_new_thread(threaded, (client_socket, addr))
        print("참가자 수 : ", len(client_sockets))
except Exception as e:
    print('에러 : ', e)

finally:
    server_socket.close()

그리고 참가자들마다 thread를 할당해주어 여러명이 함께 사용할 수 있도록 만들었다. 이 thread는 채팅하면 서버가 다른 클라이언트들에게 데이터를 전송하는 형태로 작성되었다. 그리고 클라이언트가 나가면, 해당 클라이언트의 소켓도 닫도록 하였다. 

def threaded(client_socket, addr):
    print('>> Connected by :', addr[0], ':', addr[1])

    ## process until client disconnect ##
    while True:
        try:
            ## send client if data recieved(echo) ##
            data = client_socket.recv(1024)

            if not data:
                print('>> Disconnected by ' + addr[0], ':', addr[1])
                break

            print('>> Received from ' + addr[0], ':', addr[1], data.decode())

            ## chat to client connecting client ##
            ## chat to client connecting client except person sending message ##
            for client in client_sockets:
                if client != client_socket:
                    client.send(data)
        
        except ConnectionResetError as e:
            print('>> Disconnected by ' + addr[0], ':', addr[1])
            break
    
    if client_socket in client_sockets:
        client_sockets.remove(client_socket)
        print('remove client list : ', len(client_sockets))

    client_socket.close()

이로써 서버는 열렸다.


클라이언트는 서버보다 훨씬 간단하다. 그 이유는 서버가 만들어놓은 설정에 그냥 참여하여 이용하다 나오면 그만이기 때문이다. 따라서 서버와 연결하는 부분과, 데이터를 주고받는 코드를 작성하였다.

 

전체 소켓통신 코드는 다음과 같다.

 

Server : 

## SERVER ##

import socket
from _thread import *

client_sockets = []

## Server IP and Port ##

HOST = socket.gethostbyname(socket.gethostname())
PORT = 9999

########## processing in thread ##
## new client, new thread ##

def threaded(client_socket, addr):
    print('>> Connected by :', addr[0], ':', addr[1])

    ## process until client disconnect ##
    while True:
        try:
            ## send client if data recieved(echo) ##
            data = client_socket.recv(1024)

            if not data:
                print('>> Disconnected by ' + addr[0], ':', addr[1])
                break

            print('>> Received from ' + addr[0], ':', addr[1], data.decode())

            ## chat to client connecting client ##
            ## chat to client connecting client except person sending message ##
            for client in client_sockets:
                if client != client_socket:
                    client.send(data)
        
        except ConnectionResetError as e:
            print('>> Disconnected by ' + addr[0], ':', addr[1])
            break
    
    if client_socket in client_sockets:
        client_sockets.remove(client_socket)
        print('remove client list : ', len(client_sockets))

    client_socket.close()

############# Create Socket and Bind ##

print('>> Server Start with ip :', HOST)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen()

############# Client Socket Accept ##

try:
    while True:
        print('>> Wait')

        client_socket, addr = server_socket.accept()
        client_sockets.append(client_socket)
        start_new_thread(threaded, (client_socket, addr))
        print("참가자 수 : ", len(client_sockets))
except Exception as e:
    print('에러 : ', e)

finally:
    server_socket.close()

Client : 

## CLIENT ##

import socket
from _thread import *

HOST = '' ## server에 출력되는 ip를 입력해주세요 ##
PORT = 9999

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((HOST, PORT))

def recv_data(client_socket):
    while True:
        data = client_socket.recv(1024)
        print("recive : ", repr(data.decode()))

start_new_thread(recv_data, (client_socket,))
print('>> Connect Server')

while True:
    message = input()
    if message == 'quit':
        close_data = message
        break

    client_socket.send(message.encode())

client_socket.close()
728x90
반응형