서버 구축

이우석·2024년 1월 20일

포트폴리오

목록 보기
1/15

드디어 취업을 위해 포트폴리오를 제작할 때가 왔다.

다음주면 네트워크 프로그래밍 수업이 끝나고 기획 수업과 함께 본격적인 포트폴리오 제작에 들어가는데, 최애 게임인 마비노기의 개발사 데브캣에서 마비노기 모바일의 채용공고가 올라온 것을 보고 조금이라도 빨리 포트폴리오를 뽑기로 했다.

기획서는 그동안 틈틈히 작성해뒀다.

목표로 하는 게임은 PvP 탄막 슈팅게임에 카드전략요소를 더한 형태인데
세계관을 고려해서 프로젝트명은 4Season으로 정해뒀다.
스토리나 세계관 등은 기획서에 쓰지 않았지만...

게임 개발에 도전하는 신입이긴하지만 웹 개발 4년차의 경력도 살려보고 싶기에 서버까지 붙일 생각이기에 우선은 서버를 구축하기로 했다.

아마존 웹 서비스에서 무료로 제공해주는 가상 서버를 하나 받고.. DB도 붙이고..
내게 익숙한 LAMP 환경을 구축했다.

그러다 문득 내가 PHP로는 소켓 통신을 해본 적이 없다는 사실을 깨닳았다. 물론 내게 가장 익숙한 언어이니 금방 익힐 수 있을 것 같았지만..
이왕 배우려면 더 인기 있고 넓게 쓰이는 기술을 배우고 싶었다.

그래서 과감히 LAMP 환경으로 구축된 서버를 삭제하고 Node.js에 도전해보기로 했다.

서버 사이드 코드

서버 사이드 결과

클라이언트 코드

클라이언트 결과

우여곡절이 있었지만... 일단 간단하게 TCP로 데이터를 주고 받는 것자체는 성공.
그런데 C#과 Node.js간에 데이터를 어떻게 주고 받게 해야할 지 고민이 많았다.
C#와 C# 끼리는 같은 방법으로 버퍼<->구조체 변환이 가능했는데..
Node.js에는 구조체가 없으므로 buffer 내의 내용을 수동으로 쪼개줘야 했다.
다음에는 애초에 데이터를 json<->buffer로 주고받는게 좋을 것 같다.

서버 사이드 코드

받은 buffer를 readIntLE, toString으로 쪼갤 수 있고
반대로 buffer 데이터를 만들어야 할 때는, buffer.write, buffer.IntLE로 써줄 수 있다.

클라이언트 사이드 코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System;

public class DBTest : MonoBehaviour
{
    const string Host = "ec2-52-78-226-139.ap-northeast-2.compute.amazonaws.com";
    const short Port = 7311;
    Socket server;
	bool isConnect;

	[StructLayout(LayoutKind.Sequential)]
	public struct Packet
	{
		[MarshalAs(UnmanagedType.U4)]
		public int ProtocolID;

		[MarshalAs(UnmanagedType.U4)]
		public int DataSize;

		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1016)]
		public byte[] Data;
	}

	[StructLayout(LayoutKind.Sequential)]
	public struct Packet_Data
	{
		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
		public string str;
	}

	private void Start()
    {
        server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        server.Connect(Host, Port);

        Packet_Data sendData;
        sendData.str = "포시즌";
		byte[] sendDataBytes = StructToBytes(sendData);

		Packet sendPacket;
		sendPacket.ProtocolID = 0;
		sendPacket.DataSize = sendDataBytes.Length;
		sendPacket.Data = sendDataBytes;

		byte[] sendBuffer = StructToBytes(sendPacket);
		server.Send(sendBuffer);

        isConnect = true;
    }

	private void Update()
	{
        if (isConnect)
        {
            if(server.Poll(0, SelectMode.SelectRead))
            {
				byte[] receiveBuffer = new byte[1024];
                if (server.Receive(receiveBuffer) > 0)
                {
                    Debug.Log("Data Receive");
					Packet receivePacket = BytesToStruct<Packet>(receiveBuffer, 1024);
					Packet_Data receivePacketData = BytesToStruct<Packet_Data>(receivePacket.Data, 50);
					Debug.Log(receivePacketData.str);
                }
            }
        }
	}

	public static byte[] StructToBytes(object obj)
	{
		byte[] data = null;

		// 구조체의 할당된 메모리의 크기를 구함
		int datasize = Marshal.SizeOf(obj);

		// 비관리 메모리 영역에 구조체 크기 만큼의 메모리를 할당
		IntPtr buffer = Marshal.AllocHGlobal(datasize);

		// 할당된 구조체 객체의 주소를 구한다.
		Marshal.StructureToPtr(obj, buffer, false);

		// 구조체가 복사될 배열
		data = new byte[datasize];

		//구조체 객체를 배열에 복사
		Marshal.Copy(buffer, data, 0, datasize);

		// 비관리 메모리 영역에 할당했던 메모리를 해제함
		Marshal.FreeHGlobal(buffer);

		return data;
	}

	public static T BytesToStruct<T>(byte[] data, int dataSize) where T : struct
	{
		// 배열 크기만큼 비관리 메모리 영역에 메모리 할당
		IntPtr buffer = Marshal.AllocHGlobal(dataSize);

		// 배열에 저장된 데이터를 할당한 메모리 영역에 복사
		Marshal.Copy(data, 0, buffer, dataSize);

		// 복사된 데이터를 구조체 객체로 변환
		T obj = (T)Marshal.PtrToStructure(buffer, typeof(T));

		// 비관리 메모리 영역에 할당했던 메모리를 해제
		Marshal.FreeHGlobal(buffer);

		// 변환된 구조체와 원본 데이터의 크기 비교
		if (Marshal.SizeOf(obj) != dataSize)
		{
			return default(T);
		}

		return obj;
	}
}

클라이언트 사이드 결과

profile
게임 개발자 지망생, 유니티 공부중!

0개의 댓글