• 카테고리

    질문 & 답변
  • 세부 분야

    게임 프로그래밍

  • 해결 여부

    해결됨

TCP Remote Access 관련 질문입니다.

20.07.03 12:20 작성 조회수 176

0

강사님의 서버개발 강의를 다 보고 완성해서 Windows App, Android App을 만들어 테스트를 해보려고 했는데

원격지에서 TCP 접속이 계속 Timeout이 발생합니다.

서버와 클라이언트(Windows, Android)는 동일한 Hub 내에서 동작하고 있어요

고정 IP 하나에서 DHCP로 내부 아이피 할당받아 사용중입니다.

예로 서버의 내부 아이피는 192.168.0.40 이고 포트는 7777번 사용 중이고

Client는 192.168.0.40:7777로 접속을 시도하는 중이에요.

혹시나 Server 쪽에서 In/OutBound 정책을 설정을 안했나 싶어서 TCP 7777번 포트를 모두 열어놓았으나 Timeout이 발생해

모든 포트를 다 열어서 테스트했는데 동일하게 Timeout이 발생합니다.

원격지에서 TCP서버로 접속하려면 다른 설정이나 코드가 필요한것인가요?

답변 5

·

답변을 작성해보세요.

0

ㅎㅎㅎ 딱히 실례라고 생각은 하지 않았고,
여러가지 수정을 하면서 테스트를 해보시다보면
이런 저런 문제도 많이 발견되지만, 그만큼 얻는 것도 많을겁니다.
막히는게 있으시면 제가 아는 범위에서 답변을 드리겠습니다.
특히 좌표 이동 동기화 같은 것은 1도 모르는 상태에서 도전하면 헤딩하는 재미가 있습니다.
(사실 채팅 broadcasting이 됐으면, 거의 모든 컨텐츠를 응용해서 만들 수 있다는 의미이기도 합니다)
그럼 힘내세요!

0

장민창님의 프로필

장민창

질문자

2020.07.06

질문이 많았는데, 모두 답변해주셔서 감사합니다.

어쩌면 실례가 될 수도 있는 질문들이였는데, 늦게나마 죄송하다는 말씀드리고 또 감사드린다는 말씀도 함께 드립니다.

좋은 강의 제공해주셔서 감사해요!

다음번 강의도 구매하도록 하겠습니다!

0

안녕하세요,
수업은 단계별로 진행되는 관계로 설명 안 하고 스킵된 부분이 많은데
거기서 걸려서 헤딩을 하고 계시는군요.

우선 예제에서는 클라/서버를 모두 같은 코드를 이용해 동일한 머신 이름을 갖고와 테스트를 했는데
이는 동일한 머신에서 테스트를 하는 것이니 그렇게 한거고,
실제로는 다 데이터시트로 연결 정보가 빠져야 합니다.

서버쪽에서는 어떤 단말이든 접속 가능하도록 열어 두었는데도
클라에서는 연결이 안 되는 이유는
말 그대로 서버 주소를 못 찾아서 (잘못된 주소로 연결을 시도하고 있어서) 연결이 안 되고 있는 것입니다.
무엇이 잘못 되었는지는 클라/서버 코드를 어떻게 하셨는지 모르니 정확히 말씀드릴 수가 없겠네요.

아무튼 간단히 개발 테스트할 때는 하신 것처럼 IP를 이용해 연결하면 되고,
실제로 라이브로 넘어가면 고정 IP가 아니라 Domain 이름 (ex: www.naver.com)을 이용해
DNS을 거쳐 IP를 찾은 다음 그 IP로 연결을 하는 방식으로 만들어야 합니다.
물론 이렇게 하기 위해서는 도메인 등록을 따로 돈주고 하긴 해야겠죠.
꼭 이렇게 해야 하는 이유는, 고정 IP를 클라에 박아놓으면
혹시라도 나중에 서버 쪽 IP가 이런 저런 이유로 바뀌면 아예 서버 연결이 먹통이 되기 때문입니다.
(심지어 IOS 진영에선 고정 IP 박으면 앱 reject 사유가 된다는 말도 얼핏 들었던 기억이 있네요)

SendBufferHelper의 경우 현재 정책은
[아주 큰 버퍼를 할당해서, 조금씩 쪼개서 쓰겠다]로 가고 있는데
말씀하신대로 Close할 때 null로 밀어버리면 그 의미가 퇴색되니,
그럴 바에는 작은 단위로 new를 계속 작게 할당하는 쪽이 나을 수도 있습니다.
메모리 릭이 계속 일어나는 정확한 이유는 확인해봐야겠지만
Session에서 Send 처리가 다 완료되지 않으면,
SendBuffer를 물고 있는 버퍼의 refCount가 0이 되지 않으니 그럴 가능성이 높습니다. (결국 Session쪽 문제)
테스트 환경을 어떻게 하셨는지에 따라 조금 이유가 다를 수 있긴 하지만,
어쨋든 나중에 가면 1초 단위로 클라 쪽에서 ping 패킷
(심장 박동처럼 주기적으로 보내서 연결 여부 확인하는 용도)을
보내서 접속이 끊겼거나 응답하지 않은 클라는 끊어주게 되기 때문에,
이렇게 Send 처리가 먹통된 Session을 처리해주면
의외로 메모리 릭이 자연치유 될 수도 있습니다.

그와는 별개로 학습 단계에선 하나씩 다 직접 해보는 쪽이 좋으니
수동으로 프로토콜 설계/generator까지 만들어봤지만,
최종 프로젝트에선 자체 제작 프로토콜을 사용하지 않고
Google Protobuf을 이식해서 사용해볼 생각입니다. (C++ 서버 연동도 고려하여)
따라서 프로토콜과 관련된 코드가 많이 바뀔 수 있습니다.

0

장민창님의 프로필

장민창

질문자

2020.07.03

몇 가지만 더 추가로 여쭤보고싶은게 있어서 답글로 더 등록해봅니다!

서버 띄어놓고 외부단말 3개정도 접속해보니 메모리가 금방 100MB 이상을 넘어가버리길래 메모리 스냅샷을 떠보니 아래 클래스들에서 메모리가 계속 쌓이는 것을 확인했어요.

1. SendBufferHelper => ArraySegment<byte>

2. ServerPacketManager => ArraySegment<byte>

그래서 코드를 아래처럼 바꾸었더니, 메모리는 계속 증가하지 않는 것 같은데 GC가 되게 빈번하게 일어나고 있어요.

1. SendBufferHelper

        public static ArraySegment<byte> Open(int reserveSize)
        {
            if (CurrentBuffer == null) // 추가
                CurrentBuffer = new ThreadLocal<SendBuffer>(() => null);  // 추가

            ...
            
            return CurrentBuffer.Value.Open(reserveSize);
        }

        public static ArraySegment<byte> Close(int usedSize)
        {
            ArraySegment<Byte> buffer = CurrentBuffer.Value.Close(usedSize);
            CurrentBuffer.Value = null; // 추가
            CurrentBuffer = null; // 추가

            return buffer;
        }

2. ServerPacketManager

public void OnRecvPacket(PacketSession session, ArraySegment<byte> buffer, Action<PacketSession, IPacket> onRecvCallback = null)
    {
        ...
        Func<PacketSession, ArraySegment<byte>, IPacket> func = null;
        if (_makeFunc.TryGetValue(packetId, out func) )
        {
            IPacket packet = func.Invoke(session, buffer);
            buffer = null; // 추가
            if (onRecvCallback != null)
                onRecvCallback.Invoke(session, packet);
            else
                HandlePacket(session, packet);
        }
    }

이렇게 하는 것이 맞을지 아니면 원 상태대로 유지해도 좋을지 의견 부탁드려요!

0

장민창님의 프로필

장민창

질문자

2020.07.03

Server /Listener에 있는 IPEndPoint 생성자의 첫 번째 인자의 의미를 잘 몰랐었는데 찾아보니 허용 IP 목록이라고 하더라고요.

host의 값이 Dns.GetHostName() 은 Local의 주소를 반환하니 로컬에서만 접속이 가능했던것이였습니다.

이걸 IPAddress.Any로 바꿔주니 우선 외부 기기에서는 잘 실행되는 것을 확인했는데

반대로 유니티에서 접속이 안되더라고요.

정확한 이유는 잘 모르겠지만 확인한 바로는

string host = Dns.GetHostName(); // Local PC 의 Host Name
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddress = ipHost.AddressList[0];

ipAddress의 값이 ip가 아닌 LocalPC의 이름으로 반환되는 것을 확인했습니다.

그래서 ipHost.AddressList 배열에 있는 값이 무엇이 있나 찾아보니 LocalPC의 이름이 0번 인덱스에있고 IP가 1번 인덱스에 있는 것을 확인했어요. 

인덱스가 고정되어 들어온다는 보장도 없을 것 같아서 아래코드로 바꿔봤습니다.

//string host = Dns.GetHostName(); // Local PC 의 Host Name
string host = "192.168.0.40";
IPHostEntry ipHost = Dns.GetHostEntry(host);
IPAddress ipAddress = null;
foreach (IPAddress address in ipHost.AddressList)
{
        if(address.ToString().Contains(".")) // IP 형식인지 확인
            ipAddress = address;
        Debug.Log($"Address : {ipAddress}");
}
IPEndPoint endPoint = new IPEndPoint(ipAddress, 17654); // 최종 주소

코드를 위 처럼 바꿨더니 잘 접속이 되는것을 확인했습니다.

서버쪽에서는 어떤 단말이든 접속 가능하도록 열어 두었는데 IP로만 접속이 가능한 구조로 보이는데

이런 현상에는 특별한 이유가 있는 것인가요?