반응형
I. Data format
I-1. Python using struct
- 파이썬의
struct
모듈을 이용하면 정수, 문자열 등을 바이트 객체로 변환하거나 추출할 수 있다pack
,unpack
,calcsize
을 제공
struct - Interpret bytes as packed binary data - Python 3.9.6 documentation
I-1. basic_packet_format.py
BasicPacketFormat = '='
BasicPacketFormat += 'i' # Type Of Service (int; 4bytes)
BasicPacketFormat += 'i' # Display ID (int; 4bytes)
BasicPacketFormat += 'i' # Payload Length (int; 4bytes)
I-2. touch_packet.py
from packet import basic_packet_format
from collections import namedtuple
import struct
TouchPacketFormat = basic_packet_format.BasicPacketFormat
TouchPacketFormat += 'i' # X-axis
TouchPacketFormat += 'i' # Y-axis
class TouchPacket:
def __init__(self, display_id):
print('Create Touch Packet')
self.display_id = display_id
self.type_of_service = 0
def GetData(self, input_x, input_y):
TouchStruct = namedtuple("TouchStruct", "tos id len x y")
TupleToSend = TouchStruct(
tos=self.type_of_service,
id=self.display_id,
len=8,
x=input_x,
y=input_y
)
#print(TouchPacketFormat)
#print(TupleToSend)
#print(*TupleToSend._asdict().values())
StringToSend = struct.pack(TouchPacketFormat, *TupleToSend._asdict().values())
return StringToSend
I-3. direction_packet.py
from packet import basic_packet_format
from collections import namedtuple
import struct
DirectionPacketFormat = basic_packet_format.BasicPacketFormat
DirectionPacketFormat += 'i' # Direction
class DirectionPacket:
def __init__(self, display_id):
print('Create Direction Packet')
self.display_id = display_id
self.type_of_service = 1
def GetData(self, input_direction):
DirectionStruct = namedtuple("DirectionStruct", "tos id len direction")
TupleToSend = DirectionStruct(
tos=self.type_of_service,
id=self.display_id,
len=4,
direction=input_direction
)
#print(DirectionPacketFormat)
#print(TupleToSend)
#print(*TupleToSend._asdict().values())
StringToSend = struct.pack(DirectionPacketFormat, *TupleToSend._asdict().values())
return StringToSend
I-4. gaze_packet.py
from packet import basic_packet_format
from collections import namedtuple
import struct
GazePacketFormat = basic_packet_format.BasicPacketFormat
GazePacketFormat += 'i' # X-axis
GazePacketFormat += 'i' # Y-axis
class GazePacket:
def __init__(self, display_id):
print('Create Gaze Packet')
self.display_id = display_id
self.type_of_service = 2
def GetData(self, input_x, input_y):
GazeStruct = namedtuple("GazeStruct", "tos id len x y")
TupleToSend = GazeStruct(
tos=self.type_of_service,
id=self.display_id,
len=8,
x=input_x,
y=input_y
)
StringToSend = struct.pack(GazePacketFormat, *TupleToSend._asdict().values())
return StringToSend
I-5. voice_packet.py
from packet import basic_packet_format
from collections import namedtuple
import struct
VoicePacketFormat = basic_packet_format.BasicPacketFormat
VoicePacketFormat += '1012s'
class VoicePacket:
def __init__(self, display_id):
print('Create Voice Packet')
self.display_id = display_id
self.type_of_service = 3
def GetData(self, input_len, input_txt):
VoiceStruct = namedtuple("VoiceStruct", "tos id len txt")
TupleToSend = VoiceStruct(
tos=self.type_of_service,
id=self.display_id,
len=input_len,
txt=input_txt.encode("ascii")
)
StringToSend = struct.pack(VoicePacketFormat, *TupleToSend._asdict().values())
return StringToSend
I-6. hand_skeleton_packet.py
from packet import basic_packet_format
from collections import namedtuple
import struct
HandSkeletonPacketFormat = basic_packet_format.BasicPacketFormat
HandSkeletonPacketFormat += 'i' # X-axis
HandSkeletonPacketFormat += 'i' # Y-axis
class HandSkeletonPacket:
def __init__(self, display_id):
print('Create Hand Skeleton Packet')
self.display_id = display_id
self.type_of_service = 5
def GetData(self, input_x, input_y):
HandSkeletonStruct = namedtuple("HandSkeletonStruct", "tos id len x y")
TupleToSend = HandSkeletonStruct(
tos=self.type_of_service,
id=self.display_id,
len=8,
x=input_x,
y=input_y
)
StringToSend = struct.pack(HandSkeletonPacketFormat, *TupleToSend._asdict().values())
return StringToSend
II. Python Client
II-1. Client
- 클라이언트 인터페이스 객체를 생성하고 패킷 전송 함수를 호출 (예시:
SendTouchPacket(1, 2)
)
import socket
from time import sleep
import client_interface
display_id = input('enter display id : ')
client_interface = client_interface.ClientInterface(int(display_id))
client_interface.SendTouchPacket(1, 2)
client_interface.SendDirectionPacket(0)
client_interface.SendGazePacket(11, 22)
client_interface.SendHandSkeletonPacket(3, 4)
client_interface.SendVoicePacket('hello world')
client_interface.SendTouchPacket(1, 2)
II-2. Client Interface
- 객체를 생성하면 소켓 클라이언트 연결을 요청
- 객체가 소멸될 때 소켓을 닫음
- 패킷 전송 함수를 별도로 구현하여 데이터 포맷에 맞게 전송할 수 있도록 추상화함
import struct
import socket
from time import sleep
from packet import touch_packet
from packet import gaze_packet
from packet import direction_packet
from packet import voice_packet
#from packet import body_skeleton_packet
from packet import hand_skeleton_packet
server_address = '127.0.0.1' # Need to be changed to 192.168.0.2
port = 50001
class ClientInterface:
def __init__(self, display_id):
print('Create Client Interface')
self.display_id = int(display_id)
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect((server_address, port))
def __del__(self):
self.s.close()
def SendTouchPacket(self, input_x, input_y):
touch = touch_packet.TouchPacket(self.display_id)
TouchData = touch.GetData(int(input_x), int(input_y))
self.s.send(TouchData)
def SendDirectionPacket(self, input_direction):
direction = direction_packet.DirectionPacket(self.display_id)
DirectionData = direction.GetData(int(input_direction))
self.s.send(DirectionData)
def SendGazePacket(self, input_x, input_y):
gaze = gaze_packet.GazePacket(self.display_id)
GazeData = gaze.GetData(int(input_x), int(input_y))
self.s.send(GazeData)
def SendVoicePacket(self, txt):
voice = voice_packet.VoicePacket(self.display_id)
VoiceData = voice.GetData(len(txt)+1, txt)
self.s.send(VoiceData)
def SendHandSkeletonPacket(self, input_x, input_y):
hand_skeleton = hand_skeleton_packet.HandSkeletonPacket(self.display_id)
HandSkeletonData = hand_skeleton.GetData(int(input_x), int(input_y))
self.s.send(HandSkeletonData)
III. C# Server
III-1. Server (Unity)
- C#에서 제공하는
TcpListner
,TcpClient
를 이용하여 구현함 - 게임 오브젝트가 생성되면 별도의 Thread에서 데이터를 수신할 수 있음
- 빅엔디안의 경우 바이트 배열을 역순으로 배치함 (
Array.Reverse(bytesTypeOfService);
) BitConverter
를 이용해 바이트 배열을 4바이트int
로 변환함
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class Server : MonoBehaviour
{
#region private members
private TcpListener tcpListener;
private Thread tcpListenerThread;
private TcpClient connectedTcpClient;
#endregion
void Start()
{
// Start TcpServer background thread
tcpListenerThread = new Thread (new ThreadStart(ListenForIncommingRequest));
tcpListenerThread.IsBackground = true;
tcpListenerThread.Start();
}
void Update()
{
}
// Runs in background TcpServerThread; Handles incomming TcpClient requests
private void ListenForIncommingRequest() {
try {
// Create listener on 192.168.0.2 port 50001
tcpListener = new TcpListener(IPAddress.Parse("192.168.0.2"), 50001);
tcpListener.Start();
Debug.Log("Server is listening");
while (true) {
using (connectedTcpClient = tcpListener.AcceptTcpClient()) {
// Get a stream object for reading
using (NetworkStream stream = connectedTcpClient.GetStream()) {
// Read incomming stream into byte array.
do {
Byte[] bytesTypeOfService = new Byte[4];
Byte[] bytesDisplayId = new Byte[4];
Byte[] bytesPayloadLength = new Byte[4];
int lengthTypeOfService = stream.Read(bytesTypeOfService, 0, 4);
int lengthDisplayId = stream.Read(bytesDisplayId, 0, 4);
int lengthPayloadLength = stream.Read(bytesPayloadLength, 0, 4);
if(lengthTypeOfService <= 0 && lengthDisplayId <= 0 && lengthPayloadLength <= 0) {
break;
}
// Reverse byte order, in case of big endian architecture
if (!BitConverter.IsLittleEndian) {
Array.Reverse(bytesTypeOfService);
Array.Reverse(bytesDisplayId);
Array.Reverse(bytesPayloadLength);
}
int typeOfService = BitConverter.ToInt32(bytesTypeOfService, 0);
int displayId = BitConverter.ToInt32(bytesDisplayId, 0);
int payloadLength = BitConverter.ToInt32(bytesPayloadLength, 0);
if(typeOfService == 3) {
payloadLength = 1012;
}
Byte[] bytes = new Byte[payloadLength];
int length = stream.Read(bytes, 0, payloadLength);
HandleIncommingRequest(typeOfService, displayId, payloadLength, bytes);
} while (true);
}
}
}
}
catch (SocketException socketException) {
Debug.Log("SocketException " + socketException.ToString());
}
}
// Handle incomming request
private void HandleIncommingRequest(int typeOfService, int displayId, int payloadLength, byte[] bytes) {
Debug.Log("=========================================");
Debug.Log("Type of Service : " + typeOfService);
Debug.Log("Display Id : " + displayId);
Debug.Log("Payload Length : " + payloadLength);
switch(typeOfService) {
case 0: // Touch (0)
TouchHandler(displayId, payloadLength, bytes);
break;
case 1: // Direction (1)
DirectionHander(displayId, payloadLength, bytes);
break;
case 2: // Gaze (2)
GazeHandler(displayId, payloadLength, bytes);
break;
case 3: // Voice (3)
VoiceHandler(displayId, payloadLength, bytes);
break;
case 4: // Body Skeleton (4)
BodySkeletonHandler(displayId, payloadLength, bytes);
break;
case 5: // Hand Skeleton (5)
HandSkeletonHandler(displayId, payloadLength, bytes);
break;
}
}
// Handle Touch Signal
private void TouchHandler(int displayId, int payloadLength, byte[] bytes) {
Debug.Log("Execute Touch Handler");
int x_axis = BitConverter.ToInt32(bytes, 0);
int y_axis = BitConverter.ToInt32(bytes, 4);
Debug.Log("X axis : " + x_axis);
Debug.Log("Y axis : " + y_axis);
}
// Handle Direction Signal
private void DirectionHander(int displayId, int payloadLength, byte[] bytes) {
Debug.Log("Execute Direction Handler");
int direction = BitConverter.ToInt32(bytes, 0);
Debug.Log("Direction : " + direction);
}
// Handle Gaze Signal
private void GazeHandler(int displayId, int payloadLength, byte[] bytes) {
Debug.Log("Execute Gaze Handler");
int x_axis = BitConverter.ToInt32(bytes, 0);
int y_axis = BitConverter.ToInt32(bytes, 4);
Debug.Log("X axis : " + x_axis);
Debug.Log("Y axis : " + y_axis);
}
// Handle Voice Signal
private void VoiceHandler(int displayId, int payloadLength, byte[] bytes) {
Debug.Log("Execute Voice Handler");
string str = Encoding.Default.GetString(bytes);
Debug.Log("Text : " + str);
}
// Handle Body Skeleton Signal
private void BodySkeletonHandler(int displayId, int payloadLength, byte[] bytes) {
Debug.Log("Execute Body Skeleton Handler");
// TODO
}
// Handle Hand Skeleton Signal
private void HandSkeletonHandler(int displayId, int payloadLength, byte[] bytes) {
Debug.Log("Execute Hand Skeleton Handler");
int x_axis = BitConverter.ToInt32(bytes, 0);
int y_axis = BitConverter.ToInt32(bytes, 4);
Debug.Log("X axis : " + x_axis);
Debug.Log("Y axis : " + y_axis);
}
}
반응형
'💻 programming > unity' 카테고리의 다른 글
유니티 UDP 소켓 통신 구현하기 - 유니티 UDP 서버 C# 예제 코드 <2> (0) | 2023.01.13 |
---|---|
유니티 UDP 소켓 통신 구현하기 - 예제 프로젝트 소개 <1> (0) | 2023.01.13 |
[유니티] WebGL 빌드에러: Unable to parse Build/Build.framework.js.gz! (0) | 2022.12.09 |
[유니티] WebGL 빌드에러: Getting System.ComponentModel.Win32Exception (2) (2) | 2022.12.09 |
유니티에서 소켓 통신하고 이벤트 등록하기 (C# 예제 코드) (0) | 2021.07.30 |
댓글