Machineboy空

Http통신과 WebSocket 통신 본문

Game/Unity

Http통신과 WebSocket 통신

안녕도라 2023. 9. 5. 17:03
Http 통신 WebSocket 통신
Client의 요청(Request)이 있을 때만 Server가 응답(Response)하여 해당 정보를 전송하고 곧바로 연결을 종료하는 방식 Server와 Client가 특정 Port를 통해 실시간으로 양방향 통신하는 방식
요청을 보내서 server의 응답을 기다리는 어플리케이션 개발에 주로 사용 실시간 동영상 Streaming, 온라인 게임 

 

  • 01. Socket을 통한 실시간 통신
    • 실시간 국제정거장(ISS)위치 정보를 1분 마다 받아오기 위해 활용했음
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//WebSocketSharp Plugins다운로드 후 라이브러리 사용
using WebSocketSharp;

//JSON 정보 구성

//{
//  "iss_position": {
//                      "longitude":"-137.1979",
//                      "latitude":"-10.2846"
//                  },
//  "timestamp":1693465485,
//  "message":"success"
//  }

//JSON 구조 똑같이 만들어 두기
//변수명 JSON의 키값과 완전히 동일해야 함.

[Serializable]
public struct ISSPosition
{
    public string longitude;
    public string latitude;
}

[Serializable]
public class ISSData
{
    public ISSPosition iss_position;
    public long timestamp;
    public string message;
}


public class SocketTest : MonoBehaviour
{
    public string ip = "";		//ip주소값
    public string port = "";	//port: 서버에 접속하기 위한 플러그 같은 것
    public WebSocket socket = null;

    public ISSData issData;

    // Start is called before the first frame update
    void Start()
    {
        try
        {
            //192.168.1.48:3030
            socket = new WebSocket("ws://" + ip + ":" + port);
            //socket = new WebSocket("ws://7cd2-110-70-53-53.ngrok-free.app/iss");
            socket.OnMessage += Recv;
            socket.OnOpen += OnOpen;
            socket.OnClose += OnClose;
            socket.OnError += OnError;
            Connect();

        }
        catch
        {
            print("1111111");
        }

    }

	//Socket에 연결하기
    public void Connect()
    {
        socket.Connect();
    }
    
    //Socket 서버에 연결 끊기
    public void DisConnect()
    {
        if (socket.IsAlive)
        {
            socket.Close();
        }
    }

	//WebSocket에 연결되었을 때 호출되는 이벤트
    void OnOpen(object sender, EventArgs e)
    {
        print("2222");
    }

	//WebSocket에서 메시지가 날아왔을 때 호출되는 이벤트 (원래는 webSocket.onmessage가 내장이지만 매개 변수 형태 같게 하여 변형하여 씀)
    public void Recv(object sender, MessageEventArgs e)
    {
        //print(e.Data);

        try
        {
            issData = JsonUtility.FromJson<ISSData>(e.Data);

        }
        catch
        {

        }

    }
	
    //WebSocket이 닫히면 호출되는 이벤트
    public void OnClose(object sender, CloseEventArgs e)
    {
        
        DisConnect();
        
    }

	//WebSocket에 에러가 나면 호출되는 이벤트
    public void OnError(object sender, ErrorEventArgs e)
    {
        print( "error : " + e.Message);

        //{
        //  "iss_position": {
        //                      "longitude":"-137.1979",
        //                      "latitude":"-10.2846"
        //                  },
        //  "timestamp":1693465485,
        //  "message":"success"
        //  }

    }



    // Update is called once per frame
    void Update()
    {
    }

	//어플리케이션이 종료되면 자동으로 연결되지 않도록
    private void OnApplicationQuit()
    {
        DisConnect();
    }

}
01. WebSocket용 플러그인, 라이브러리 다운로드   using WebSocketSharp;
02.  서버 JSON정보 구조와 같이 구조 짜두기    
03. 필요 변수 적어두기 ip (internet Protocol) 인터넷상에서 사용하는 주소체계
port IP주소가 가리키는 PC에 접속할 수 있는 통로(채널)
JSON 구조의 structure  
04. Start함수에서 Connect
ApplicationQuit에서 Disconnect
   
05. SocketSharp 라이브러리에서 사용하는 내장 함수들 socket.Connect(); Socket서버에 접속
socket.OnOpen(); WebSocket에 연결되었을 때 호출되는 이벤트
socket.OnMessage(); WebSocket에서 메세지가 날아오면 호출되는 이벤트
socket.OnError(); WebSocket에 에러가 나면 호출되는 이벤트
socket.OnClose(); WebSocket이 닫히면 호출되는 이벤트
socket.Disconnect(); Socket서버에 연결 끊기
  • Socket : 네트워크 통신에서 종단의 교신점, 서버에 접속하기 위한 연결 부분
    • 예시) 전화 서비스: 송신자 - 전화 - 수신자
      • 송신자: 전화번호를 누른다 (전화번호: 소켓 주소)
      • 수신자: 수화기를 든다.
      • 전화 = 소켓

네트워크의 기본

특정 서버에 접속하기 위해서는 다른 PC를 가리키는 주소를 알아야 한다.

  • IP(Internet Protocol) : 인터넷에 연결되어 있는 각 컴퓨터는 숫자로 이루어진 고유 주소를 가지며, 이를 IP 주소라고 한다.  
  • Port: IP내에서 애츨리케이션 상호 구분을 위해 사용하는 번호로, 포트 숫자는 IP주소가 가리키는 PC에 접숙할 수 있는 통로(채널)을 의미한다.

 

  • 02. Http를 활용한 요청 시 통신
    • 어플리케이션 접속 당시 시간이 낮인지 밤인지를 일출, 일몰 정보를 담은 서버를 활용해 UI 표시
  • Http Method(8)
GET 서버로부터 데이터를 취득한다.
POST 서버에 데이터를 추가,작성한다.
PUT 서버의 데이터를 갱신,작성한다.
DELETE 서버의 데이터를 삭제한다.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

//http 통신 활용 시 활용하는 라이브러리
using UnityEngine.Networking;

//JSON 구조 변수명 그대로 활용
[Serializable]
public class SunriseNSunset
{
    public string sunrise;
    public string sunset;
    public string nowTime;
    public bool isDay;
    public bool isNight;
}

//Http Method (총 8개)
public enum EJRequestType
{
    GET,	//조회, 서버로부터 데이터를 취득
    POST,	//등록, 서버에 데이터를 추가, 작성 등
    PUT,	//수정, 서버의 데이터를 갱신, 작성
    DELETE,	//삭제, 서버의 데이터를 삭제
    TEXTURE
}

public class EJHttpInfo
{
    public EJRequestType EJRequestType;
    public string url = "";
    public string body;
    public Action<DownloadHandler> onReceive;

	//DownloadHandler: 데이터의 다운로드 제어
    //UploadHandler: 데이터의 업로드 제어
    
    public void Set(
        EJRequestType type,
        string u,
        Action<DownloadHandler> callback,	//데이터 다운로드 시, 실행할 콜백 함수?
        bool useDefaultUrl = true)
    {
        EJRequestType = type;
        if (useDefaultUrl) url = "http://172.16.16.228:3000";

        //172.16.16.228:3000/api/weather/dayLight
        url += u;
        onReceive = callback;
    }
}

public class EJhttpManager : MonoBehaviour
{
    static EJhttpManager instance;

    public static EJhttpManager Get()
    {
        if (instance == null)
        {
            GameObject go = new GameObject("EJhttpManager");
            go.AddComponent<EJhttpManager>();
        }

        return instance;
    }

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void SendRequest(EJHttpInfo httpInfo)
    {
        StartCoroutine(CoSendRequest(httpInfo));
    }

    IEnumerator CoSendRequest(EJHttpInfo httpInfo)
    {
        UnityWebRequest req = null;

        switch (httpInfo.EJRequestType)
        {
            case EJRequestType.GET:
                //Get방식으로 req 에 정보 셋팅
                req = UnityWebRequest.Get(httpInfo.url);
                break;
                
            case EJRequestType.POST:
                req = UnityWebRequest.Post(httpInfo.url, httpInfo.body);
                byte[] byteBody = Encoding.UTF8.GetBytes(httpInfo.body);
                req.uploadHandler = new UploadHandlerRaw(byteBody);
                //헤더 추가
                req.SetRequestHeader("Content-Type", "application/json");

                break;
                
            case EJRequestType.PUT:
                req = UnityWebRequest.Put(httpInfo.url, httpInfo.body);
                break;
                
            case EJRequestType.DELETE:
                req = UnityWebRequest.Delete(httpInfo.url);
                break;
                
            case EJRequestType.TEXTURE:
                req = UnityWebRequestTexture.GetTexture(httpInfo.url);
                break;
        }

        yield return req.SendWebRequest();

        //만약에 응답이 성공했다면
        if (req.result == UnityWebRequest.Result.Success)
        {
            //print("네트워크 응답 : " + req.downloadHandler.text);
            if (httpInfo.onReceive != null)
            {
                httpInfo.onReceive(req.downloadHandler);
            }
        }
        //통신 실패
        else
        {
            print("네트워크 에러 : " + req.error);
        }
    }
}​
1 JSON 데이터 구조를 짜둔다. 변수명과 똑같이    
2 RequestType을 정해둔다.   (GET/ POST/ PUT/ DELETE/ TEXTURE)
3 SendRequest 서버에게 REST API를 요청하는 함수  
CoSendRequest 서버에 요청을 보내고 응답이 올때까지 다른 일들을 처리하기 위해서 코루틴으로 작성  
switch문으로 requestType별 동작 정리 req = UnityWebRequest.Get(httpInfo.url)