모르는게 많은 개발자

[스마일게이트 서버 개발 캠프 4기]#2 게임 채팅 서버 구현(로그인, 채팅) 본문

스마일게이트서버캠프4기

[스마일게이트 서버 개발 캠프 4기]#2 게임 채팅 서버 구현(로그인, 채팅)

Awdsd 2020. 1. 25. 16:33
반응형

캠프에서 유일하게 게임 개발 프로젝트를 하게 되었다. 서버 담당을 맡은 나는 두번째 개인과제에서 개발한 로그인 시스템을 이용해 유저끼리 간단히 파이썬 소켓 서버를 통해 클라이언트끼리 채팅하는 프로그램을 구현하였다.

 

1. 로그인 화면

간단하게 이메일과 비밀번호를 입력할 수 있는 씬을 제작.

간단한 로그인 화면

 

2. HTTP 통신 구현

개인과제에서 Flask를 이용한 로그인 시스템을 구현했기 때문에 Flask서버를 그대로 이용해 게임 로그인에 적용했다. 일단 C#에서 HTTPManager클래스를 만들어 서버와의 통신을 구현했다.

using System.Net;
using System.IO;
using System.Text;


public class HTTPManager
{
    string url;
    string res = string.Empty;
    string postData = string.Empty;
    byte[] sendData;
    HttpWebRequest request;
    HttpWebResponse resp;
    Stream requestStream;

    public HTTPManager()
    {
        url = "http://localhost:8082/";     
        
    }

    //이메일과 비밀번호를 입력받아 Flask서버로 로그인 요청 
    public string LoginReq(string email, string pw)
    {
        request = Init("clientlogin");      //URL 경로 이름
        request.Method = "POST";            
        postData = string.Format("email={0}&password={1}", email, pw);      //데이터 포맷 방법.
        SendData(postData);
        return ReceiveData(request);
    }

    public string UserCacheReq(string email)
    {
        request = Init("clientusercache");
        request.Method = "POST";
        postData = string.Format("email={0}", email);
        SendData(postData);
        return ReceiveData(request);
        
    }
    //URL Path를 입력받아 request객체 생성
    private HttpWebRequest Init(string req)
    {
        HttpWebRequest tmp;
        tmp = (HttpWebRequest)WebRequest.Create(url + req);
        tmp.Timeout = 30 * 1000;
        tmp.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
        return tmp;
    }

    //C#에서 서버로 데이터를 보내기 위해 byte로 변환후 전송.
    private void SendData(string data)
    { 
        sendData = UTF8Encoding.UTF8.GetBytes(data);
        requestStream = request.GetRequestStream();
        requestStream.Write(sendData, 0, sendData.Length);
        requestStream.Close();
    }

    //Response데이터 받아오기
    private string ReceiveData(HttpWebRequest req)
    {
        using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
        {
            HttpStatusCode status = resp.StatusCode;

            Stream stream = resp.GetResponseStream();
            using (StreamReader sr = new StreamReader(stream))
            {
                res = sr.ReadToEnd();
                return res;
            }
        }
    }
}

Flask 서버 구동
DB 유저 정보

 

3. 채팅 소켓 서버

import socket
from _thread import *


HOST = '127.0.0.1'
PORT = 3098
#소켓 서버 설정
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
server_socket.bind((HOST, PORT))
server_socket.listen()

#서버에 접속한 유저 소켓 리스트 저장
list_of_clients = []
print('server start')


#각 클라이언트 소켓 쓰레드
def client_thread(user_socket):
    while True:
        try:
            #클라이언트에서 온 데이터 수신
            message = str(str(user_socket[2]) + ":").encode() + user_socket[0].recv(2048)
            if not message:
                remove(user_socket)
                break
            print(message)
            #클라이언트에서 온 데이터를 다른 클라이언트에게도 전달
            broadcast(message, user_socket[1][1])   
        except:
            continue


def broadcast(message, user):
    for clients in list_of_clients:
        try:
            clients[0].send(message)
        except:
            clients[0].close()
            remove(clients)


def remove(connection):
    if connection in list_of_clients:
        print("asdasdasd")
        list_of_clients.remove(connection)
    for row in list_of_clients:
        print(row)


while True:
    print('waiting new client..')
    client_socket, addr = server_socket.accept()        #소켓
    print(addr[0] + ' ' + str(addr[1]) + ' connected')
    user_email = client_socket.recv(2048)

    list_of_clients.append([client_socket, addr, user_email])
    #쓰레드 시작
    start_new_thread(client_thread, (list_of_clients[-1],))

server_socket.close()

 

4. 정보 입력

위의 세팅을 토대로 정보를 입력한다.

정보 입력

using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class LogInManager : MonoBehaviour
{
    UserInfo userInfo;
    HTTPManager httpManager;
    [Header("InputFields")]
    public InputField idInput;
    public InputField pwInput;

    [Header("Buttons")]
    public Button signInButton;
    public Button signUpButton;
    
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("awda");
        userInfo = GameObject.Find("UserInfoObject").GetComponent<UserInfo>();
        httpManager = new HTTPManager();
    }
    
    // 로그인 버튼 클릭시 이벤트 발생 함수
    public void SignInBtn()
    {
        userInfo.userData = JsonUtility.FromJson<UserData>(httpManager.LoginReq(idInput.text, pwInput.text));
        Debug.Log(userInfo.userData.login);
        //로그인 성공시
        if (userInfo.userData.login == "true")
        {
            SceneManager.LoadScene("Chatting");
        }
    }

    // 회원가입 버튼 클릭시 이벤트 발생 함수
    public void SignUpBtn()
    {
        // 회원가입 페이지 or 씬으로 이동.
    }


}

Sign In 버튼 클릭시 SignInBtn() 함수가 실행되면서 서버로 로그인 request를 보낸다. 

로그인이 성공하면 채팅화면으로 이동한다.

채팅 화면

 

5. 채팅 입력

채팅 입력

채팅 입력후 send버튼을 누르게 되면 아래 사진처럼 클라이언트에서 서버로 메세지가 전달이 된다. 그리고 현

클라이언트에서 서버로 전송된 메세지

현재 클라이언트에서 서버로 보낸 메세지가 모든 클라이언트에게 다시 전송하므로 결과는 내가 입력한 hello world와 내가 서버로 보낸 메세지가 다시 출력되어 아래 사진처럼 두개의 메세지가 출력되게 된다.

반응형
Comments