├── LICENSE ├── README.md ├── baccaratServer ├── baccaratServer.go ├── connectedSessions │ ├── session.go │ └── sessionManager.go ├── distributePacket.go ├── docs │ ├── 51_GameStart.puml │ └── 52_Batting.puml ├── go.mod ├── go.sum ├── main.go ├── protocol │ ├── errorcode.go │ ├── packet.go │ └── packetID.go └── roomPkg │ ├── Game.go │ ├── define.go │ ├── room.go │ ├── roomManager.go │ ├── roomUser.go │ ├── room_Packet.go │ ├── room_PacketChat.go │ ├── room_PacketEnter.go │ ├── room_PacketGame.go │ └── room_PacketLeave.go ├── bin ├── config_logger.json ├── run_baccaratServer.bat ├── run_chatServer.bat ├── run_chatServer2.bat └── run_echoServer.bat ├── chatServer ├── chatServer.go ├── connectedSessions │ ├── session.go │ └── sessionManager.go ├── distributePacket.go ├── go.mod ├── go.sum ├── logger.go ├── main.go ├── protocol │ ├── errorcode.go │ ├── packet.go │ └── packetID.go └── roomPkg │ ├── define.go │ ├── room.go │ ├── roomManager.go │ ├── room_Packet.go │ ├── room_PacketChat.go │ ├── room_PacketEnter.go │ └── room_PacketLeave.go ├── chatServer2 ├── chatServer.go ├── clientSessionEvent.go ├── configAppServer.go ├── connectedSessions │ ├── checkState.go │ ├── session.go │ └── sessionManager.go ├── distributePacket.go ├── go.mod ├── go.sum ├── main.go ├── protocol │ ├── errorcode.go │ ├── internalPacket.go │ ├── packet.go │ └── packetID.go ├── roomPkg │ ├── define.go │ ├── room.go │ ├── roomManager.go │ ├── roomPacketDistributor.go │ ├── roomPacketPipe.go │ ├── room_Packet.go │ ├── room_PacketChat.go │ └── room_PacketEnterLeave.go └── timerScheduler.go ├── chatServer_msgpack ├── chatServer.go ├── connectedSessions │ ├── session.go │ └── sessionManager.go ├── distributePacket.go ├── go.mod ├── go.sum ├── main.go ├── protocol │ ├── errorcode.go │ ├── packet.go │ └── packetID.go └── roomPkg │ ├── define.go │ ├── room.go │ ├── roomManager.go │ ├── room_Packet.go │ ├── room_PacketChat.go │ ├── room_PacketEnter.go │ └── room_PacketLeave.go ├── csharp_test_client ├── App.config ├── ClientSimpleTcp.cs ├── DevLog.cs ├── Packet.cs ├── PacketBufferManager.cs ├── PacketDefine.cs ├── PacketProcessForm.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── csharp_test_client.csproj ├── csharp_test_client.sln ├── mainForm.Designer.cs ├── mainForm.cs └── mainForm.resx ├── csharp_test_client_msgpack ├── App.config ├── ClientSimpleTcp.cs ├── DevLog.cs ├── Packet.cs ├── PacketBufferManager.cs ├── PacketDefine.cs ├── PacketProcessForm.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── csharp_test_client_msgpack.csproj ├── csharp_test_client_msgpack.sln ├── mainForm.Designer.cs ├── mainForm.cs └── mainForm.resx ├── echoServer ├── echoServer.go ├── go.mod ├── go.sum └── main.go ├── gohipernetFake ├── TcpSession.go ├── clientSessionManager.go ├── configNetwork.go ├── define.go ├── go.mod ├── go.sum ├── goHiperNet.go ├── goHiperNet_Impl.go ├── log.go ├── packetEnDecoder.go ├── utilDeque.go └── utilPrintPanicStack.go ├── lib_opensource ├── 1m-go-tcp-server-master.zip ├── 1m-go-websockets-master.zip └── gnet-dev.zip ├── socket_api.md.md └── thirdparty └── SimpleMsgPack.Net ├── .gitignore ├── LICENSE ├── README.md ├── Samples ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── SimpleMsgPackTester.csproj ├── SimpleMsgPack.sln ├── Source ├── BytesTools.cs ├── Consts.cs ├── MsgPack.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── ReadTools.cs ├── SimpleMsgPack.csproj └── WriteTools.cs └── changes.txt /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Choi HeungBae 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golang_socketGameServer_codelab 2 | - golang을 이용하여 실시간 통신 게임 서버 만들기 실습. 3 | - 각 서버의 원본 코드를 하나씩 따라서 코딩하면서 서버 만드는 방법을 배운다. 4 | - 코딩하면서 해당 코드의 구현 방법과 이유를 설명 듣는다. 5 | 6 | **버그가 있을 수 있습니다**. 버그 잡아서 수정하는 것도 학습 중 일부라고 생각해 주세요^^;. 7 | 8 | 9 | ## 목적 10 | - golang으로 소켓 통신용 서버를 만들 수 있는 기술고 경험을 쌓는 것이 목표이다. 11 | - golang으로 소켓 통신용 서버를 만든 경험이 없는(있더라도 작은) 사람을 대상으로 한다. 12 | - golang의 socket API를 사용하지 않고, 센트럴서버팀에서 만든 goHiperNet(golang 네트워크 라이브러리)의 짝퉁(?)을 사용한다. 13 | - 이 라이브러리는 goHiperNet과 API만 같고, 내부 구현은 완전 다르다. 14 | - 학습용으로 충분히 사용할 수 있다. 15 | - golang의 socket API를 사용하여 밑바닥부터 개발하는 방법을 배우고 싶다면 별도 요청을 바람. 16 | - 실습은 단계 별로 진행하고, 각 단계 별로 소요 시간은 다르다. 17 | - 시간이 많이 필요한 경우라도 1번에 최대 3시간을 넘지 않는다. 18 | - 한번에 너무 많이 나가면 뒤에 복습이 어려워지기 때문이다. 19 | 20 | 21 | ## 준비 22 | - 1인 1 노트북(Windows or OSX) 23 | - 최신 버전의 golang SDK 24 | - 최신 버전의 GoLand 25 | - 기본 golang의 문법 학습 26 | - 코딩은 한번도 해본적인 없는 경우도 괜찮음. 27 | 28 | 29 | ## 패킷 헤더 30 | 패킷 허더의 크기는 총 5바이트 31 | - 패킷의 총 크기(2바이트. 헤더와 보디 합친) + 패킷ID(2바이트) + 패킷Type(1바이트) 32 | 33 | 34 | ## 설명 영상 35 | - [실습 목적과 방법 설명](https://youtu.be/zR_zcY7SXio ) 36 | - [Echo Server 만들기](https://youtu.be/OSiwcsPAO2o ) 37 | - [채팅 서버 코드 설명](https://youtu.be/2rppKuW-wQg ) 38 | 39 | 40 | ## echoServer 41 | - 디렉토리: echoServer 42 | - GoLand를 사용하여 golang용 프로그램을 만들고, 빌드/디버깅을 한다. 43 | - 아주 간단한 규모이다. 44 | 45 | 46 | ## chatServer 47 | - 디렉토리: chatServer 48 | - 방 개념의 채팅 서버 49 | - 패킷 요청 처리를 1개의 고루틴(스레드)에서만 한다. 50 | - echoServer에 비해 규모는 3~4배 크다. 51 | 52 | ### 추가 기능 구현 53 | - 1:1 귓속말 54 | - 방 초대 55 | 56 | 57 | 58 | ## baccaratServer 59 | - 디렉토리: baccaratServer 60 | - 겜블 게임인 바카라 게임을 온라인화 한 것이다. 61 | - 바카라 룰: https://namu.wiki/w/%EB%B0%94%EC%B9%B4%EB%9D%BC 62 | - chatServer에 바카라 게임 로직이 올라간 것으로 chatServer에 대한 이해가 꼭 필요하다. 63 | 64 | ### 추가 기능 구현 65 | - 게임 서버 Scale-Out 기능 구현 66 | - API Server(http)와 연동 67 | - 유저를 특정 게임 서버에 할당하는 기능 68 | - 매칭 기능 69 | 70 | 71 | 72 | ## chatServer2 73 | - 디렉토리: chatServer2 74 | - 방 개념의 채팅 서버 75 | - 패킷 요청 처리를 N개의 고루틴(스레드)에서 한다. 76 | - 패킷 처리를 멀티 고루틴에서 하므로 공유 객체 동기화를 조심해야 한다. 77 | - chatServer의 코드와 겹치는 부분이 많으므로 chatServer에 대한 이해가 꼭 필요하다 78 | 79 | ### 추가 기능 구현 80 | - Redis 연동 81 | - API Server(http)와 연동 82 | - 로그인을 API Server에서 한다. 83 | 84 | 85 | ## msgpack을 사용한 chatServer 86 | - 디렉토리: chatServer_msgpack 87 | - 클라이언트 디렉토리: csharp_test_client_msgpack 88 | - 서버와 클라이언트가 네트워크로 주고 받는 패킷 데이터 포맷을 msgpack을 사용한다. 89 | - [Go](https://github.com/vmihailenco/msgpack ) 90 | - [C#](https://github.com/ymofen/SimpleMsgPack.Net ) 91 | - golang 라이브러리와 데이터 포맷이 일치하지 않는 부분이 있어서 코드를 수정하였음. 92 | - thirdparty/SimpleMsgPack.Net 디렉토리에 코드가 있다/ 93 | 94 | 95 | ## 참고 96 | - [유튜브: 오픈소스 코드로 배우는 Golang TCP Socket Server 프로그래밍 ](https://youtu.be/boDo8JoyHuo ) 97 | 98 | -------------------------------------------------------------------------------- /baccaratServer/baccaratServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | . "gohipernetFake" 6 | "strconv" 7 | "strings" 8 | 9 | "main/connectedSessions" 10 | "main/protocol" 11 | "main/roomPkg" 12 | ) 13 | 14 | type configAppServer struct { 15 | GameName string 16 | 17 | RoomMaxCount int32 18 | RoomStartNum int32 19 | RoomMaxUserCount int32 20 | } 21 | 22 | type ChatServer struct { 23 | ServerIndex int 24 | IP string 25 | Port int 26 | 27 | PacketChan chan protocol.Packet 28 | 29 | RoomMgr *roomPkg.RoomManager 30 | } 31 | 32 | func createAnsStartServer(netConfig NetworkConfig, appConfig configAppServer) { 33 | OutPutLog(LOG_LEVEL_INFO,"", 0,"CreateServer !!!") 34 | 35 | var server ChatServer 36 | 37 | if server.setIPAddress(netConfig.BindAddress) == false { 38 | OutPutLog(LOG_LEVEL_ERROR,"", 0,"fail. server address") 39 | return 40 | } 41 | 42 | protocol.Init_packet(); 43 | 44 | maxUserCount := appConfig.RoomMaxCount * appConfig.RoomMaxUserCount 45 | connectedSessions.Init(netConfig.MaxSessionCount, maxUserCount) 46 | 47 | server.PacketChan = make(chan protocol.Packet, 256) 48 | 49 | roomConfig := roomPkg.RoomConfig{ 50 | appConfig.RoomStartNum, 51 | appConfig.RoomMaxCount, 52 | appConfig.RoomMaxUserCount, 53 | } 54 | server.RoomMgr = roomPkg.NewRoomManager(roomConfig) 55 | 56 | 57 | go server.PacketProcess_goroutine() 58 | 59 | 60 | networkFunctor := SessionNetworkFunctors{} 61 | networkFunctor.OnConnect = server.OnConnect 62 | networkFunctor.OnReceive = server.OnReceive 63 | networkFunctor.OnReceiveBufferedData = nil 64 | networkFunctor.OnClose = server.OnClose 65 | networkFunctor.PacketTotalSizeFunc = PacketTotalSize 66 | networkFunctor.PacketHeaderSize = PACKET_HEADER_SIZE 67 | networkFunctor.IsClientSession = true 68 | 69 | NetLibStartNetwork(&netConfig, networkFunctor) 70 | } 71 | 72 | func (server *ChatServer) setIPAddress(ipAddress string) bool { 73 | results := strings.Split(ipAddress, ":") 74 | if len(results) != 2 { 75 | return false 76 | } 77 | 78 | server.IP = results[0] 79 | server.Port, _ = strconv.Atoi(results[1]) 80 | 81 | return true 82 | } 83 | 84 | func (server *ChatServer) OnConnect(sessionIndex int32, sessionUniqueID uint64) { 85 | OutPutLog(LOG_LEVEL_INFO,"", 0,fmt.Sprintf("[OnConnect] sessionIndex: %d", sessionIndex)) 86 | 87 | connectedSessions.AddSession(sessionIndex, sessionUniqueID) 88 | } 89 | 90 | func (server *ChatServer) OnReceive(sessionIndex int32, sessionUniqueID uint64, data []byte) bool { 91 | server.DistributePacket(sessionIndex, sessionUniqueID, data) 92 | return true 93 | } 94 | 95 | func (server *ChatServer) OnClose(sessionIndex int32, sessionUniqueID uint64) { 96 | OutPutLog(LOG_LEVEL_INFO,"", 0,fmt.Sprintf("[OnClose] sessionIndex: %d", sessionIndex)) 97 | 98 | server.disConnectClient(sessionIndex, sessionUniqueID) 99 | } 100 | 101 | func (server *ChatServer) disConnectClient(sessionIndex int32, sessionUniqueId uint64) { 102 | // 로그인도 안한 유저라면 그냥 여기서 처리한다. 103 | // 방 입장을 안한 유저라면 여기서 처리해도 괜찮지만 아래로 넘긴다. 104 | if connectedSessions.IsLoginUser(sessionIndex) == false { 105 | connectedSessions.RemoveSession(sessionIndex, false) 106 | return 107 | } 108 | 109 | 110 | packet := protocol.Packet { 111 | sessionIndex, 112 | sessionUniqueId, 113 | protocol.PACKET_ID_SESSION_CLOSE_SYS, 114 | 0, 115 | nil, 116 | } 117 | 118 | server.PacketChan <- packet 119 | 120 | OutPutLog(LOG_LEVEL_INFO,"", 0,fmt.Sprintf("[DisConnectClient] Login User sessionIndex: %d", sessionIndex)) 121 | } 122 | -------------------------------------------------------------------------------- /baccaratServer/connectedSessions/session.go: -------------------------------------------------------------------------------- 1 | package connectedSessions 2 | 3 | import ( 4 | "sync/atomic" 5 | 6 | "main/protocol" 7 | ) 8 | 9 | type session struct { 10 | _index int32 11 | 12 | _networkUniqueID uint64 //네트워크 세션의 유니크 ID 13 | 14 | _userID [protocol.MAX_USER_ID_BYTE_LENGTH]byte 15 | _userIDLength int8 16 | 17 | _connectTimeSec int64 // 연결된 시간 18 | _RoomNum int32 // 19 | _RoomNumOfEntering int32 // 현재 입장 중인 룸의 번호 20 | } 21 | 22 | func (session *session) Init(index int32) { 23 | session._index = index 24 | session.Clear() 25 | } 26 | 27 | func (session *session) _ClearUserId() { 28 | session._userIDLength = 0 29 | } 30 | 31 | func (session *session) Clear() { 32 | session._ClearUserId() 33 | session.setRoomNumber(0, -1, 0) 34 | session.SetConnectTimeSec(0, 0) 35 | } 36 | 37 | func (session *session) GetIndex() int32 { 38 | return session._index 39 | } 40 | 41 | func (session *session) GetNetworkUniqueID() uint64 { 42 | return atomic.LoadUint64(&session._networkUniqueID) 43 | } 44 | 45 | func (session *session) validNetworkUniqueID(uniqueId uint64) bool { 46 | return atomic.LoadUint64(&session._networkUniqueID) == uniqueId 47 | } 48 | 49 | func (session *session) GetNetworkInfo() (int32, uint64) { 50 | index := session.GetIndex() 51 | uniqueID := atomic.LoadUint64(&session._networkUniqueID) 52 | return index, uniqueID 53 | } 54 | 55 | func (session *session) setUserID(userID []byte) { 56 | session._userIDLength = int8(len(userID)) 57 | copy(session._userID[:], userID) 58 | } 59 | 60 | func (session *session) getUserID() []byte { 61 | return session._userID[0:session._userIDLength] 62 | } 63 | 64 | func (session *session) getUserIDLength() int8 { 65 | return session._userIDLength 66 | } 67 | 68 | func (session *session) SetConnectTimeSec(timeSec int64, uniqueID uint64) { 69 | atomic.StoreInt64(&session._connectTimeSec, timeSec) 70 | atomic.StoreUint64(&session._networkUniqueID, uniqueID) 71 | } 72 | 73 | func (session *session) GetConnectTimeSec() int64 { 74 | return atomic.LoadInt64(&session._connectTimeSec) 75 | } 76 | 77 | func (session *session) SetUser(sessionUniqueId uint64, 78 | userID []byte, 79 | curTimeSec int64, 80 | ) { 81 | session.setUserID(userID) 82 | session.setRoomNumber(sessionUniqueId, -1, curTimeSec) // 방어적인 목적으로 채널 번호 초기화 83 | } 84 | 85 | func (session *session) IsAuth() bool { 86 | if session._userIDLength > 0 { 87 | return true 88 | } 89 | 90 | return false 91 | } 92 | 93 | func (session *session) setRoomEntering(roomNum int32) bool { 94 | if atomic.CompareAndSwapInt32(&session._RoomNumOfEntering, -1, roomNum) == false { 95 | return false 96 | } 97 | 98 | return true 99 | } 100 | 101 | func (session *session) setRoomNumber(sessionUniqueId uint64, roomNum int32, curTimeSec int64) bool { 102 | if roomNum == -1 { 103 | atomic.StoreInt32(&session._RoomNum, roomNum) 104 | atomic.StoreInt32(&session._RoomNumOfEntering, roomNum) 105 | return true 106 | } 107 | 108 | if sessionUniqueId != 0 && session.validNetworkUniqueID(sessionUniqueId) == false { 109 | return false 110 | 111 | } 112 | // 입력이 -1이 아닌경우 -1이 아닐 때만 compareswap으로 변경한다. 실패하면 채널 입장도 실패이다. 113 | if atomic.CompareAndSwapInt32(&session._RoomNum, -1, roomNum) == false { 114 | return false 115 | } 116 | 117 | atomic.StoreInt32(&session._RoomNumOfEntering, roomNum) 118 | return true 119 | } 120 | 121 | func (session *session) getRoomNumber() (int32, int32) { 122 | roomNum := atomic.LoadInt32(&session._RoomNum) 123 | roomNumOfEntering := atomic.LoadInt32(&session._RoomNum) 124 | return roomNum, roomNumOfEntering 125 | } 126 | -------------------------------------------------------------------------------- /baccaratServer/connectedSessions/sessionManager.go: -------------------------------------------------------------------------------- 1 | package connectedSessions 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | // 스레드 세이프 해야 한다. 10 | type Manager struct { 11 | _UserIDsessionMap *sync.Map 12 | 13 | _maxSessionCount int32 14 | _sessionList []*session 15 | 16 | _maxUserCount int32 17 | 18 | _currentLoginUserCount int32 19 | } 20 | 21 | var _manager Manager 22 | 23 | func Init(maxSessionCount int, maxUserCount int32) bool { 24 | _manager._UserIDsessionMap = new(sync.Map) 25 | _manager._maxUserCount = maxUserCount 26 | 27 | _manager._maxSessionCount = int32(maxSessionCount) 28 | _manager._sessionList = make([]*session, maxSessionCount) 29 | 30 | for i := 0; i < maxSessionCount; i++ { 31 | _manager._sessionList[i] = new(session) 32 | 33 | index := int32(i) 34 | _manager._sessionList[i].Init(index) 35 | } 36 | 37 | return true 38 | } 39 | 40 | func AddSession(sessionIndex int32, sessionUniqueID uint64) bool { 41 | if _validSessionIndex(sessionIndex) == false { 42 | return false 43 | } 44 | 45 | if _manager._sessionList[sessionIndex].GetConnectTimeSec() > 0 { 46 | return false 47 | } 48 | 49 | // 방어적인 목적으로 한번 더 Clear 한다 50 | _manager._sessionList[sessionIndex].Clear() 51 | 52 | _manager._sessionList[sessionIndex].SetConnectTimeSec(time.Now().Unix(), sessionUniqueID) 53 | return true 54 | } 55 | 56 | func RemoveSession(sessionIndex int32, isLoginedUser bool) bool { 57 | if _validSessionIndex(sessionIndex) == false { 58 | return false 59 | } 60 | 61 | if isLoginedUser { 62 | atomic.AddInt32(&_manager._currentLoginUserCount, -1) 63 | 64 | userID := string(_manager._sessionList[sessionIndex].getUserID()) 65 | _manager._UserIDsessionMap.Delete(userID) 66 | } 67 | 68 | _manager._sessionList[sessionIndex].Clear() 69 | 70 | return true 71 | } 72 | 73 | func _validSessionIndex(index int32) bool { 74 | if index < 0 || index >= _manager._maxSessionCount { 75 | return false 76 | } 77 | return true 78 | } 79 | 80 | func GetNetworkUniqueID(sessionIndex int32) uint64 { 81 | if _validSessionIndex(sessionIndex) == false { 82 | return 0 83 | } 84 | 85 | return _manager._sessionList[sessionIndex].GetNetworkUniqueID() 86 | } 87 | 88 | func GetUserID(sessionIndex int32) ([]byte, bool) { 89 | if _validSessionIndex(sessionIndex) == false { 90 | return nil, false 91 | } 92 | 93 | return _manager._sessionList[sessionIndex].getUserID(), true 94 | } 95 | 96 | func SetLogin(sessionIndex int32, sessionUniqueId uint64, userID []byte, curTimeSec int64) bool { 97 | if _validSessionIndex(sessionIndex) == false { 98 | return false 99 | } 100 | 101 | newUserID := string(userID) 102 | if _, ok := _manager._UserIDsessionMap.Load(newUserID); ok { 103 | return false 104 | } 105 | 106 | _manager._sessionList[sessionIndex].SetUser(sessionUniqueId, userID, curTimeSec) 107 | _manager._UserIDsessionMap.Store(newUserID, _manager._sessionList[sessionIndex]) 108 | 109 | atomic.AddInt32(&_manager._currentLoginUserCount, 1) 110 | return true 111 | } 112 | 113 | func IsLoginUser(sessionIndex int32) bool { 114 | if _validSessionIndex(sessionIndex) == false { 115 | return false 116 | } 117 | 118 | return _manager._sessionList[sessionIndex].IsAuth() 119 | } 120 | 121 | func SetRoomNumber(sessionIndex int32, sessionUniqueId uint64, roomNum int32, curTimeSec int64) bool { 122 | if _validSessionIndex(sessionIndex) == false { 123 | return false 124 | } 125 | 126 | return _manager._sessionList[sessionIndex].setRoomNumber(sessionUniqueId, roomNum, curTimeSec) 127 | } 128 | 129 | func GetRoomNumber(sessionIndex int32) (int32, int32) { 130 | if _validSessionIndex(sessionIndex) == false { 131 | return -1, -1 132 | } 133 | return _manager._sessionList[sessionIndex].getRoomNumber() 134 | } 135 | -------------------------------------------------------------------------------- /baccaratServer/distributePacket.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "time" 6 | 7 | . "gohipernetFake" 8 | 9 | "main/connectedSessions" 10 | "main/protocol" 11 | ) 12 | 13 | func (server *ChatServer) DistributePacket(sessionIndex int32, 14 | sessionUniqueId uint64, 15 | packetData []byte, 16 | ) { 17 | packetID := protocol.PeekPacketID(packetData) 18 | bodySize, bodyData := protocol.PeekPacketBody(packetData) 19 | 20 | packet := protocol.Packet{Id: packetID} 21 | packet.UserSessionIndex = sessionIndex 22 | packet.UserSessionUniqueId = sessionUniqueId 23 | packet.Id = packetID 24 | packet.DataSize = bodySize 25 | packet.Data = make([]byte, packet.DataSize) 26 | copy(packet.Data, bodyData) 27 | 28 | server.PacketChan <- packet 29 | } 30 | 31 | 32 | func (server *ChatServer) PacketProcess_goroutine() { 33 | for { 34 | if server.PacketProcess_goroutine_Impl() { 35 | OutPutLog(LOG_LEVEL_INFO,"", 0,"Wanted Stop PacketProcess goroutine") 36 | break 37 | } 38 | } 39 | 40 | OutPutLog(LOG_LEVEL_INFO,"", 0,"Stop rooms PacketProcess goroutine") 41 | } 42 | 43 | func (server *ChatServer) PacketProcess_goroutine_Impl() bool { 44 | IsWantedTermination := false // 이 서버에서는 별 의미가 없음 45 | defer PrintPanicStack() 46 | 47 | secondTimeticker := time.NewTicker(time.Second) 48 | defer secondTimeticker.Stop() 49 | 50 | 51 | for { 52 | select { 53 | case packet := <-server.PacketChan: 54 | { 55 | sessionIndex := packet.UserSessionIndex 56 | sessionUniqueId := packet.UserSessionUniqueId 57 | bodySize := packet.DataSize 58 | bodyData := packet.Data 59 | 60 | if packet.Id == protocol.PACKET_ID_LOGIN_REQ { 61 | ProcessPacketLogin(sessionIndex, sessionUniqueId, bodySize, bodyData) 62 | } else if packet.Id == protocol.PACKET_ID_SESSION_CLOSE_SYS { 63 | ProcessPacketSessionClosed(server, sessionIndex, sessionUniqueId) 64 | } else { 65 | roomNumber, _ := connectedSessions.GetRoomNumber(sessionIndex) 66 | server.RoomMgr.PacketProcess(roomNumber, packet) 67 | } 68 | } 69 | case curTime := <-secondTimeticker.C: 70 | { 71 | server.RoomMgr.CheckRoomState(curTime.Unix()) 72 | } 73 | } 74 | } 75 | 76 | return IsWantedTermination 77 | } 78 | 79 | func ProcessPacketLogin(sessionIndex int32, 80 | sessionUniqueId uint64, 81 | bodySize int16, 82 | bodyData []byte ) { 83 | //DB와 연동하지 않으므로 중복 로그인만 아니면 다 성공으로 한다 84 | var request protocol.LoginReqPacket 85 | if (&request).Decoding(bodyData) == false { 86 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_PACKET_DECODING_FAIL) 87 | return 88 | } 89 | 90 | userID := bytes.Trim(request.UserID[:], "\x00"); 91 | 92 | if len(userID) <= 0 { 93 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_LOGIN_USER_INVALID_ID) 94 | return 95 | } 96 | 97 | curTime := time.Now().Unix() 98 | 99 | if connectedSessions.SetLogin(sessionIndex, sessionUniqueId, userID, curTime) == false { 100 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_LOGIN_USER_DUPLICATION) 101 | return 102 | } 103 | 104 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_NONE) 105 | } 106 | 107 | func _sendLoginResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 108 | var response protocol.LoginResPacket 109 | response.Result = result 110 | sendPacket, _ := response.EncodingPacket() 111 | 112 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 113 | } 114 | 115 | 116 | func ProcessPacketSessionClosed(server *ChatServer, sessionIndex int32, sessionUniqueId uint64) { 117 | roomNumber, _ := connectedSessions.GetRoomNumber(sessionIndex) 118 | 119 | if roomNumber > -1 { 120 | packet := protocol.Packet{ 121 | sessionIndex, 122 | sessionUniqueId, 123 | protocol.PACKET_ID_ROOM_LEAVE_REQ, 124 | 0, 125 | nil, 126 | } 127 | 128 | server.RoomMgr.PacketProcess(roomNumber, packet) 129 | } 130 | 131 | connectedSessions.RemoveSession(sessionIndex, true) 132 | } 133 | 134 | 135 | -------------------------------------------------------------------------------- /baccaratServer/docs/51_GameStart.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | note over Client: 방장만 보낼 수 있음 3 | Client --> GameServer: PACKET_ID_GAME_START_REQ 4 | 5 | GameServer --> Client: PACKET_ID_GAME_START_RES 6 | 7 | note over Client: 방에 있는 모든 유저들 8 | GameServer --> Client: PACKET_ID_GAME_START_NTF 9 | @enduml -------------------------------------------------------------------------------- /baccaratServer/docs/52_Batting.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | Client --> GameServer: PACKET_ID_GAME_BATTING_REQ 3 | 4 | GameServer --> Client: PACKET_ID_GAME_BATTING_RES 5 | 6 | note over GameServer: 방에 있는 모든 유저들 7 | GameServer --> Client: PACKET_ID_GAME_BATTING_NTF 8 | 9 | note over GameServer: 모두 배팅 했거나 or 대기 시간이 지나면 게임 결과를 보낸다 10 | GameServer --> Client: PACKET_ID_GAME_RESULT_NTF 11 | note right GameServer: 일정 시간이 지나면 게임 플레이 가능 12 | @enduml -------------------------------------------------------------------------------- /baccaratServer/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | go.uber.org/atomic v1.4.0 // indirect 8 | go.uber.org/multierr v1.1.0 // indirect 9 | go.uber.org/zap v1.10.0 10 | gohipernetFake v0.0.0 11 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect 12 | ) 13 | 14 | replace gohipernetFake v0.0.0 => ../gohipernetFake 15 | -------------------------------------------------------------------------------- /baccaratServer/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= 4 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 5 | go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= 6 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 7 | go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= 8 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 9 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 10 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 11 | -------------------------------------------------------------------------------- /baccaratServer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | 6 | . "gohipernetFake" 7 | ) 8 | 9 | func main() { 10 | NetLibInitLog(LOG_LEVEL_DEBUG, nil) 11 | 12 | netConfig, appConfig := parseAppConfig() 13 | netConfig.WriteNetworkConfig(true) 14 | 15 | // 아래 함수를 호출하면 강제적으로 종료 시킬 때까지 대기 상태가 된다. 16 | createAnsStartServer(netConfig, appConfig) 17 | } 18 | 19 | func parseAppConfig() (NetworkConfig, configAppServer) { 20 | appConfig := configAppServer{ 21 | "chatServer", 22 | 1000, 23 | 0, 24 | 4, 25 | } 26 | 27 | netConfig := NetworkConfig{} 28 | 29 | flag.BoolVar(&netConfig.IsTcp4Addr, "c_IsTcp4Addr", true, "bool flag") 30 | flag.StringVar(&netConfig.BindAddress, "c_BindAddress", "127.0.0.1:11021", "string flag") 31 | flag.IntVar(&netConfig.MaxSessionCount, "c_MaxSessionCount", 0, "int flag") 32 | flag.IntVar(&netConfig.MaxPacketSize, "c_MaxPacketSize", 0, "int flag") 33 | 34 | flag.Parse() 35 | return netConfig, appConfig 36 | } 37 | -------------------------------------------------------------------------------- /baccaratServer/protocol/errorcode.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | const ( 4 | ERROR_CODE_NONE = 1 5 | 6 | ERROR_CODE_PACKET_DECODING_FAIL = 51 7 | ERROR_CODE_PACKET_NOT_LOGIN_USER = 52 8 | ERROR_CODE_ROOM_NOT_REGISTED_PACKET_ID = 53 9 | ERROR_CODE_USER_NOT_IN_ROOM = 54 10 | 11 | ERROR_CODE_DISCONNECT_UNAUTHENTICATED_USER = 61 12 | 13 | ERROR_CODE_LOGIN_USER_DUPLICATION = 111 14 | ERROR_CODE_LOGIN_USER_INVALID_ID = 112 15 | 16 | ERROR_CODE_ROOM_NOT_IN_USER = 121 17 | ERROR_CODE_ROOM_INVALIDE_NUMBER = 122 18 | 19 | ERROR_CODE_ENTER_ROOM_ALREADY = 131 20 | ERROR_CODE_ENTER_ROOM_PREV_WORKING = 132 21 | ERROR_CODE_ENTER_ROOM_INVALID_USER_ID = 133 22 | ERROR_CODE_ENTER_ROOM_USER_FULL = 134 23 | ERROR_CODE_ENTER_ROOM_DUPLCATION_USER = 135 24 | ERROR_CODE_ENTER_ROOM_INVALID_SESSION_STATE = 136 25 | ERROR_CODE_ENTER_ROOM_AUTO_ROOM_NUMBER = 137 26 | 27 | ERROR_CODE_LEAVE_ROOM_INTERNAL_INVALID_USER = 141 28 | 29 | ERROR_CODE_ROOM_CHAT_CHAT_MSG_LEN = 151 30 | 31 | ERROR_CODE_ROOM_RELAY_FAIL_DECPDING = 161 32 | 33 | ERROR_CODE_ROOM_GAME_START_INVALID_ROOM_STATE = 171 34 | ERROR_CODE_ROOM_GAME_START_NOT_ENOUGH_MEMBERS = 172 35 | ERROR_CODE_ROOM_GAME_START_NOT_MASTER = 173 36 | 37 | ERROR_CODE_ROOM_GAME_BATTING_FAIL_PACKET = 181 38 | ERROR_CODE_ROOM_GAME_BATTING_INVALID_ROOM_STATE = 182 39 | ERROR_CODE_ROOM_GAME_BATTING_INVALID_BAT_SELECT = 183 40 | ERROR_CODE_ROOM_GAME_BATTING_SAME_BAT_SELECT = 184 41 | ) 42 | -------------------------------------------------------------------------------- /baccaratServer/protocol/packetID.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | 4 | const ( 5 | PACKET_ID_PING_REQ = 201 6 | PACKET_ID_PING_RES = 202 7 | 8 | PACKET_ID_ERROR_NTF = 203 9 | 10 | PACKET_ID_SESSION_CLOSE_SYS = 211 11 | 12 | 13 | PACKET_ID_LOGIN_REQ = 701 14 | PACKET_ID_LOGIN_RES = 702 15 | 16 | PACKET_ID_ROOM_ENTER_REQ = 721 17 | PACKET_ID_ROOM_ENTER_RES = 722 18 | PACKET_ID_ROOM_USER_LIST_NTF = 723 19 | PACKET_ID_ROOM_NEW_USER_NTF = 724 20 | 21 | PACKET_ID_ROOM_LEAVE_REQ = 726 22 | PACKET_ID_ROOM_LEAVE_RES = 727 23 | PACKET_ID_ROOM_LEAVE_USER_NTF = 728 24 | 25 | PACKET_ID_ROOM_CHAT_REQ = 731 26 | PACKET_ID_ROOM_CHAT_RES = 732 27 | PACKET_ID_ROOM_CHAT_NOTIFY = 733 28 | 29 | PACKET_ID_ROOM_RELAY_REQ = 741 30 | PACKET_ID_ROOM_RELAY_NTF = 742 31 | 32 | PACKET_ID_GAME_START_REQ = 751 33 | PACKET_ID_GAME_START_RES = 752 34 | PACKET_ID_GAME_START_NTF = 753 35 | 36 | PACKET_ID_GAME_BATTING_REQ = 761 37 | PACKET_ID_GAME_BATTING_RES = 762 38 | PACKET_ID_GAME_BATTING_NTF = 753 39 | 40 | PACKET_ID_GAME_RESULT_NTF = 764 41 | 42 | ) 43 | 44 | 45 | -------------------------------------------------------------------------------- /baccaratServer/roomPkg/define.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | type RoomConfig struct { 4 | StartRoomNumber int32 5 | MaxRoomCount int32 6 | MaxUserCount int32 7 | } 8 | 9 | 10 | type addRoomUserInfo struct { 11 | userID []byte 12 | 13 | netSessionIndex int32 14 | netSessionUniqueId uint64 15 | } 16 | 17 | // 방의 상태 18 | const ( 19 | ROOM_STATE_NOE = 1 20 | ROOM_STATE_GAME_WAIT_BATTING = 2 21 | ROOM_STATE_GAME_RESULT = 3 22 | ) 23 | 24 | // 카드 정보 25 | const MAX_CARD_CONT = 52 26 | const CARD_ROW_COUNT = 13 27 | // 카드 순서 스페이드, 다이아몬드, 클로버, 하트 A,2,3,4,5,6,7,8,9,10,J,Q,K 28 | func makeCard() []int8 { 29 | a := make([]int8, MAX_CARD_CONT) 30 | for i := range a { 31 | a[i] = (int8)(i) 32 | } 33 | return a 34 | } 35 | 36 | const BATTING_WAIT_MILLISEC = 5000 37 | const NEXT_GAME_WAIT_MILLISEC = 10000 38 | 39 | const ( 40 | BATTING_SELECT_NONE = 0 41 | BATTING_SELECT_PLAYER = 1 42 | BATTING_SELECT_BANKER = 2 43 | ) 44 | 45 | // 게임 결과 46 | const ( 47 | GAME_RESULT_WIN_PLAYER = 1 48 | GAME_RESULT_WIN_BANKER = 2 49 | GAME_RESULT_TIE = 3 50 | ) 51 | 52 | type baccaratGameResultInfo struct { 53 | cardsBanker [3]int8 54 | cardsPlayer [3]int8 55 | 56 | playerScore int8 57 | bankerScore int8 58 | 59 | result int8 60 | } 61 | 62 | -------------------------------------------------------------------------------- /baccaratServer/roomPkg/roomManager.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "main/protocol" 5 | ) 6 | 7 | 8 | type RoomManager struct { 9 | _roomStartNum int32 10 | _maxRoomCount int32 11 | _roomCountList []int16 12 | _roomList []baseRoom 13 | } 14 | 15 | func NewRoomManager(config RoomConfig) *RoomManager { 16 | roomManager := new(RoomManager) 17 | roomManager._initialize(config) 18 | return roomManager 19 | } 20 | 21 | func (roomMgr *RoomManager) _initialize(config RoomConfig) { 22 | roomMgr._roomStartNum = config.StartRoomNumber 23 | roomMgr._maxRoomCount = config.MaxRoomCount 24 | roomMgr._roomCountList = make([]int16, config.MaxRoomCount) 25 | roomMgr._roomList = make([]baseRoom, config.MaxRoomCount) 26 | 27 | for i := int32(0); i < roomMgr._maxRoomCount; i++ { 28 | roomMgr._roomList[i].initialize(i, config) 29 | roomMgr._roomList[i].settingPacketFunction() 30 | } 31 | } 32 | 33 | func (roomMgr *RoomManager) GetAllChannelUserCount() []int16 { 34 | maxRoomCount := roomMgr._maxRoomCount 35 | for i := int32(0); i < maxRoomCount; i++ { 36 | roomMgr._roomCountList[i] = (int16)(roomMgr._getRoomUserCount(i)) 37 | } 38 | 39 | return roomMgr._roomCountList 40 | } 41 | 42 | func (roomMgr *RoomManager) getRoomByNumber(roomNumber int32) *baseRoom { 43 | roomIndex := roomNumber - roomMgr._roomStartNum 44 | 45 | if roomNumber < 0 || roomIndex >= roomMgr._maxRoomCount { 46 | return nil 47 | } 48 | 49 | return &roomMgr._roomList[roomIndex] 50 | } 51 | 52 | // 이 함수를 호출할 때의 채널 인덱스는 꼭 호출자가 유효범위인 것을 보증해야 한다. 53 | func (roomMgr *RoomManager) _getRoomUserCount(roomId int32) int { 54 | return roomMgr._roomList[roomId].getCurUserCount() 55 | } 56 | 57 | func (roomMgr *RoomManager) PacketProcess(roomNumber int32, packet protocol.Packet) { 58 | isRoomEnterReq := false 59 | 60 | if roomNumber == -1 && packet.Id == protocol.PACKET_ID_ROOM_ENTER_REQ { 61 | isRoomEnterReq = true 62 | 63 | var requestPacket protocol.RoomEnterReqPacket 64 | (&requestPacket).Decoding(packet.Data) 65 | 66 | roomNumber = requestPacket.RoomNumber 67 | } 68 | 69 | room := roomMgr.getRoomByNumber(roomNumber) 70 | if room == nil { 71 | protocol.NotifyErrorPacket(packet.UserSessionIndex, packet.UserSessionUniqueId, 72 | protocol.ERROR_CODE_ROOM_INVALIDE_NUMBER) 73 | return 74 | } 75 | 76 | user := room.getUser(packet.UserSessionUniqueId) 77 | if user == nil && isRoomEnterReq == false { 78 | protocol.NotifyErrorPacket(packet.UserSessionIndex, packet.UserSessionUniqueId, 79 | protocol.ERROR_CODE_ROOM_NOT_IN_USER) 80 | return 81 | } 82 | 83 | funcCount := len(room._funcPackeIdlist) 84 | for i := 0; i < funcCount; i++ { 85 | if room._funcPackeIdlist[i] != packet.Id { 86 | continue 87 | } 88 | 89 | room._funclist[i](user, packet) 90 | return 91 | } 92 | } 93 | 94 | func (roomMgr *RoomManager) CheckRoomState(curTimeMilliSec int64) { 95 | //TODO 한번에 모든 방을 다 조사할 필요가 없다. 밀리세컨드 단위로 타이머를 돌게 하고 그룹 단위로 방을 조사한다 96 | 97 | for i := 0; i < (int)(roomMgr._maxRoomCount); i++ { 98 | roomMgr._roomList[i].checkState(curTimeMilliSec) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /baccaratServer/roomPkg/roomUser.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import "main/protocol" 4 | 5 | type roomUser struct { 6 | netSessionIndex int32 7 | netSessionUniqueId uint64 8 | 9 | // <<< 다른 유저에게 알려줘야 하는 정보 10 | RoomUniqueId uint64 11 | IDLen int8 12 | ID [protocol.MAX_USER_ID_BYTE_LENGTH]byte 13 | // >>> 다른 유저에게 알려줘야 하는 정보 14 | packetDataSize int16 // 다른 유저에게 알려줘야 하는 정보 의 크기 15 | 16 | selectBat int8 17 | } 18 | 19 | func (user *roomUser) init(userID []byte, uniqueId uint64) { 20 | idlen := len(userID) 21 | 22 | user.IDLen = int8(idlen) 23 | copy(user.ID[:], userID) 24 | 25 | user.RoomUniqueId = uniqueId 26 | 27 | user.selectBat = BATTING_SELECT_NONE 28 | } 29 | 30 | func (user *roomUser) SetNetworkInfo(sessionIndex int32, sessionUniqueId uint64) { 31 | user.netSessionIndex = sessionIndex 32 | user.netSessionUniqueId = sessionUniqueId 33 | } 34 | 35 | func (user *roomUser) PacketDataSize() int16 { 36 | return int16(1) + int16(user.IDLen) + 8 37 | } 38 | 39 | func (user *roomUser) selectBatting(value int8) { 40 | user.selectBat = value 41 | } 42 | -------------------------------------------------------------------------------- /baccaratServer/roomPkg/room_Packet.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "main/protocol" 5 | ) 6 | 7 | func (room *baseRoom) _packetProcess_Relay(user *roomUser, packet protocol.Packet) int16 { 8 | var relayNotify protocol.RoomRelayNtfPacket 9 | relayNotify.RoomUserUniqueId = user.RoomUniqueId 10 | relayNotify.Data = packet.Data 11 | notifySendBuf, packetSize := relayNotify.EncodingPacket(packet.DataSize) 12 | room.broadcastPacket(packetSize, notifySendBuf, 0) 13 | 14 | return protocol.ERROR_CODE_NONE 15 | } -------------------------------------------------------------------------------- /baccaratServer/roomPkg/room_PacketChat.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | . "gohipernetFake" 5 | 6 | "main/protocol" 7 | ) 8 | 9 | 10 | func (room *baseRoom) _packetProcess_Chat(user *roomUser, packet protocol.Packet) int16 { 11 | sessionIndex := packet.UserSessionIndex 12 | sessionUniqueId := packet.UserSessionUniqueId 13 | 14 | var chatPacket protocol.RoomChatReqPacket 15 | if chatPacket.Decoding(packet.Data) == false { 16 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_PACKET_DECODING_FAIL) 17 | return protocol.ERROR_CODE_PACKET_DECODING_FAIL 18 | } 19 | 20 | // 채팅 최대길이 제한 21 | msgLen := len(chatPacket.Msgs) 22 | if msgLen < 1 || msgLen > protocol.MAX_CHAT_MESSAGE_BYTE_LENGTH { 23 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_ROOM_CHAT_CHAT_MSG_LEN) 24 | return protocol.ERROR_CODE_ROOM_CHAT_CHAT_MSG_LEN 25 | } 26 | 27 | 28 | var chatNotifyResponse protocol.RoomChatNtfPacket 29 | chatNotifyResponse.RoomUserUniqueId = user.RoomUniqueId 30 | chatNotifyResponse.MsgLen = int16(msgLen) 31 | chatNotifyResponse.Msg = chatPacket.Msgs 32 | notifySendBuf, packetSize := chatNotifyResponse.EncodingPacket() 33 | room.broadcastPacket(packetSize, notifySendBuf, 0) 34 | 35 | 36 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_NONE) 37 | 38 | return protocol.ERROR_CODE_NONE 39 | } 40 | 41 | func _sendRoomChatResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 42 | response := protocol.RoomChatResPacket{ result } 43 | sendPacket, _ := response.EncodingPacket() 44 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 45 | } -------------------------------------------------------------------------------- /baccaratServer/roomPkg/room_PacketEnter.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "time" 5 | 6 | . "gohipernetFake" 7 | 8 | "main/connectedSessions" 9 | "main/protocol" 10 | ) 11 | 12 | func (room *baseRoom) _packetProcess_EnterUser(inValidUser *roomUser, packet protocol.Packet) int16 { 13 | curTime := time.Now().Unix() 14 | sessionIndex := packet.UserSessionIndex 15 | sessionUniqueId := packet.UserSessionUniqueId 16 | 17 | var requestPacket protocol.RoomEnterReqPacket 18 | (&requestPacket).Decoding(packet.Data) 19 | 20 | //TODO 방의 상태가 NONE이 아니면 들어올 수 없다. 21 | 22 | userID, ok := connectedSessions.GetUserID(sessionIndex) 23 | if ok == false { 24 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, 0,0, protocol.ERROR_CODE_ENTER_ROOM_INVALID_USER_ID) 25 | return protocol.ERROR_CODE_ENTER_ROOM_INVALID_USER_ID 26 | } 27 | 28 | userInfo := addRoomUserInfo{ 29 | userID, 30 | sessionIndex, 31 | sessionUniqueId, 32 | } 33 | newUser, addResult := room.addUser(userInfo) 34 | 35 | if addResult != protocol.ERROR_CODE_NONE { 36 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, 0, 0, addResult) 37 | return addResult 38 | } 39 | 40 | if connectedSessions.SetRoomNumber(sessionIndex, sessionUniqueId, room.getNumber(), curTime) == false { 41 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, 0, 0, protocol.ERROR_CODE_ENTER_ROOM_INVALID_SESSION_STATE) 42 | return protocol.ERROR_CODE_ENTER_ROOM_INVALID_SESSION_STATE 43 | } 44 | 45 | if room.getCurUserCount() > 1 { 46 | //룸의 다른 유저에게 통보한다. 47 | room._sendNewUserInfoPacket(newUser) 48 | 49 | // 지금 들어온 유저에게 이미 채널에 있는 유저들의 정보를 보낸다 50 | room._sendUserInfoListPacket(newUser) 51 | } 52 | 53 | 54 | roomNumebr := room.getNumber() 55 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, roomNumebr, newUser.RoomUniqueId, protocol.ERROR_CODE_NONE) 56 | return protocol.ERROR_CODE_NONE 57 | } 58 | 59 | func _sendRoomEnterResult(sessionIndex int32, sessionUniqueId uint64, roomNumber int32, userUniqueId uint64, result int16) { 60 | response := protocol.RoomEnterResPacket{ 61 | result, 62 | roomNumber, 63 | userUniqueId, 64 | } 65 | sendPacket, _ := response.EncodingPacket() 66 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 67 | } 68 | 69 | 70 | func (room *baseRoom) _sendUserInfoListPacket(user *roomUser) { 71 | userCount, userInfoListSize, userInfoListBuffer := room.allocAllUserInfo(user.netSessionUniqueId) 72 | 73 | var response protocol.RoomUserListNtfPacket 74 | response.UserCount = userCount 75 | response.UserList = userInfoListBuffer 76 | sendBuf, _ := response.EncodingPacket(userInfoListSize) 77 | NetLibIPostSendToClient(user.netSessionIndex, user.netSessionUniqueId, sendBuf) 78 | } 79 | 80 | func (room *baseRoom) _sendNewUserInfoPacket(user *roomUser) { 81 | userInfoSize, userInfoListBuffer := room._allocUserInfo(user) 82 | 83 | var response protocol.RoomNewUserNtfPacket 84 | response.User = userInfoListBuffer 85 | sendBuf, packetSize := response.EncodingPacket(userInfoSize) 86 | room.broadcastPacket(int16(packetSize), sendBuf, user.netSessionUniqueId) // 자신을 제외하고 모든 유저에게 Send 87 | } 88 | 89 | 90 | -------------------------------------------------------------------------------- /baccaratServer/roomPkg/room_PacketGame.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | . "gohipernetFake" 5 | 6 | "main/protocol" 7 | ) 8 | 9 | 10 | func (room *baseRoom) _packetProcess_GameStart(user *roomUser, packet protocol.Packet) int16 { 11 | errorCode := (int16)(protocol.ERROR_CODE_NONE) 12 | sessionIndex := packet.UserSessionIndex 13 | sessionUniqueId := packet.UserSessionUniqueId 14 | 15 | // 방의 상태가 NONE 인가? 16 | if room.isStateNone() == false { 17 | errorCode = protocol.ERROR_CODE_ROOM_GAME_START_INVALID_ROOM_STATE 18 | goto CheckError 19 | } 20 | 21 | // 유저의 최소 수. 현재는 테스트를 위해 일단 1명이 최소 수 22 | if room.getCurUserCount() < 1 { 23 | errorCode = protocol.ERROR_CODE_ROOM_GAME_START_NOT_ENOUGH_MEMBERS 24 | goto CheckError 25 | } 26 | 27 | // 시작 요청은 방장이 하는가? 28 | if room.getMasterSessionUniqueId() != sessionUniqueId { 29 | errorCode = protocol.ERROR_CODE_ROOM_GAME_START_NOT_MASTER 30 | goto CheckError 31 | } 32 | 33 | // 게임을 시작한다. 34 | room.changeState(ROOM_STATE_GAME_WAIT_BATTING) 35 | 36 | _sendRoomGameStartResult(sessionIndex, sessionUniqueId, errorCode) 37 | _sendRoomGameStartNotify(room); 38 | return errorCode 39 | 40 | CheckError: 41 | _sendRoomGameStartResult(sessionIndex, sessionUniqueId, errorCode) 42 | return errorCode 43 | } 44 | 45 | func _sendRoomGameStartResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 46 | response := protocol.RoomGameStartResPacket{ result } 47 | sendPacket, _ := response.EncodingPacket() 48 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 49 | } 50 | 51 | func _sendRoomGameStartNotify(room *baseRoom) { 52 | notify := protocol.RoomGameStartNtfPacket{} 53 | notifySendBuf, packetSize := notify.EncodingPacket() 54 | room.broadcastPacket(packetSize, notifySendBuf, 0) 55 | } 56 | 57 | 58 | 59 | func (room *baseRoom) _packetProcess_GameBatting(user *roomUser, packet protocol.Packet) int16 { 60 | errorCode := (int16)(protocol.ERROR_CODE_NONE) 61 | sessionIndex := packet.UserSessionIndex 62 | sessionUniqueId := packet.UserSessionUniqueId 63 | var battingPacket protocol.RoomGameBattingReqPacket 64 | 65 | // 방의 상태가 배팅 기다림인가? 66 | if room.isStateGameBattingWait() == false { 67 | errorCode = protocol.ERROR_CODE_ROOM_GAME_BATTING_INVALID_ROOM_STATE 68 | goto CheckError 69 | } 70 | 71 | if battingPacket.Decoding(packet.Data) == false { 72 | errorCode = protocol.ERROR_CODE_ROOM_GAME_BATTING_FAIL_PACKET 73 | goto CheckError 74 | } 75 | 76 | if battingPacket.SelectSide < BATTING_SELECT_PLAYER || battingPacket.SelectSide > BATTING_SELECT_BANKER { 77 | errorCode = protocol.ERROR_CODE_ROOM_GAME_BATTING_INVALID_BAT_SELECT 78 | goto CheckError 79 | } 80 | 81 | if battingPacket.SelectSide == user.selectBat { 82 | errorCode = protocol.ERROR_CODE_ROOM_GAME_BATTING_SAME_BAT_SELECT 83 | goto CheckError 84 | } 85 | 86 | 87 | // 배팅하고, 모두에게 알린다. 88 | user.selectBatting(battingPacket.SelectSide) 89 | 90 | _sendRoomGameBattingResult(sessionIndex, sessionUniqueId, errorCode) 91 | 92 | _sendRoomGameBattingNotify(room, user.RoomUniqueId, battingPacket.SelectSide) 93 | 94 | 95 | if room.isAllUserBatting() { 96 | room.endGame() 97 | } 98 | 99 | return protocol.ERROR_CODE_NONE 100 | 101 | CheckError: 102 | _sendRoomGameBattingResult(sessionIndex, sessionUniqueId, errorCode) 103 | return errorCode 104 | } 105 | 106 | func _sendRoomGameBattingResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 107 | response := protocol.RoomGameBattingResPacket{ result } 108 | sendPacket, _ := response.EncodingPacket() 109 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 110 | } 111 | 112 | func _sendRoomGameBattingNotify(room *baseRoom, roomUserUniqueId uint64, selectSide int8) { 113 | notify := protocol.RoomGameBattingNtfPacket{ roomUserUniqueId, selectSide } 114 | notifySendBuf, packetSize := notify.EncodingPacket() 115 | room.broadcastPacket(packetSize, notifySendBuf, 0) 116 | } 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /baccaratServer/roomPkg/room_PacketLeave.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "time" 5 | 6 | . "gohipernetFake" 7 | 8 | "main/connectedSessions" 9 | "main/protocol" 10 | ) 11 | 12 | 13 | func (room *baseRoom) _packetProcess_LeaveUser(user *roomUser, packet protocol.Packet) int16 { 14 | room._leaveUserProcess(user) 15 | 16 | sessionIndex := packet.UserSessionIndex 17 | sessionUniqueId := packet.UserSessionUniqueId 18 | _sendRoomLeaveResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_NONE) 19 | return protocol.ERROR_CODE_NONE 20 | } 21 | 22 | func (room *baseRoom) _leaveUserProcess(user *roomUser) { 23 | //TODO 방의 상태가 ROOM_STATE_NOE, ROOM_STATE_GAME_RESULT 일 때만 나갈 수 있다. 24 | 25 | //TODO 유저가 접속이 끊어져서 나가는 경우라면 게임이 끝날 때까지 유저 정보 들고 있다가 26 | // ROOM_STATE_GAME_RESULT 상태일 때 제거한다. 27 | 28 | roomUserUniqueId := user.RoomUniqueId 29 | userSessionIndex := user.netSessionIndex 30 | userSessionUniqueId := user.netSessionUniqueId 31 | 32 | room._removeUser(user) 33 | 34 | room._sendRoomLeaveUserNotify(roomUserUniqueId, userSessionUniqueId) 35 | 36 | curTime := time.Now().Unix() 37 | connectedSessions.SetRoomNumber(userSessionIndex, userSessionUniqueId, -1, curTime) 38 | } 39 | 40 | 41 | func _sendRoomLeaveResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 42 | response := protocol.RoomLeaveResPacket{ result } 43 | sendPacket, _ := response.EncodingPacket() 44 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 45 | } 46 | 47 | func (room *baseRoom) _sendRoomLeaveUserNotify(roomUserUniqueId uint64, userSessionUniqueId uint64) { 48 | notifyPacket := protocol.RoomLeaveUserNtfPacket{roomUserUniqueId } 49 | sendBuf, packetSize := notifyPacket.EncodingPacket() 50 | room.broadcastPacket(int16(packetSize), sendBuf, userSessionUniqueId) 51 | } -------------------------------------------------------------------------------- /bin/config_logger.json: -------------------------------------------------------------------------------- 1 | { 2 | "level": "debug", 3 | "encoding": "json", 4 | "maxSize": 100, 5 | "maxBackups" : 3, 6 | "maxAge" : 28, 7 | "encoderConfig": { 8 | "messageKey": "Msg", 9 | "levelKey": "Level", 10 | "timeKey": "Time", 11 | "nameKey": "Name", 12 | "callerKey": "Caller", 13 | "stacktraceKey": "St", 14 | "levelEncoder": "capital", 15 | "timeEncoder": "iso8601", 16 | "durationEncoder": "string", 17 | "callerEncoder": "short" 18 | }, 19 | "outputPaths": [ 20 | "stdout" 21 | ], 22 | "errorOutputPaths": [ 23 | "stderr" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /bin/run_baccaratServer.bat: -------------------------------------------------------------------------------- 1 | baccaratServer.exe -c_IsTcp4Addr=true -c_BindAddress=127.0.0.1:11021 -c_MaxSessionCount=200 -c_MaxPacketSize=1024 -------------------------------------------------------------------------------- /bin/run_chatServer.bat: -------------------------------------------------------------------------------- 1 | chatServer.exe -c_IsTcp4Addr=true -c_BindAddress=127.0.0.1:11021 -c_MaxSessionCount=200 -c_MaxPacketSize=1024 -------------------------------------------------------------------------------- /bin/run_chatServer2.bat: -------------------------------------------------------------------------------- 1 | chatServer2.exe -c_IsTcp4Addr=true -c_BindAddress=127.0.0.1:11021 -c_MaxSessionCount=20000 -c_MaxPacketSize=1024 -c_MaxReceiveBufferSize=4012 -GameName=chatServer2 -RoomMaxCount=2 -RoomStartNum=0 -RoomMaxUserCount=2 -RoomMaxProcessBufferCount=2 -RoomCountByGoroutine=2 -RoomInternalPacketChanBufferCount=2 -CheckCountAtOnce=256 -CheckReriodMillSec=32 -LoginWaitTimeSec=6000 -DisConnectWaitTimeSec=6000 -RoomEnterWaitTimeSec=6000 -PingWaitTimeSec=6000 -MaxRequestCountPerSecond=6000 -------------------------------------------------------------------------------- /bin/run_echoServer.bat: -------------------------------------------------------------------------------- 1 | echoServer.exe -c_IsTcp4Addr=true -c_BindAddress=127.0.0.1:11021 -c_MaxSessionCount=20000 -c_MaxPacketSize=1024 -------------------------------------------------------------------------------- /chatServer/chatServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | . "gohipernetFake" 8 | 9 | "main/connectedSessions" 10 | "main/protocol" 11 | "main/roomPkg" 12 | ) 13 | 14 | type configAppServer struct { 15 | GameName string 16 | 17 | RoomMaxCount int32 18 | RoomStartNum int32 19 | RoomMaxUserCount int32 20 | } 21 | 22 | type ChatServer struct { 23 | ServerIndex int 24 | IP string 25 | Port int 26 | 27 | PacketChan chan protocol.Packet 28 | 29 | RoomMgr *roomPkg.RoomManager 30 | } 31 | 32 | func createAnsStartServer(netConfig NetworkConfig, appConfig configAppServer) { 33 | OutPutLog(LOG_LEVEL_INFO, "", 0, "CreateServer !!!") 34 | 35 | var server ChatServer 36 | 37 | if server.setIPAddress(netConfig.BindAddress) == false { 38 | OutPutLog(LOG_LEVEL_INFO, "", 0, "fail. server address") 39 | return 40 | } 41 | 42 | protocol.Init_packet(); 43 | 44 | maxUserCount := appConfig.RoomMaxCount * appConfig.RoomMaxUserCount 45 | connectedSessions.Init(netConfig.MaxSessionCount, maxUserCount) 46 | 47 | server.PacketChan = make(chan protocol.Packet, 256) 48 | 49 | roomConfig := roomPkg.RoomConfig{ 50 | appConfig.RoomStartNum, 51 | appConfig.RoomMaxCount, 52 | appConfig.RoomMaxUserCount, 53 | } 54 | server.RoomMgr = roomPkg.NewRoomManager(roomConfig) 55 | 56 | 57 | go server.PacketProcess_goroutine() 58 | 59 | 60 | networkFunctor := SessionNetworkFunctors{} 61 | networkFunctor.OnConnect = server.OnConnect 62 | networkFunctor.OnReceive = server.OnReceive 63 | networkFunctor.OnReceiveBufferedData = nil 64 | networkFunctor.OnClose = server.OnClose 65 | networkFunctor.PacketTotalSizeFunc = PacketTotalSize 66 | networkFunctor.PacketHeaderSize = PACKET_HEADER_SIZE 67 | networkFunctor.IsClientSession = true 68 | 69 | 70 | NetLibStartNetwork(&netConfig, networkFunctor) 71 | } 72 | 73 | func (server *ChatServer) setIPAddress(ipAddress string) bool { 74 | results := strings.Split(ipAddress, ":") 75 | if len(results) != 2 { 76 | return false 77 | } 78 | 79 | server.IP = results[0] 80 | server.Port, _ = strconv.Atoi(results[1]) 81 | 82 | return true 83 | } 84 | 85 | 86 | 87 | 88 | func (server *ChatServer) OnConnect(sessionIndex int32, sessionUniqueID uint64) { 89 | connectedSessions.AddSession(sessionIndex, sessionUniqueID) 90 | } 91 | 92 | func (server *ChatServer) OnReceive(sessionIndex int32, sessionUniqueID uint64, data []byte) bool { 93 | server.DistributePacket(sessionIndex, sessionUniqueID, data) 94 | return true 95 | } 96 | 97 | func (server *ChatServer) OnClose(sessionIndex int32, sessionUniqueID uint64) { 98 | server.disConnectClient(sessionIndex, sessionUniqueID) 99 | } 100 | 101 | func (server *ChatServer) disConnectClient(sessionIndex int32, sessionUniqueId uint64) { 102 | // 로그인도 안한 유저라면 그냥 여기서 처리한다. 103 | // 방 입장을 안한 유저라면 여기서 처리해도 괜찮지만 아래로 넘긴다. 104 | if connectedSessions.IsLoginUser(sessionIndex) == false { 105 | connectedSessions.RemoveSession(sessionIndex, false) 106 | return 107 | } 108 | 109 | 110 | packet := protocol.Packet { 111 | sessionIndex, 112 | sessionUniqueId, 113 | protocol.PACKET_ID_SESSION_CLOSE_SYS, 114 | 0, 115 | nil, 116 | } 117 | 118 | server.PacketChan <- packet 119 | } 120 | -------------------------------------------------------------------------------- /chatServer/connectedSessions/session.go: -------------------------------------------------------------------------------- 1 | package connectedSessions 2 | 3 | import ( 4 | "sync/atomic" 5 | 6 | "main/protocol" 7 | ) 8 | 9 | type session struct { 10 | _index int32 11 | 12 | _networkUniqueID uint64 //네트워크 세션의 유니크 ID 13 | 14 | _userID [protocol.MAX_USER_ID_BYTE_LENGTH]byte 15 | _userIDLength int8 16 | 17 | _connectTimeSec int64 // 연결된 시간 18 | _RoomNum int32 // 19 | _RoomNumOfEntering int32 // 현재 입장 중인 룸의 번호 20 | } 21 | 22 | func (session *session) Init(index int32) { 23 | session._index = index 24 | session.Clear() 25 | } 26 | 27 | func (session *session) _ClearUserId() { 28 | session._userIDLength = 0 29 | } 30 | 31 | func (session *session) Clear() { 32 | session._ClearUserId() 33 | session.setRoomNumber(0, -1, 0) 34 | session.SetConnectTimeSec(0, 0) 35 | } 36 | 37 | func (session *session) GetIndex() int32 { 38 | return session._index 39 | } 40 | 41 | func (session *session) GetNetworkUniqueID() uint64 { 42 | return atomic.LoadUint64(&session._networkUniqueID) 43 | } 44 | 45 | func (session *session) validNetworkUniqueID(uniqueId uint64) bool { 46 | return atomic.LoadUint64(&session._networkUniqueID) == uniqueId 47 | } 48 | 49 | func (session *session) GetNetworkInfo() (int32, uint64) { 50 | index := session.GetIndex() 51 | uniqueID := atomic.LoadUint64(&session._networkUniqueID) 52 | return index, uniqueID 53 | } 54 | 55 | func (session *session) setUserID(userID []byte) { 56 | session._userIDLength = int8(len(userID)) 57 | copy(session._userID[:], userID) 58 | } 59 | 60 | func (session *session) getUserID() []byte { 61 | return session._userID[0:session._userIDLength] 62 | } 63 | 64 | func (session *session) getUserIDLength() int8 { 65 | return session._userIDLength 66 | } 67 | 68 | func (session *session) SetConnectTimeSec(timeSec int64, uniqueID uint64) { 69 | atomic.StoreInt64(&session._connectTimeSec, timeSec) 70 | atomic.StoreUint64(&session._networkUniqueID, uniqueID) 71 | } 72 | 73 | func (session *session) GetConnectTimeSec() int64 { 74 | return atomic.LoadInt64(&session._connectTimeSec) 75 | } 76 | 77 | func (session *session) SetUser(sessionUniqueId uint64, 78 | userID []byte, 79 | curTimeSec int64, 80 | ) { 81 | session.setUserID(userID) 82 | session.setRoomNumber(sessionUniqueId, -1, curTimeSec) // 방어적인 목적으로 채널 번호 초기화 83 | } 84 | 85 | func (session *session) IsAuth() bool { 86 | if session._userIDLength > 0 { 87 | return true 88 | } 89 | 90 | return false 91 | } 92 | 93 | func (session *session) setRoomEntering(roomNum int32) bool { 94 | if atomic.CompareAndSwapInt32(&session._RoomNumOfEntering, -1, roomNum) == false { 95 | return false 96 | } 97 | 98 | return true 99 | } 100 | 101 | func (session *session) setRoomNumber(sessionUniqueId uint64, roomNum int32, curTimeSec int64) bool { 102 | if roomNum == -1 { 103 | atomic.StoreInt32(&session._RoomNum, roomNum) 104 | atomic.StoreInt32(&session._RoomNumOfEntering, roomNum) 105 | return true 106 | } 107 | 108 | if sessionUniqueId != 0 && session.validNetworkUniqueID(sessionUniqueId) == false { 109 | return false 110 | 111 | } 112 | // 입력이 -1이 아닌경우 -1이 아닐 때만 compareswap으로 변경한다. 실패하면 채널 입장도 실패이다. 113 | if atomic.CompareAndSwapInt32(&session._RoomNum, -1, roomNum) == false { 114 | return false 115 | } 116 | 117 | atomic.StoreInt32(&session._RoomNumOfEntering, roomNum) 118 | return true 119 | } 120 | 121 | func (session *session) getRoomNumber() (int32, int32) { 122 | roomNum := atomic.LoadInt32(&session._RoomNum) 123 | roomNumOfEntering := atomic.LoadInt32(&session._RoomNum) 124 | return roomNum, roomNumOfEntering 125 | } 126 | -------------------------------------------------------------------------------- /chatServer/connectedSessions/sessionManager.go: -------------------------------------------------------------------------------- 1 | package connectedSessions 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | // 스레드 세이프 해야 한다. 10 | type Manager struct { 11 | _UserIDsessionMap *sync.Map 12 | 13 | _maxSessionCount int32 14 | _sessionList []*session 15 | 16 | _maxUserCount int32 17 | 18 | _currentLoginUserCount int32 19 | } 20 | 21 | var _manager Manager 22 | 23 | func Init(maxSessionCount int, maxUserCount int32) bool { 24 | _manager._UserIDsessionMap = new(sync.Map) 25 | _manager._maxUserCount = maxUserCount 26 | 27 | _manager._maxSessionCount = int32(maxSessionCount) 28 | _manager._sessionList = make([]*session, maxSessionCount) 29 | 30 | for i := 0; i < maxSessionCount; i++ { 31 | _manager._sessionList[i] = new(session) 32 | 33 | index := int32(i) 34 | _manager._sessionList[i].Init(index) 35 | } 36 | 37 | return true 38 | } 39 | 40 | func AddSession(sessionIndex int32, sessionUniqueID uint64) bool { 41 | if _validSessionIndex(sessionIndex) == false { 42 | return false 43 | } 44 | 45 | if _manager._sessionList[sessionIndex].GetConnectTimeSec() > 0 { 46 | return false 47 | } 48 | 49 | // 방어적인 목적으로 한번 더 Clear 한다 50 | _manager._sessionList[sessionIndex].Clear() 51 | 52 | _manager._sessionList[sessionIndex].SetConnectTimeSec(time.Now().Unix(), sessionUniqueID) 53 | return true 54 | } 55 | 56 | func RemoveSession(sessionIndex int32, isLoginedUser bool) bool { 57 | if _validSessionIndex(sessionIndex) == false { 58 | return false 59 | } 60 | 61 | if isLoginedUser { 62 | atomic.AddInt32(&_manager._currentLoginUserCount, -1) 63 | 64 | userID := string(_manager._sessionList[sessionIndex].getUserID()) 65 | _manager._UserIDsessionMap.Delete(userID) 66 | } 67 | 68 | _manager._sessionList[sessionIndex].Clear() 69 | 70 | return true 71 | } 72 | 73 | func _validSessionIndex(index int32) bool { 74 | if index < 0 || index >= _manager._maxSessionCount { 75 | return false 76 | } 77 | return true 78 | } 79 | 80 | func GetNetworkUniqueID(sessionIndex int32) uint64 { 81 | if _validSessionIndex(sessionIndex) == false { 82 | return 0 83 | } 84 | 85 | return _manager._sessionList[sessionIndex].GetNetworkUniqueID() 86 | } 87 | 88 | func GetUserID(sessionIndex int32) ([]byte, bool) { 89 | if _validSessionIndex(sessionIndex) == false { 90 | return nil, false 91 | } 92 | 93 | return _manager._sessionList[sessionIndex].getUserID(), true 94 | } 95 | 96 | func SetLogin(sessionIndex int32, sessionUniqueId uint64, userID []byte, curTimeSec int64) bool { 97 | if _validSessionIndex(sessionIndex) == false { 98 | return false 99 | } 100 | 101 | newUserID := string(userID) 102 | if _, ok := _manager._UserIDsessionMap.Load(newUserID); ok { 103 | return false 104 | } 105 | 106 | _manager._sessionList[sessionIndex].SetUser(sessionUniqueId, userID, curTimeSec) 107 | _manager._UserIDsessionMap.Store(newUserID, _manager._sessionList[sessionIndex]) 108 | 109 | atomic.AddInt32(&_manager._currentLoginUserCount, 1) 110 | return true 111 | } 112 | 113 | func IsLoginUser(sessionIndex int32) bool { 114 | if _validSessionIndex(sessionIndex) == false { 115 | return false 116 | } 117 | 118 | return _manager._sessionList[sessionIndex].IsAuth() 119 | } 120 | 121 | func SetRoomNumber(sessionIndex int32, sessionUniqueId uint64, roomNum int32, curTimeSec int64) bool { 122 | if _validSessionIndex(sessionIndex) == false { 123 | return false 124 | } 125 | 126 | return _manager._sessionList[sessionIndex].setRoomNumber(sessionUniqueId, roomNum, curTimeSec) 127 | } 128 | 129 | func GetRoomNumber(sessionIndex int32) (int32, int32) { 130 | if _validSessionIndex(sessionIndex) == false { 131 | return -1, -1 132 | } 133 | return _manager._sessionList[sessionIndex].getRoomNumber() 134 | } 135 | -------------------------------------------------------------------------------- /chatServer/distributePacket.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "main/connectedSessions" 6 | "time" 7 | 8 | . "gohipernetFake" 9 | 10 | "main/protocol" 11 | ) 12 | 13 | func (server *ChatServer) DistributePacket(sessionIndex int32, 14 | sessionUniqueId uint64, 15 | packetData []byte, 16 | ) { 17 | packetID := protocol.PeekPacketID(packetData) 18 | bodySize, bodyData := protocol.PeekPacketBody(packetData) 19 | 20 | 21 | packet := protocol.Packet{Id: packetID} 22 | packet.UserSessionIndex = sessionIndex 23 | packet.UserSessionUniqueId = sessionUniqueId 24 | packet.Id = packetID 25 | packet.DataSize = bodySize 26 | packet.Data = make([]byte, packet.DataSize) 27 | copy(packet.Data, bodyData) 28 | 29 | server.PacketChan <- packet 30 | } 31 | 32 | 33 | func (server *ChatServer) PacketProcess_goroutine() { 34 | for { 35 | if server.PacketProcess_goroutine_Impl() { 36 | OutPutLog(LOG_LEVEL_INFO, "", 0, "Wanted Stop PacketProcess goroutine") 37 | break 38 | } 39 | } 40 | 41 | OutPutLog(LOG_LEVEL_INFO, "", 0,"Stop rooms PacketProcess goroutine") 42 | } 43 | 44 | func (server *ChatServer) PacketProcess_goroutine_Impl() bool { 45 | IsWantedTermination := false // 여기에서는 의미 없음. 서버 종료를 명시적으로 하는 경우만 유용 46 | defer PrintPanicStack() 47 | 48 | 49 | for { 50 | packet := <-server.PacketChan 51 | sessionIndex := packet.UserSessionIndex 52 | sessionUniqueId := packet.UserSessionUniqueId 53 | bodySize := packet.DataSize 54 | bodyData := packet.Data 55 | 56 | if packet.Id == protocol.PACKET_ID_LOGIN_REQ { 57 | ProcessPacketLogin(sessionIndex, sessionUniqueId, bodySize, bodyData) 58 | } else if packet.Id == protocol.PACKET_ID_SESSION_CLOSE_SYS { 59 | ProcessPacketSessionClosed(server, sessionIndex, sessionUniqueId) 60 | } else { 61 | roomNumber, _ := connectedSessions.GetRoomNumber(sessionIndex) 62 | server.RoomMgr.PacketProcess(roomNumber, packet) 63 | } 64 | } 65 | 66 | return IsWantedTermination 67 | } 68 | 69 | func ProcessPacketLogin(sessionIndex int32, 70 | sessionUniqueId uint64, 71 | bodySize int16, 72 | bodyData []byte ) { 73 | //DB와 연동하지 않으므로 중복 로그인만 아니면 다 성공으로 한다 74 | var request protocol.LoginReqPacket 75 | if (&request).Decoding(bodyData) == false { 76 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_PACKET_DECODING_FAIL) 77 | return 78 | } 79 | 80 | userID := bytes.Trim(request.UserID[:], "\x00"); 81 | 82 | if len(userID) <= 0 { 83 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_LOGIN_USER_INVALID_ID) 84 | return 85 | } 86 | 87 | curTime := time.Now().Unix() 88 | 89 | if connectedSessions.SetLogin(sessionIndex, sessionUniqueId, userID, curTime) == false { 90 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_LOGIN_USER_DUPLICATION) 91 | return 92 | } 93 | 94 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_NONE) 95 | } 96 | 97 | func _sendLoginResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 98 | var response protocol.LoginResPacket 99 | response.Result = result 100 | sendPacket, _ := response.EncodingPacket() 101 | 102 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 103 | } 104 | 105 | 106 | func ProcessPacketSessionClosed(server *ChatServer, sessionIndex int32, sessionUniqueId uint64) { 107 | roomNumber, _ := connectedSessions.GetRoomNumber(sessionIndex) 108 | 109 | if roomNumber > -1 { 110 | packet := protocol.Packet{ 111 | sessionIndex, 112 | sessionUniqueId, 113 | protocol.PACKET_ID_ROOM_LEAVE_REQ, 114 | 0, 115 | nil, 116 | } 117 | 118 | server.RoomMgr.PacketProcess(roomNumber, packet) 119 | } 120 | 121 | connectedSessions.RemoveSession(sessionIndex, true) 122 | } 123 | 124 | 125 | -------------------------------------------------------------------------------- /chatServer/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | go.uber.org/atomic v1.4.0 // indirect 8 | go.uber.org/multierr v1.1.0 // indirect 9 | go.uber.org/zap v1.10.0 10 | gohipernetFake v0.0.0 11 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect 12 | ) 13 | 14 | replace gohipernetFake v0.0.0 => ../gohipernetFake 15 | -------------------------------------------------------------------------------- /chatServer/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= 4 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 5 | go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= 6 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 7 | go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= 8 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 9 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 10 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 11 | -------------------------------------------------------------------------------- /chatServer/logger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "time" 11 | 12 | "go.uber.org/zap" 13 | "go.uber.org/zap/zapcore" 14 | "gopkg.in/natefinch/lumberjack.v2" 15 | ) 16 | 17 | var ( 18 | Logger, _ = zap.NewProduction() 19 | ) 20 | 21 | type extendedZapConfig struct{ 22 | MaxSize int `json:"maxSize"` 23 | MaxBackups int `json:"maxBackups"` 24 | MaxAge int `json:"maxAge"` 25 | zap.Config 26 | } 27 | 28 | func init_Log() { 29 | currentDir, err := filepath.Abs(filepath.Dir(os.Args[0])) 30 | if err != nil { 31 | panic(err) 32 | } 33 | 34 | configJson, err := ioutil.ReadFile(filepath.FromSlash(currentDir + "/" + "config_logger.json")) 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | var myConfig extendedZapConfig 40 | 41 | if err := json.Unmarshal(configJson, &myConfig); err != nil { 42 | panic(err) 43 | } 44 | 45 | for index := range myConfig.ErrorOutputPaths{ 46 | if myConfig.ErrorOutputPaths[index] != "stderr"{ 47 | myConfig.ErrorOutputPaths[index], _ = _createFileName(myConfig.ErrorOutputPaths[index]) 48 | } 49 | } 50 | 51 | enc := zapcore.NewJSONEncoder(myConfig.EncoderConfig) 52 | Logger = zap.New(zapcore.NewCore(enc, _combineSinkFromConfig(myConfig), myConfig.Level)) 53 | } 54 | 55 | func _combineSinkFromConfig(myConfig extendedZapConfig) zapcore.WriteSyncer{ 56 | var fileName string 57 | stdOutLogOn := false 58 | for index:= range myConfig.OutputPaths{ 59 | if myConfig.OutputPaths[index] != "stdout"{ // 그 외는 텍스트 파일 60 | fileName = myConfig.OutputPaths[index] 61 | } else { 62 | stdOutLogOn = true // 설정파일에 stdout이 있을 경우 63 | } 64 | } 65 | 66 | sink := zapcore.AddSync( 67 | &lumberjack.Logger{ 68 | Filename: fileName, 69 | MaxSize: myConfig.MaxSize, // MB 단위 70 | MaxBackups: myConfig.MaxBackups, 71 | MaxAge: myConfig.MaxAge, // 28일 단위 72 | }, 73 | ) 74 | var combineSink zapcore.WriteSyncer 75 | if stdOutLogOn { 76 | combineSink = zap.CombineWriteSyncers(sink, os.Stdout) 77 | } else { 78 | combineSink = sink 79 | } 80 | return combineSink 81 | } 82 | 83 | func _createFileName(outputName string) (string, error){ 84 | currentTime := time.Now() 85 | formattedTime := currentTime.Format("20060102_150405") 86 | fileNameArr := strings.Split(outputName, ".") 87 | fileName := fileNameArr[0] 88 | fileExt := "." + fileNameArr[1] 89 | if len(fileNameArr) > 2{ 90 | return "", errors.New("log ouput name Invalid") 91 | } 92 | return fileName + formattedTime + fileExt, nil 93 | } 94 | 95 | 96 | var IExportLog func(string, string) = _emptyExportLog 97 | 98 | func _emptyExportLog(level string, message string) {} 99 | 100 | -------------------------------------------------------------------------------- /chatServer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | . "gohipernetFake" 6 | ) 7 | 8 | 9 | func main() { 10 | NetLibInitLog(LOG_LEVEL_DEBUG, nil) 11 | 12 | netConfig, appConfig := parseAppConfig() 13 | netConfig.WriteNetworkConfig(true) 14 | 15 | // 아래 함수를 호출하면 강제적으로 종료 시킬 때까지 대기 상태가 된다. 16 | createAnsStartServer(netConfig, appConfig) 17 | } 18 | 19 | func parseAppConfig() (NetworkConfig, configAppServer) { 20 | OutPutLog(LOG_LEVEL_INFO,"", 0,"[[Setting NetworkConfig]]") 21 | 22 | //TODO flag 사용하기 23 | appConfig := configAppServer { 24 | "chatServer", 25 | 1000, 26 | 0, 27 | 4, 28 | } 29 | 30 | 31 | netConfig := NetworkConfig{} 32 | 33 | flag.BoolVar(&netConfig.IsTcp4Addr,"c_IsTcp4Addr", true, "bool flag") 34 | flag.StringVar(&netConfig.BindAddress,"c_BindAddress", "127.0.0.1:11021", "string flag") 35 | flag.IntVar(&netConfig.MaxSessionCount,"c_MaxSessionCount", 0, "int flag") 36 | flag.IntVar(&netConfig.MaxPacketSize,"c_MaxPacketSize", 0, "int flag") 37 | 38 | flag.Parse() 39 | return netConfig, appConfig 40 | } 41 | 42 | -------------------------------------------------------------------------------- /chatServer/protocol/errorcode.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | const ( 4 | ERROR_CODE_NONE = 1 5 | 6 | ERROR_CODE_PACKET_DECODING_FAIL = 51 7 | ERROR_CODE_PACKET_NOT_LOGIN_USER = 52 8 | ERROR_CODE_ROOM_NOT_REGISTED_PACKET_ID = 53 9 | ERROR_CODE_USER_NOT_IN_ROOM = 54 10 | 11 | ERROR_CODE_DISCONNECT_UNAUTHENTICATED_USER = 61 12 | 13 | ERROR_CODE_LOGIN_USER_DUPLICATION = 111 14 | ERROR_CODE_LOGIN_USER_INVALID_ID = 112 15 | 16 | ERROR_CODE_ROOM_NOT_IN_USER = 121 17 | ERROR_CODE_ROOM_INVALIDE_NUMBER = 122 18 | 19 | ERROR_CODE_ENTER_ROOM_ALREADY = 131 20 | ERROR_CODE_ENTER_ROOM_PREV_WORKING = 132 21 | ERROR_CODE_ENTER_ROOM_INVALID_USER_ID = 133 22 | ERROR_CODE_ENTER_ROOM_USER_FULL = 134 23 | ERROR_CODE_ENTER_ROOM_DUPLCATION_USER = 135 24 | ERROR_CODE_ENTER_ROOM_INVALID_SESSION_STATE = 136 25 | ERROR_CODE_ENTER_ROOM_AUTO_ROOM_NUMBER = 137 26 | 27 | ERROR_CODE_LEAVE_ROOM_INTERNAL_INVALID_USER = 141 28 | 29 | ERROR_CODE_ROOM_CHAT_CHAT_MSG_LEN = 151 30 | 31 | ERROR_CODE_ROOM_RELAY_FAIL_DECPDING = 161 32 | 33 | ERROR_CODE_ROOM_NOT_REGISTED_INTERNAL_PACKET_ID = 252 // 번역 필요없음 34 | 35 | 36 | ) 37 | -------------------------------------------------------------------------------- /chatServer/protocol/packetID.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | 4 | const ( 5 | PACKET_ID_PING_REQ = 201 6 | PACKET_ID_PING_RES = 202 7 | 8 | PACKET_ID_ERROR_NTF = 203 9 | 10 | PACKET_ID_SESSION_CLOSE_SYS = 211 11 | 12 | 13 | PACKET_ID_LOGIN_REQ = 701 14 | PACKET_ID_LOGIN_RES = 702 15 | 16 | PACKET_ID_ROOM_ENTER_REQ = 721 17 | PACKET_ID_ROOM_ENTER_RES = 722 18 | PACKET_ID_ROOM_USER_LIST_NTF = 723 19 | PACKET_ID_ROOM_NEW_USER_NTF = 724 20 | 21 | PACKET_ID_ROOM_LEAVE_REQ = 726 22 | PACKET_ID_ROOM_LEAVE_RES = 727 23 | PACKET_ID_ROOM_LEAVE_USER_NTF = 728 24 | 25 | PACKET_ID_ROOM_CHAT_REQ = 731 26 | PACKET_ID_ROOM_CHAT_RES = 732 27 | PACKET_ID_ROOM_CHAT_NOTIFY = 733 28 | 29 | PACKET_ID_ROOM_RELAY_REQ = 741 30 | PACKET_ID_ROOM_RELAY_NTF = 742 31 | 32 | 33 | INTERNAL_PACKET_ID_DISCONNECTED_USER_TO_ROOM = 1602 34 | ) 35 | 36 | 37 | -------------------------------------------------------------------------------- /chatServer/roomPkg/define.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import "main/protocol" 4 | 5 | type RoomConfig struct { 6 | StartRoomNumber int32 7 | MaxRoomCount int32 8 | MaxUserCount int32 9 | } 10 | 11 | type roomUser struct { 12 | netSessionIndex int32 13 | netSessionUniqueId uint64 14 | 15 | // <<< 다른 유저에게 알려줘야 하는 정보 16 | RoomUniqueId uint64 17 | IDLen int8 18 | ID [protocol.MAX_USER_ID_BYTE_LENGTH]byte 19 | // >>> 다른 유저에게 알려줘야 하는 정보 20 | packetDataSize int16 // 다른 유저에게 알려줘야 하는 정보 의 크기 21 | } 22 | 23 | func (user *roomUser) init(userID []byte, uniqueId uint64) { 24 | idlen := len(userID) 25 | 26 | user.IDLen = int8(idlen) 27 | copy(user.ID[:], userID) 28 | 29 | user.RoomUniqueId = uniqueId 30 | } 31 | 32 | func (user *roomUser) SetNetworkInfo(sessionIndex int32, sessionUniqueId uint64) { 33 | user.netSessionIndex = sessionIndex 34 | user.netSessionUniqueId = sessionUniqueId 35 | } 36 | 37 | func (user *roomUser) PacketDataSize() int16 { 38 | return int16(1) + int16(user.IDLen) + 8 39 | } 40 | 41 | type addRoomUserInfo struct { 42 | userID []byte 43 | 44 | netSessionIndex int32 45 | netSessionUniqueId uint64 46 | } 47 | -------------------------------------------------------------------------------- /chatServer/roomPkg/roomManager.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "main/protocol" 5 | ) 6 | 7 | type RoomManager struct { 8 | _roomStartNum int32 9 | _maxRoomCount int32 10 | _roomCountList []int16 11 | _roomList []baseRoom 12 | } 13 | 14 | func NewRoomManager(config RoomConfig) *RoomManager { 15 | roomManager := new(RoomManager) 16 | roomManager._initialize(config) 17 | return roomManager 18 | } 19 | 20 | func (roomMgr *RoomManager) _initialize(config RoomConfig) { 21 | roomMgr._roomStartNum = config.StartRoomNumber 22 | roomMgr._maxRoomCount = config.MaxRoomCount 23 | roomMgr._roomCountList = make([]int16, config.MaxRoomCount) 24 | roomMgr._roomList = make([]baseRoom, config.MaxRoomCount) 25 | 26 | for i := int32(0); i < roomMgr._maxRoomCount; i++ { 27 | roomMgr._roomList[i].initialize(i, config) 28 | roomMgr._roomList[i].settingPacketFunction() 29 | } 30 | } 31 | 32 | func (roomMgr *RoomManager) GetAllChannelUserCount() []int16 { 33 | maxRoomCount := roomMgr._maxRoomCount 34 | for i := int32(0); i < maxRoomCount; i++ { 35 | roomMgr._roomCountList[i] = (int16)(roomMgr._getRoomUserCount(i)) 36 | } 37 | 38 | return roomMgr._roomCountList 39 | } 40 | 41 | func (roomMgr *RoomManager) getRoomByNumber(roomNumber int32) *baseRoom { 42 | roomIndex := roomNumber - roomMgr._roomStartNum 43 | 44 | if roomNumber < 0 || roomIndex >= roomMgr._maxRoomCount { 45 | return nil 46 | } 47 | 48 | return &roomMgr._roomList[roomIndex] 49 | } 50 | 51 | // 이 함수를 호출할 때의 채널 인덱스는 꼭 호출자가 유효범위인 것을 보증해야 한다. 52 | func (roomMgr *RoomManager) _getRoomUserCount(roomId int32) int32 { 53 | return roomMgr._roomList[roomId].getCurUserCount() 54 | } 55 | 56 | func (roomMgr *RoomManager) PacketProcess(roomNumber int32, packet protocol.Packet) { 57 | isRoomEnterReq := false 58 | 59 | if roomNumber == -1 && packet.Id == protocol.PACKET_ID_ROOM_ENTER_REQ { 60 | isRoomEnterReq = true 61 | 62 | var requestPacket protocol.RoomEnterReqPacket 63 | (&requestPacket).Decoding(packet.Data) 64 | 65 | roomNumber = requestPacket.RoomNumber 66 | } 67 | 68 | room := roomMgr.getRoomByNumber(roomNumber) 69 | if room == nil { 70 | protocol.NotifyErrorPacket(packet.UserSessionIndex, packet.UserSessionUniqueId, 71 | protocol.ERROR_CODE_ROOM_INVALIDE_NUMBER) 72 | return 73 | } 74 | 75 | user := room.getUser(packet.UserSessionUniqueId) 76 | if user == nil && isRoomEnterReq == false { 77 | protocol.NotifyErrorPacket(packet.UserSessionIndex, packet.UserSessionUniqueId, 78 | protocol.ERROR_CODE_ROOM_NOT_IN_USER) 79 | return 80 | } 81 | 82 | funcCount := len(room._funcPackeIdlist) 83 | for i := 0; i < funcCount; i++ { 84 | if room._funcPackeIdlist[i] != packet.Id { 85 | continue 86 | } 87 | 88 | room._funclist[i](user, packet) 89 | return 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /chatServer/roomPkg/room_Packet.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "main/protocol" 5 | ) 6 | 7 | func (room *baseRoom) _packetProcess_Relay(user *roomUser, packet protocol.Packet) int16 { 8 | var relayNotify protocol.RoomRelayNtfPacket 9 | relayNotify.RoomUserUniqueId = user.RoomUniqueId 10 | relayNotify.Data = packet.Data 11 | notifySendBuf, packetSize := relayNotify.EncodingPacket(packet.DataSize) 12 | room.broadcastPacket(packetSize, notifySendBuf, 0) 13 | 14 | return protocol.ERROR_CODE_NONE 15 | } 16 | -------------------------------------------------------------------------------- /chatServer/roomPkg/room_PacketChat.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | . "gohipernetFake" 5 | 6 | "main/protocol" 7 | ) 8 | 9 | func (room *baseRoom) _packetProcess_Chat(user *roomUser, packet protocol.Packet) int16 { 10 | sessionIndex := packet.UserSessionIndex 11 | sessionUniqueId := packet.UserSessionUniqueId 12 | 13 | var chatPacket protocol.RoomChatReqPacket 14 | if chatPacket.Decoding(packet.Data) == false { 15 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_PACKET_DECODING_FAIL) 16 | return protocol.ERROR_CODE_PACKET_DECODING_FAIL 17 | } 18 | 19 | // 채팅 최대길이 제한 20 | msgLen := len(chatPacket.Msgs) 21 | if msgLen < 1 || msgLen > protocol.MAX_CHAT_MESSAGE_BYTE_LENGTH { 22 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_ROOM_CHAT_CHAT_MSG_LEN) 23 | return protocol.ERROR_CODE_ROOM_CHAT_CHAT_MSG_LEN 24 | } 25 | 26 | var chatNotifyResponse protocol.RoomChatNtfPacket 27 | chatNotifyResponse.RoomUserUniqueId = user.RoomUniqueId 28 | chatNotifyResponse.MsgLen = int16(msgLen) 29 | chatNotifyResponse.Msg = chatPacket.Msgs 30 | notifySendBuf, packetSize := chatNotifyResponse.EncodingPacket() 31 | room.broadcastPacket(packetSize, notifySendBuf, 0) 32 | 33 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_NONE) 34 | 35 | return protocol.ERROR_CODE_NONE 36 | } 37 | 38 | func _sendRoomChatResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 39 | response := protocol.RoomChatResPacket{result} 40 | sendPacket, _ := response.EncodingPacket() 41 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 42 | } 43 | -------------------------------------------------------------------------------- /chatServer/roomPkg/room_PacketEnter.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "time" 5 | 6 | . "gohipernetFake" 7 | 8 | "main/connectedSessions" 9 | "main/protocol" 10 | ) 11 | 12 | func (room *baseRoom) _packetProcess_EnterUser(inValidUser *roomUser, packet protocol.Packet) int16 { 13 | curTime := time.Now().Unix() 14 | sessionIndex := packet.UserSessionIndex 15 | sessionUniqueId := packet.UserSessionUniqueId 16 | 17 | var requestPacket protocol.RoomEnterReqPacket 18 | (&requestPacket).Decoding(packet.Data) 19 | 20 | userID, ok := connectedSessions.GetUserID(sessionIndex) 21 | if ok == false { 22 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, 0, 0, protocol.ERROR_CODE_ENTER_ROOM_INVALID_USER_ID) 23 | return protocol.ERROR_CODE_ENTER_ROOM_INVALID_USER_ID 24 | } 25 | 26 | userInfo := addRoomUserInfo{ 27 | userID, 28 | sessionIndex, 29 | sessionUniqueId, 30 | } 31 | newUser, addResult := room.addUser(userInfo) 32 | 33 | if addResult != protocol.ERROR_CODE_NONE { 34 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, 0, 0, addResult) 35 | return addResult 36 | } 37 | 38 | if connectedSessions.SetRoomNumber(sessionIndex, sessionUniqueId, room.getNumber(), curTime) == false { 39 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, 0, 0, protocol.ERROR_CODE_ENTER_ROOM_INVALID_SESSION_STATE) 40 | return protocol.ERROR_CODE_ENTER_ROOM_INVALID_SESSION_STATE 41 | } 42 | 43 | if room.getCurUserCount() > 1 { 44 | //룸의 다른 유저에게 통보한다. 45 | room._sendNewUserInfoPacket(newUser) 46 | 47 | // 지금 들어온 유저에게 이미 채널에 있는 유저들의 정보를 보낸다 48 | room._sendUserInfoListPacket(newUser) 49 | } 50 | 51 | roomNumebr := room.getNumber() 52 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, roomNumebr, newUser.RoomUniqueId, protocol.ERROR_CODE_NONE) 53 | return protocol.ERROR_CODE_NONE 54 | } 55 | 56 | func _sendRoomEnterResult(sessionIndex int32, sessionUniqueId uint64, roomNumber int32, userUniqueId uint64, result int16) { 57 | response := protocol.RoomEnterResPacket{ 58 | result, 59 | roomNumber, 60 | userUniqueId, 61 | } 62 | sendPacket, _ := response.EncodingPacket() 63 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 64 | } 65 | 66 | func (room *baseRoom) _sendUserInfoListPacket(user *roomUser) { 67 | userCount, userInfoListSize, userInfoListBuffer := room.allocAllUserInfo(user.netSessionUniqueId) 68 | 69 | var response protocol.RoomUserListNtfPacket 70 | response.UserCount = userCount 71 | response.UserList = userInfoListBuffer 72 | sendBuf, _ := response.EncodingPacket(userInfoListSize) 73 | NetLibIPostSendToClient(user.netSessionIndex, user.netSessionUniqueId, sendBuf) 74 | } 75 | 76 | func (room *baseRoom) _sendNewUserInfoPacket(user *roomUser) { 77 | userInfoSize, userInfoListBuffer := room._allocUserInfo(user) 78 | 79 | var response protocol.RoomNewUserNtfPacket 80 | response.User = userInfoListBuffer 81 | sendBuf, packetSize := response.EncodingPacket(userInfoSize) 82 | room.broadcastPacket(int16(packetSize), sendBuf, user.netSessionUniqueId) // 자신을 제외하고 모든 유저에게 Send 83 | } 84 | -------------------------------------------------------------------------------- /chatServer/roomPkg/room_PacketLeave.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "time" 5 | 6 | . "gohipernetFake" 7 | 8 | "main/connectedSessions" 9 | "main/protocol" 10 | ) 11 | 12 | func (room *baseRoom) _packetProcess_LeaveUser(user *roomUser, packet protocol.Packet) int16 { 13 | room._leaveUserProcess(user) 14 | 15 | sessionIndex := packet.UserSessionIndex 16 | sessionUniqueId := packet.UserSessionUniqueId 17 | _sendRoomLeaveResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_NONE) 18 | return protocol.ERROR_CODE_NONE 19 | } 20 | 21 | func (room *baseRoom) _leaveUserProcess(user *roomUser) { 22 | roomUserUniqueId := user.RoomUniqueId 23 | userSessionIndex := user.netSessionIndex 24 | userSessionUniqueId := user.netSessionUniqueId 25 | 26 | room._removeUser(user) 27 | 28 | room._sendRoomLeaveUserNotify(roomUserUniqueId, userSessionUniqueId) 29 | 30 | curTime := time.Now().Unix() 31 | connectedSessions.SetRoomNumber(userSessionIndex, userSessionUniqueId, -1, curTime) 32 | } 33 | 34 | func _sendRoomLeaveResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 35 | response := protocol.RoomLeaveResPacket{result} 36 | sendPacket, _ := response.EncodingPacket() 37 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 38 | } 39 | 40 | func (room *baseRoom) _sendRoomLeaveUserNotify(roomUserUniqueId uint64, userSessionUniqueId uint64) { 41 | notifyPacket := protocol.RoomLeaveUserNtfPacket{roomUserUniqueId} 42 | sendBuf, packetSize := notifyPacket.EncodingPacket() 43 | room.broadcastPacket(int16(packetSize), sendBuf, userSessionUniqueId) 44 | } 45 | -------------------------------------------------------------------------------- /chatServer2/chatServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "strconv" 6 | "strings" 7 | "time" 8 | 9 | "go.uber.org/zap" 10 | 11 | . "gohipernetFake" 12 | "main/connectedSessions" 13 | "main/protocol" 14 | "main/roomPkg" 15 | ) 16 | 17 | type ChatServer struct { 18 | ServerIndex int 19 | IP string 20 | Port int 21 | 22 | appConfig configAppServer 23 | 24 | _roomMgr *roomPkg.RoomManager 25 | 26 | //_timerScheduler *TimerScheduler //현재는 사용하지 않음 27 | } 28 | 29 | func createServer(netConfig NetworkConfig, appConfig configAppServer) { 30 | NTELIB_LOG_INFO("CreateServer !!!") 31 | 32 | if _checkConfig(netConfig, appConfig) == false { 33 | return 34 | } 35 | 36 | var server ChatServer 37 | 38 | if server.setIPAddress(netConfig.BindAddress) == false { 39 | NTELIB_LOG_ERROR("fail. server address") 40 | return 41 | } 42 | 43 | protocol.Init_packet(); 44 | 45 | server.init(int32(netConfig.MaxSessionCount), appConfig) 46 | 47 | networkFunctor := SessionNetworkFunctors{} 48 | networkFunctor.OnConnect = server.OnConnect 49 | networkFunctor.OnReceive = server.OnReceive 50 | networkFunctor.OnReceiveBufferedData = nil 51 | networkFunctor.OnClose = server.OnClose 52 | networkFunctor.PacketTotalSizeFunc = PacketTotalSize 53 | networkFunctor.PacketHeaderSize = PACKET_HEADER_SIZE 54 | networkFunctor.IsClientSession = true 55 | 56 | // 실습에서는 아래 코드 호출하여도 적용 되자 않음 57 | NetLibInitNetwork(protocol.ClientHeaderSize(), protocol.ServerHeaderSize()) 58 | 59 | NetLibStartNetwork(&netConfig, networkFunctor) 60 | 61 | server.Stop() 62 | } 63 | 64 | func (server *ChatServer) setIPAddress(ipAddress string) bool { 65 | results := strings.Split(ipAddress, ":") 66 | if len(results) != 2 { 67 | return false 68 | } 69 | 70 | server.IP = results[0] 71 | server.Port, _ = strconv.Atoi(results[1]) 72 | 73 | NTELIB_LOG_INFO("Server Address", zap.String("IP", server.IP), zap.Int("Port", server.Port)) 74 | return true 75 | } 76 | 77 | func (server *ChatServer) init(maxClientSessionCount int32, appConfig configAppServer) bool { 78 | server.appConfig = appConfig 79 | 80 | maxUserCount := int32((appConfig.RoomMaxCount * appConfig.RoomMaxUserCount) + 3) // 3은 관리자 수 81 | checkStateConfig := connectedSessions.CheckSessionStateConfig{ 82 | appConfig.CheckCountAtOnce, 83 | appConfig.CheckReriodMillSec, 84 | appConfig.LoginWaitTimeSec, 85 | appConfig.DisConnectWaitTimeSec, 86 | appConfig.RoomEnterWaitTimeSec, 87 | appConfig.PingWaitTimeSec, 88 | appConfig.MaxRequestCountPerSecond} 89 | connectedSessions.Init(maxClientSessionCount, maxUserCount, checkStateConfig) 90 | connectedSessions.Start() 91 | 92 | config := roomPkg.RoomConfig{int32(appConfig.RoomStartNum), 93 | int32(appConfig.RoomMaxCount), 94 | int32(appConfig.RoomMaxUserCount), 95 | int32(appConfig.RoomCountByGoroutine), 96 | int32(appConfig.RoomMaxProcessBufferCount), 97 | int32(appConfig.RoomInternalPacketChanBufferCount) } 98 | 99 | server._roomMgr = roomPkg.NewRoomManager(config) 100 | server._roomMgr.Start() 101 | 102 | //server._init_TimerScheduler() 103 | return true 104 | } 105 | 106 | /*func (server *ChatServer) _init_TimerScheduler() { 107 | NTELIB_LOG_INFO("Start main TimerScheduler") 108 | 109 | server._timerScheduler = new(TimerScheduler) 110 | server._timerScheduler.Start() 111 | }*/ 112 | 113 | func (server *ChatServer) PacketTotalSize(data []byte) int16 { 114 | totalsize := binary.LittleEndian.Uint16(data) 115 | return int16(totalsize) 116 | } 117 | 118 | func (server *ChatServer) Stop() { 119 | NTELIB_LOG_INFO("chatServer Stop !!!") 120 | 121 | server._roomMgr.Stop() 122 | 123 | NTELIB_LOG_INFO("chatServer Stop Waitting...") 124 | time.Sleep(1 * time.Second) 125 | } 126 | 127 | 128 | func _checkConfig(netConfig NetworkConfig, appConfig configAppServer) bool { 129 | userCount := appConfig.RoomMaxUserCount * appConfig.RoomMaxCount 130 | 131 | if netConfig.MaxSessionCount < userCount { 132 | NTELIB_LOG_ERROR("userCount less than netConfig.MaxSessionCount", zap.Int("userCount", userCount), zap.Int("MaxSessionCount", netConfig.MaxSessionCount)) 133 | return false 134 | } 135 | return true 136 | } 137 | -------------------------------------------------------------------------------- /chatServer2/clientSessionEvent.go: -------------------------------------------------------------------------------- 1 | // 클라이언트 세션에 대한 네트워크 이벤트 처리 2 | package main 3 | 4 | import ( 5 | "go.uber.org/zap" 6 | 7 | "main/connectedSessions" 8 | . "gohipernetFake" 9 | ) 10 | 11 | func (server *ChatServer) OnConnect(sessionIndex int32, sessionUniqueID uint64) { 12 | NTELIB_LOG_INFO("client OnConnect", zap.Int32("sessionIndex",sessionIndex), zap.Uint64("sessionUniqueId",sessionUniqueID)) 13 | 14 | connectedSessions.AddSession(sessionIndex, sessionUniqueID) 15 | } 16 | 17 | func (server *ChatServer) OnReceive(sessionIndex int32, sessionUniqueID uint64, data []byte) bool { 18 | server._distributePacket(sessionIndex, sessionUniqueID, data) 19 | return true 20 | } 21 | 22 | func (server *ChatServer) OnClose(sessionIndex int32, sessionUniqueID uint64) { 23 | NTELIB_LOG_INFO("client OnCloseClientSession", zap.Int32("sessionIndex", sessionIndex), zap.Uint64("sessionUniqueId", sessionUniqueID)) 24 | 25 | server.disConnectClient(sessionIndex, sessionUniqueID) 26 | } 27 | 28 | 29 | 30 | func (server *ChatServer) disConnectClient(sessionIndex int32, sessionUniqueId uint64) { 31 | // 로그인도 안한 유저라면 그냥 여기서 처리한다. 32 | if connectedSessions.IsLoginUser(sessionIndex) == false { 33 | NTELIB_LOG_INFO("DisConnectClient - Not Login User", zap.Int32("sessionIndex", sessionIndex)) 34 | connectedSessions.RemoveSession(sessionIndex, false) 35 | return 36 | } 37 | 38 | connectedTime := connectedSessions.GetConnectTimeSec(sessionIndex) 39 | if connectedTime == 0 { 40 | NTELIB_LOG_INFO("DisConnectClient - getConnectTimeSec is 0!") 41 | } else { 42 | //DB.WriteUserLastLoginTime(connectedTime, userID) 43 | } 44 | 45 | 46 | roomNum, roomNumOfEntering := connectedSessions.GetRoomNumber(sessionIndex) 47 | 48 | connectedSessions.RemoveSession(sessionIndex, true) 49 | 50 | if roomNum > -1 { 51 | server._roomMgr.DisConnectedUser(sessionUniqueId, roomNum) 52 | } 53 | 54 | // 현재 방에 들어가고 있는 중이므로 방에서 유저를 뺀다. 55 | if roomNumOfEntering > -1 && roomNum != roomNumOfEntering { 56 | server._roomMgr.DisConnectedUser(sessionUniqueId, roomNumOfEntering) 57 | } 58 | 59 | // 해당 세션을 초기화 한다. 메모리를 지우고, redis도 지운다 60 | NTELIB_LOG_INFO("DisConnectClient - Login User", zap.Int32("sessionIndex", sessionIndex)) 61 | } -------------------------------------------------------------------------------- /chatServer2/configAppServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | . "gohipernetFake" 5 | ) 6 | 7 | type configAppServer struct { 8 | GameName string 9 | 10 | RoomMaxCount int 11 | RoomStartNum int 12 | RoomMaxUserCount int 13 | RoomMaxProcessBufferCount int 14 | RoomCountByGoroutine int 15 | RoomInternalPacketChanBufferCount int 16 | 17 | CheckCountAtOnce int 18 | CheckReriodMillSec int 19 | LoginWaitTimeSec int 20 | DisConnectWaitTimeSec int 21 | RoomEnterWaitTimeSec int 22 | PingWaitTimeSec int 23 | MaxRequestCountPerSecond int 24 | } 25 | 26 | func (config configAppServer) writeServerConfig() { 27 | NTELIB_LOG_INFO("writeServerConfig") 28 | /*NTELIB_LOG_INFO("writeServerConfig - " + config.GameName, 29 | zap.Int("RoomMaxCount", config.RoomMaxCount), 30 | zap.Int("RoomStartNum", config.RoomStartNum), 31 | zap.Int("RoomMaxUserCount", config.RoomMaxUserCount), 32 | zap.Int("RoomMaxProcessBufferCount", config.RoomMaxProcessBufferCount), 33 | zap.Int("RoomCountByGoroutine", config.RoomCountByGoroutine), 34 | zap.Int("RoomInternalPacketChanBufferCount", config.RoomInternalPacketChanBufferCount), 35 | zap.Int("CheckCountAtOnce", config.CheckCountAtOnce), 36 | zap.Int("CheckReriodMillSec", config.CheckReriodMillSec), 37 | zap.Int("LoginWaitTimeSec", config.LoginWaitTimeSec), 38 | zap.Int("DisConnectWaitTimeSec", config.DisConnectWaitTimeSec), 39 | zap.Int("RoomEnterWaitTimeSec", config.RoomEnterWaitTimeSec), 40 | zap.Int("PingWaitTimeSec", config.PingWaitTimeSec), 41 | zap.Int("MaxRequestCountPerSecond", config.MaxRequestCountPerSecond))*/ 42 | } -------------------------------------------------------------------------------- /chatServer2/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | go.uber.org/atomic v1.4.0 // indirect 8 | go.uber.org/multierr v1.1.0 // indirect 9 | go.uber.org/zap v1.10.0 10 | gohipernetFake v0.0.0 11 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect 12 | ) 13 | 14 | replace gohipernetFake v0.0.0 => ../gohipernetFake 15 | -------------------------------------------------------------------------------- /chatServer2/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= 4 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 5 | go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= 6 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 7 | go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= 8 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 9 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 10 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 11 | -------------------------------------------------------------------------------- /chatServer2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | 6 | "go.uber.org/zap" 7 | 8 | . "gohipernetFake" 9 | "main/protocol" 10 | ) 11 | 12 | func main() { 13 | NetLibInitLog() 14 | 15 | netConfig, appConfig := parseAppConfig() 16 | appConfig.writeServerConfig() 17 | netConfig.WriteNetworkConfig(true) 18 | 19 | 20 | protocol.Init_packet() 21 | 22 | NTELIB_LOG_INFO("[[protocolHeaderSize]]", 23 | zap.Int16("ClientHeaderSize", protocol.ClientHeaderSize()), 24 | zap.Int16("ServerHeaderSize", protocol.ServerHeaderSize())) 25 | 26 | 27 | // 아래 함수를 호출하면 강제적으로 종료 시킬 때까지 대기 상태가 된다. 28 | createServer(netConfig, appConfig) 29 | } 30 | 31 | func parseAppConfig() (NetworkConfig, configAppServer) { 32 | client := NetworkConfig{} 33 | appConfig := configAppServer{} 34 | 35 | flag.BoolVar(&client.IsTcp4Addr,"c_IsTcp4Addr", true, "bool flag") 36 | flag.StringVar(&client.BindAddress,"c_BindAddress", "127.0.0.1:11021", "string flag") 37 | flag.IntVar(&client.MaxSessionCount,"c_MaxSessionCount", 0, "int flag") 38 | flag.IntVar(&client.MaxPacketSize,"c_MaxPacketSize", 0, "int flag") 39 | flag.IntVar(&client.MaxReceiveBufferSize,"c_MaxReceiveBufferSize", 0, "int flag") 40 | 41 | 42 | // 43 | flag.StringVar(&appConfig.GameName,"GameName", "default", "string flag") 44 | flag.IntVar(&appConfig.RoomMaxCount,"RoomMaxCount", 0, "int flag") 45 | flag.IntVar(&appConfig.RoomStartNum,"RoomStartNum", 0, "int flag") 46 | flag.IntVar(&appConfig.RoomMaxUserCount,"RoomMaxUserCount", 0, "RoomMaxUserCount flag") 47 | flag.IntVar(&appConfig.RoomMaxProcessBufferCount,"RoomMaxProcessBufferCount", 0, "int flag") 48 | flag.IntVar(&appConfig.RoomCountByGoroutine,"RoomCountByGoroutine", 0, "int flag") 49 | flag.IntVar(&appConfig.RoomInternalPacketChanBufferCount,"RoomInternalPacketChanBufferCount", 0, "int flag") 50 | 51 | flag.IntVar(&appConfig.CheckCountAtOnce,"CheckCountAtOnce", 0, "int flag") 52 | flag.IntVar(&appConfig.CheckReriodMillSec,"CheckReriodMillSec", 0, "int flag") 53 | flag.IntVar(&appConfig.LoginWaitTimeSec,"LoginWaitTimeSec", 0, "int flag") 54 | flag.IntVar(&appConfig.DisConnectWaitTimeSec,"DisConnectWaitTimeSec", 0, "int flag") 55 | flag.IntVar(&appConfig.RoomEnterWaitTimeSec,"RoomEnterWaitTimeSec", 0, "int flag") 56 | flag.IntVar(&appConfig.PingWaitTimeSec,"PingWaitTimeSec", 0, "int flag") 57 | flag.IntVar(&appConfig.MaxRequestCountPerSecond,"MaxRequestCountPerSecond", 0, "int flag") 58 | 59 | flag.Parse() 60 | return client, appConfig 61 | } -------------------------------------------------------------------------------- /chatServer2/protocol/errorcode.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | const ( 4 | ERROR_CODE_NONE = 1 5 | 6 | ERROR_CODE_PACKET_DECODING_FAIL = 51 7 | ERROR_CODE_PACKET_NOT_LOGIN_USER = 52 8 | ERROR_CODE_ROOM_NOT_REGISTED_PACKET_ID = 53 9 | ERROR_CODE_USER_NOT_IN_ROOM = 54 10 | 11 | ERROR_CODE_DISCONNECT_UNAUTHENTICATED_USER = 61 12 | 13 | ERROR_CODE_LOGIN_USER_DUPLICATION = 111 14 | ERROR_CODE_LOGIN_USER_INVALID_ID = 112 15 | 16 | ERROR_CODE_ROOM_NOT_IN_USER = 121 17 | ERROR_CODE_ROOM_INVALIDE_NUMBER = 122 18 | 19 | ERROR_CODE_ENTER_ROOM_ALREADY = 131 20 | ERROR_CODE_ENTER_ROOM_PREV_WORKING = 132 21 | ERROR_CODE_ENTER_ROOM_INVALID_USER_ID = 133 22 | ERROR_CODE_ENTER_ROOM_USER_FULL = 134 23 | ERROR_CODE_ENTER_ROOM_DUPLCATION_USER = 135 24 | ERROR_CODE_ENTER_ROOM_INVALID_SESSION_STATE = 136 25 | ERROR_CODE_ENTER_ROOM_AUTO_ROOM_NUMBER = 137 26 | 27 | ERROR_CODE_LEAVE_ROOM_INTERNAL_INVALID_USER = 141 28 | 29 | ERROR_CODE_ROOM_CHAT_CHAT_MSG_LEN = 151 30 | 31 | ERROR_CODE_ROOM_RELAY_FAIL_DECPDING = 161 32 | 33 | ERROR_CODE_ROOM_NOT_REGISTED_INTERNAL_PACKET_ID = 252 // 번역 필요없음 34 | 35 | 36 | ) 37 | -------------------------------------------------------------------------------- /chatServer2/protocol/internalPacket.go: -------------------------------------------------------------------------------- 1 | // 내부에서 사용하는 패킷 2 | package protocol 3 | 4 | import ( 5 | . "gohipernetFake" 6 | ) 7 | 8 | type InternalPacket struct { 9 | RoomIndex int32 10 | Id int16 11 | DataSize int16 12 | Data []byte 13 | } 14 | 15 | 16 | type InternalPacketDisConnectedUserToRoom struct{ 17 | SessionUniqueId uint64 18 | RoomNum int32 19 | } 20 | 21 | func (packet *InternalPacketDisConnectedUserToRoom ) Encoding() ([]byte, int16) { 22 | totalSize := int16(8 + 4) 23 | sendBuf := make([]byte, totalSize) 24 | 25 | writer := MakeWriter(sendBuf, true) 26 | writer.WriteU64(packet.SessionUniqueId) 27 | writer.WriteS32(packet.RoomNum) 28 | return sendBuf, totalSize 29 | } 30 | 31 | func (packet *InternalPacketDisConnectedUserToRoom) Decoding(Data []byte) bool { 32 | reader := MakeReader(Data, true) 33 | 34 | packet.SessionUniqueId, _ = reader.ReadU64() 35 | packet.RoomNum, _ = reader.ReadS32() 36 | return true 37 | } -------------------------------------------------------------------------------- /chatServer2/protocol/packetID.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | 4 | const ( 5 | PACKET_ID_DEV_ECHO_REQ = 192 6 | PACKET_ID_DEV_ECHO_RES = 193 7 | 8 | PACKET_ID_PING_REQ = 201 9 | PACKET_ID_PING_RES = 201 10 | PACKET_ID_ERROR_NTF = 203 11 | 12 | 13 | PACKET_ID_LOGIN_REQ = 701 14 | PACKET_ID_LOGIN_RES = 702 15 | 16 | PACKET_ID_ROOM_ENTER_REQ = 721 17 | PACKET_ID_ROOM_ENTER_RES = 722 18 | PACKET_ID_ROOM_USER_LIST_NTF = 723 19 | PACKET_ID_ROOM_NEW_USER_NTF = 724 20 | 21 | PACKET_ID_ROOM_LEAVE_REQ = 726 22 | PACKET_ID_ROOM_LEAVE_RES = 727 23 | PACKET_ID_ROOM_LEAVE_USER_NTF = 728 24 | 25 | PACKET_ID_ROOM_CHAT_REQ = 731 26 | PACKET_ID_ROOM_CHAT_RES = 732 27 | PACKET_ID_ROOM_CHAT_NOTIFY = 733 28 | 29 | PACKET_ID_ROOM_RELAY_REQ = 741 30 | PACKET_ID_ROOM_RELAY_NTF = 742 31 | 32 | 33 | INTERNAL_PACKET_ID_DISCONNECTED_USER_TO_ROOM = 1602 34 | ) 35 | 36 | 37 | -------------------------------------------------------------------------------- /chatServer2/roomPkg/define.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import "main/protocol" 4 | 5 | type RoomConfig struct { 6 | StartRoomNumber int32 7 | MaxRoomCount int32 8 | MaxUserCount int32 9 | 10 | RoomCountByGoroutine int32 11 | ChanPacketBufferCount int32 12 | InternalPacketChanBufferCount int32 13 | } 14 | 15 | 16 | type roomUser struct { 17 | netSessionIndex int32 18 | netSessionUniqueId uint64 19 | 20 | // <<< 다른 유저에게 알려줘야 하는 정보 21 | RoomUniqueId uint64 22 | IDLen int8 23 | ID [protocol.MAX_USER_ID_BYTE_LENGTH]byte 24 | // >>> 다른 유저에게 알려줘야 하는 정보 25 | 26 | packetDataSize int16 27 | } 28 | 29 | func (user *roomUser) init(userID []byte, uniqueId uint64) { 30 | idlen := len(userID) 31 | 32 | user.IDLen = int8(idlen) 33 | copy(user.ID[:], userID) 34 | 35 | user.RoomUniqueId = uniqueId 36 | } 37 | 38 | func (user *roomUser) SetNetworkInfo(sessionIndex int32, sessionUniqueId uint64) { 39 | user.netSessionIndex = sessionIndex 40 | user.netSessionUniqueId = sessionUniqueId 41 | } 42 | 43 | func (user *roomUser) PacketDataSize() int16 { 44 | return int16(1) + int16(user.IDLen) + 8 45 | } 46 | 47 | 48 | 49 | type RoomMemberPacket struct { 50 | RoomIndex int 51 | 52 | UserID []byte 53 | SessionIndex int32 54 | SessionUniqueId uint64 55 | } 56 | 57 | 58 | 59 | type userNetworkInfo struct { 60 | sessionIndex int32 61 | sessionUniqueId uint64 62 | } 63 | 64 | type addRoomUserInfo struct { 65 | userID []byte 66 | 67 | netSessionIndex int32 68 | netSessionUniqueId uint64 69 | } 70 | -------------------------------------------------------------------------------- /chatServer2/roomPkg/room_Packet.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | 6 | "main/protocol" 7 | . "gohipernetFake" 8 | ) 9 | 10 | func (room *baseRoom) _packetProcess_Relay(user *roomUser, packet protocol.Packet) int16 { 11 | var relayNotify protocol.RoomRelayNtfPacket 12 | relayNotify.RoomUserUniqueId = user.RoomUniqueId 13 | relayNotify.Data = packet.Data 14 | notifySendBuf, packetSize := relayNotify.EncodingPacket(packet.DataSize) 15 | room.broadcastPacket(packetSize, notifySendBuf, 0) 16 | 17 | NTELIB_LOG_DEBUG("Room Relay", zap.String("Sender", string(user.ID[:]))) 18 | return protocol.ERROR_CODE_NONE 19 | } -------------------------------------------------------------------------------- /chatServer2/roomPkg/room_PacketChat.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | 6 | "main/protocol" 7 | . "gohipernetFake" 8 | ) 9 | 10 | 11 | func (room *baseRoom) _packetProcess_Chat(user *roomUser, packet protocol.Packet) int16 { 12 | sessionIndex := packet.UserSessionIndex 13 | sessionUniqueId := packet.UserSessionUniqueId 14 | 15 | var chatPacket protocol.RoomChatReqPacket 16 | if chatPacket.Decoding(packet.Data) == false { 17 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_PACKET_DECODING_FAIL) 18 | return protocol.ERROR_CODE_PACKET_DECODING_FAIL 19 | } 20 | 21 | // 채팅 최대길이 제한 22 | msgLen := len(chatPacket.Msgs) 23 | if msgLen < 1 || msgLen > protocol.MAX_CHAT_MESSAGE_BYTE_LENGTH { 24 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_ROOM_CHAT_CHAT_MSG_LEN) 25 | return protocol.ERROR_CODE_ROOM_CHAT_CHAT_MSG_LEN 26 | } 27 | 28 | 29 | var chatNotifyResponse protocol.RoomChatNtfPacket 30 | chatNotifyResponse.RoomUserUniqueId = user.RoomUniqueId 31 | chatNotifyResponse.MsgLen = int16(msgLen) 32 | chatNotifyResponse.Msg = chatPacket.Msgs 33 | notifySendBuf, packetSize := chatNotifyResponse.EncodingPacket() 34 | room.broadcastPacket(packetSize, notifySendBuf, 0) 35 | 36 | 37 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_NONE) 38 | 39 | NTELIB_LOG_DEBUG("ParkChannel Chat Notify Function", zap.String("Sender", string(user.ID[:])), 40 | zap.String("Message", string(chatPacket.Msgs))) 41 | 42 | return protocol.ERROR_CODE_NONE 43 | } 44 | 45 | func _sendRoomChatResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 46 | response := protocol.RoomChatResPacket{ result } 47 | sendPacket, _ := response.EncodingPacket() 48 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 49 | } -------------------------------------------------------------------------------- /chatServer2/timerScheduler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | . "gohipernetFake" 7 | ) 8 | 9 | type TimerScheduler struct { 10 | onDone chan struct{} 11 | } 12 | 13 | func (scheduler *TimerScheduler) Start() { 14 | scheduler.onDone = make(chan struct{}) 15 | go scheduler._periodicLoop_goroutine() 16 | } 17 | 18 | func (scheduler *TimerScheduler) End() { 19 | close(scheduler.onDone) 20 | } 21 | 22 | func (scheduler *TimerScheduler) _periodicLoop_goroutine() { 23 | NTELIB_LOG_INFO("Start TimerScheduler goroutine !!!") 24 | 25 | for { 26 | if scheduler._periodicLoop_goroutine_Impl() { 27 | NTELIB_LOG_INFO("Wanted Stop TimerScheduler goroutine !!!") 28 | break 29 | } 30 | } 31 | 32 | NTELIB_LOG_INFO("Stop TimerScheduler goroutine !!!") 33 | } 34 | 35 | func (scheduler *TimerScheduler) _periodicLoop_goroutine_Impl() bool { 36 | IsWantedTermination := false 37 | 38 | secondTimeticker := time.NewTicker(time.Second) 39 | 40 | time.Sleep(2 * time.Second) // 순서 종속성으로 인해 2초 뒤 시작한다. 41 | defer PrintPanicStack() 42 | defer secondTimeticker.Stop() 43 | 44 | for { 45 | select { 46 | /*case secondTime := <-secondTimeticker.C: 47 | { 48 | NetLibSetCurrnetUnixTime(secondTime.Unix()) 49 | }*/ 50 | case <-scheduler.onDone: 51 | { 52 | IsWantedTermination = true 53 | break 54 | } 55 | } 56 | } 57 | 58 | return IsWantedTermination 59 | } 60 | -------------------------------------------------------------------------------- /chatServer_msgpack/chatServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | . "gohipernetFake" 6 | "strconv" 7 | "strings" 8 | 9 | "main/connectedSessions" 10 | "main/protocol" 11 | "main/roomPkg" 12 | ) 13 | 14 | type configAppServer struct { 15 | GameName string 16 | 17 | RoomMaxCount int32 18 | RoomStartNum int32 19 | RoomMaxUserCount int32 20 | } 21 | 22 | type ChatServer struct { 23 | ServerIndex int 24 | IP string 25 | Port int 26 | 27 | PacketChan chan protocol.Packet 28 | 29 | RoomMgr *roomPkg.RoomManager 30 | } 31 | 32 | func createAnsStartServer(netConfig NetworkConfig, appConfig configAppServer) { 33 | OutPutLog(LOG_LEVEL_INFO,"", 0,"CreateServer !!!") 34 | 35 | var server ChatServer 36 | 37 | if server.setIPAddress(netConfig.BindAddress) == false { 38 | OutPutLog(LOG_LEVEL_ERROR,"", 0,"fail. server address") 39 | return 40 | } 41 | 42 | protocol.Init_packet(); 43 | 44 | maxUserCount := appConfig.RoomMaxCount * appConfig.RoomMaxUserCount 45 | connectedSessions.Init(netConfig.MaxSessionCount, maxUserCount) 46 | 47 | server.PacketChan = make(chan protocol.Packet, 256) 48 | 49 | roomConfig := roomPkg.RoomConfig{ 50 | appConfig.RoomStartNum, 51 | appConfig.RoomMaxCount, 52 | appConfig.RoomMaxUserCount, 53 | } 54 | server.RoomMgr = roomPkg.NewRoomManager(roomConfig) 55 | 56 | 57 | go server.PacketProcess_goroutine() 58 | 59 | 60 | networkFunctor := SessionNetworkFunctors{} 61 | networkFunctor.OnConnect = server.OnConnect 62 | networkFunctor.OnReceive = server.OnReceive 63 | networkFunctor.OnReceiveBufferedData = nil 64 | networkFunctor.OnClose = server.OnClose 65 | networkFunctor.PacketTotalSizeFunc = PacketTotalSize 66 | networkFunctor.PacketHeaderSize = PACKET_HEADER_SIZE 67 | networkFunctor.IsClientSession = true 68 | 69 | 70 | NetLibStartNetwork(&netConfig, networkFunctor) 71 | } 72 | 73 | func (server *ChatServer) setIPAddress(ipAddress string) bool { 74 | results := strings.Split(ipAddress, ":") 75 | if len(results) != 2 { 76 | return false 77 | } 78 | 79 | server.IP = results[0] 80 | server.Port, _ = strconv.Atoi(results[1]) 81 | 82 | return true 83 | } 84 | 85 | func (server *ChatServer) OnConnect(sessionIndex int32, sessionUniqueID uint64) { 86 | OutPutLog(LOG_LEVEL_INFO,"", 0,fmt.Sprintf("[OnConnect] sessionIndex: %d", sessionIndex)) 87 | 88 | connectedSessions.AddSession(sessionIndex, sessionUniqueID) 89 | } 90 | 91 | func (server *ChatServer) OnReceive(sessionIndex int32, sessionUniqueID uint64, data []byte) bool { 92 | server.DistributePacket(sessionIndex, sessionUniqueID, data) 93 | return true 94 | } 95 | 96 | func (server *ChatServer) OnClose(sessionIndex int32, sessionUniqueID uint64) { 97 | OutPutLog(LOG_LEVEL_INFO,"", 0,fmt.Sprintf("[OnConnect] sessionIndex: %d", sessionIndex)) 98 | 99 | server.disConnectClient(sessionIndex, sessionUniqueID) 100 | } 101 | 102 | func (server *ChatServer) disConnectClient(sessionIndex int32, sessionUniqueId uint64) { 103 | // 로그인도 안한 유저라면 그냥 여기서 처리한다. 104 | // 방 입장을 안한 유저라면 여기서 처리해도 괜찮지만 아래로 넘긴다. 105 | if connectedSessions.IsLoginUser(sessionIndex) == false { 106 | connectedSessions.RemoveSession(sessionIndex, false) 107 | return 108 | } 109 | 110 | 111 | packet := protocol.Packet { 112 | sessionIndex, 113 | sessionUniqueId, 114 | protocol.PACKET_ID_SESSION_CLOSE_SYS, 115 | 0, 116 | nil, 117 | } 118 | 119 | server.PacketChan <- packet 120 | } 121 | -------------------------------------------------------------------------------- /chatServer_msgpack/connectedSessions/session.go: -------------------------------------------------------------------------------- 1 | package connectedSessions 2 | 3 | import ( 4 | "sync/atomic" 5 | 6 | "main/protocol" 7 | ) 8 | 9 | type session struct { 10 | _index int32 11 | 12 | _networkUniqueID uint64 //네트워크 세션의 유니크 ID 13 | 14 | _userID [protocol.MAX_USER_ID_BYTE_LENGTH]byte 15 | _userIDLength int8 16 | 17 | _connectTimeSec int64 // 연결된 시간 18 | _RoomNum int32 // 19 | _RoomNumOfEntering int32 // 현재 입장 중인 룸의 번호 20 | } 21 | 22 | func (session *session) Init(index int32) { 23 | session._index = index 24 | session.Clear() 25 | } 26 | 27 | func (session *session) _ClearUserId() { 28 | session._userIDLength = 0 29 | } 30 | 31 | func (session *session) Clear() { 32 | session._ClearUserId() 33 | session.setRoomNumber(0, -1, 0) 34 | session.SetConnectTimeSec(0, 0) 35 | } 36 | 37 | func (session *session) GetIndex() int32 { 38 | return session._index 39 | } 40 | 41 | func (session *session) GetNetworkUniqueID() uint64 { 42 | return atomic.LoadUint64(&session._networkUniqueID) 43 | } 44 | 45 | func (session *session) validNetworkUniqueID(uniqueId uint64) bool { 46 | return atomic.LoadUint64(&session._networkUniqueID) == uniqueId 47 | } 48 | 49 | func (session *session) GetNetworkInfo() (int32, uint64) { 50 | index := session.GetIndex() 51 | uniqueID := atomic.LoadUint64(&session._networkUniqueID) 52 | return index, uniqueID 53 | } 54 | 55 | func (session *session) setUserID(userID []byte) { 56 | session._userIDLength = int8(len(userID)) 57 | copy(session._userID[:], userID) 58 | } 59 | 60 | func (session *session) getUserID() []byte { 61 | return session._userID[0:session._userIDLength] 62 | } 63 | 64 | func (session *session) getUserIDLength() int8 { 65 | return session._userIDLength 66 | } 67 | 68 | func (session *session) SetConnectTimeSec(timeSec int64, uniqueID uint64) { 69 | atomic.StoreInt64(&session._connectTimeSec, timeSec) 70 | atomic.StoreUint64(&session._networkUniqueID, uniqueID) 71 | } 72 | 73 | func (session *session) GetConnectTimeSec() int64 { 74 | return atomic.LoadInt64(&session._connectTimeSec) 75 | } 76 | 77 | func (session *session) SetUser(sessionUniqueId uint64, 78 | userID []byte, 79 | curTimeSec int64, 80 | ) { 81 | session.setUserID(userID) 82 | session.setRoomNumber(sessionUniqueId, -1, curTimeSec) // 방어적인 목적으로 채널 번호 초기화 83 | } 84 | 85 | func (session *session) IsAuth() bool { 86 | if session._userIDLength > 0 { 87 | return true 88 | } 89 | 90 | return false 91 | } 92 | 93 | func (session *session) setRoomEntering(roomNum int32) bool { 94 | if atomic.CompareAndSwapInt32(&session._RoomNumOfEntering, -1, roomNum) == false { 95 | return false 96 | } 97 | 98 | return true 99 | } 100 | 101 | func (session *session) setRoomNumber(sessionUniqueId uint64, roomNum int32, curTimeSec int64) bool { 102 | if roomNum == -1 { 103 | atomic.StoreInt32(&session._RoomNum, roomNum) 104 | atomic.StoreInt32(&session._RoomNumOfEntering, roomNum) 105 | return true 106 | } 107 | 108 | if sessionUniqueId != 0 && session.validNetworkUniqueID(sessionUniqueId) == false { 109 | return false 110 | 111 | } 112 | // 입력이 -1이 아닌경우 -1이 아닐 때만 compareswap으로 변경한다. 실패하면 채널 입장도 실패이다. 113 | if atomic.CompareAndSwapInt32(&session._RoomNum, -1, roomNum) == false { 114 | return false 115 | } 116 | 117 | atomic.StoreInt32(&session._RoomNumOfEntering, roomNum) 118 | return true 119 | } 120 | 121 | func (session *session) getRoomNumber() (int32, int32) { 122 | roomNum := atomic.LoadInt32(&session._RoomNum) 123 | roomNumOfEntering := atomic.LoadInt32(&session._RoomNum) 124 | return roomNum, roomNumOfEntering 125 | } 126 | -------------------------------------------------------------------------------- /chatServer_msgpack/connectedSessions/sessionManager.go: -------------------------------------------------------------------------------- 1 | package connectedSessions 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | // 스레드 세이프 해야 한다. 10 | type Manager struct { 11 | _UserIDsessionMap *sync.Map 12 | 13 | _maxSessionCount int32 14 | _sessionList []*session 15 | 16 | _maxUserCount int32 17 | 18 | _currentLoginUserCount int32 19 | } 20 | 21 | var _manager Manager 22 | 23 | func Init(maxSessionCount int, maxUserCount int32) bool { 24 | _manager._UserIDsessionMap = new(sync.Map) 25 | _manager._maxUserCount = maxUserCount 26 | 27 | _manager._maxSessionCount = int32(maxSessionCount) 28 | _manager._sessionList = make([]*session, maxSessionCount) 29 | 30 | for i := 0; i < maxSessionCount; i++ { 31 | _manager._sessionList[i] = new(session) 32 | 33 | index := int32(i) 34 | _manager._sessionList[i].Init(index) 35 | } 36 | 37 | return true 38 | } 39 | 40 | func AddSession(sessionIndex int32, sessionUniqueID uint64) bool { 41 | if _validSessionIndex(sessionIndex) == false { 42 | return false 43 | } 44 | 45 | if _manager._sessionList[sessionIndex].GetConnectTimeSec() > 0 { 46 | return false 47 | } 48 | 49 | // 방어적인 목적으로 한번 더 Clear 한다 50 | _manager._sessionList[sessionIndex].Clear() 51 | 52 | _manager._sessionList[sessionIndex].SetConnectTimeSec(time.Now().Unix(), sessionUniqueID) 53 | return true 54 | } 55 | 56 | func RemoveSession(sessionIndex int32, isLoginedUser bool) bool { 57 | if _validSessionIndex(sessionIndex) == false { 58 | return false 59 | } 60 | 61 | if isLoginedUser { 62 | atomic.AddInt32(&_manager._currentLoginUserCount, -1) 63 | 64 | userID := string(_manager._sessionList[sessionIndex].getUserID()) 65 | _manager._UserIDsessionMap.Delete(userID) 66 | } 67 | 68 | _manager._sessionList[sessionIndex].Clear() 69 | 70 | return true 71 | } 72 | 73 | func _validSessionIndex(index int32) bool { 74 | if index < 0 || index >= _manager._maxSessionCount { 75 | return false 76 | } 77 | return true 78 | } 79 | 80 | func GetNetworkUniqueID(sessionIndex int32) uint64 { 81 | if _validSessionIndex(sessionIndex) == false { 82 | return 0 83 | } 84 | 85 | return _manager._sessionList[sessionIndex].GetNetworkUniqueID() 86 | } 87 | 88 | func GetUserID(sessionIndex int32) ([]byte, bool) { 89 | if _validSessionIndex(sessionIndex) == false { 90 | return nil, false 91 | } 92 | 93 | return _manager._sessionList[sessionIndex].getUserID(), true 94 | } 95 | 96 | func SetLogin(sessionIndex int32, sessionUniqueId uint64, userID []byte, curTimeSec int64) bool { 97 | if _validSessionIndex(sessionIndex) == false { 98 | return false 99 | } 100 | 101 | newUserID := string(userID) 102 | if _, ok := _manager._UserIDsessionMap.Load(newUserID); ok { 103 | return false 104 | } 105 | 106 | _manager._sessionList[sessionIndex].SetUser(sessionUniqueId, userID, curTimeSec) 107 | _manager._UserIDsessionMap.Store(newUserID, _manager._sessionList[sessionIndex]) 108 | 109 | atomic.AddInt32(&_manager._currentLoginUserCount, 1) 110 | return true 111 | } 112 | 113 | func IsLoginUser(sessionIndex int32) bool { 114 | if _validSessionIndex(sessionIndex) == false { 115 | return false 116 | } 117 | 118 | return _manager._sessionList[sessionIndex].IsAuth() 119 | } 120 | 121 | func SetRoomNumber(sessionIndex int32, sessionUniqueId uint64, roomNum int32, curTimeSec int64) bool { 122 | if _validSessionIndex(sessionIndex) == false { 123 | return false 124 | } 125 | 126 | return _manager._sessionList[sessionIndex].setRoomNumber(sessionUniqueId, roomNum, curTimeSec) 127 | } 128 | 129 | func GetRoomNumber(sessionIndex int32) (int32, int32) { 130 | if _validSessionIndex(sessionIndex) == false { 131 | return -1, -1 132 | } 133 | return _manager._sessionList[sessionIndex].getRoomNumber() 134 | } 135 | -------------------------------------------------------------------------------- /chatServer_msgpack/distributePacket.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/vmihailenco/msgpack/v4" 5 | "time" 6 | 7 | . "gohipernetFake" 8 | 9 | "main/connectedSessions" 10 | "main/protocol" 11 | ) 12 | 13 | func (server *ChatServer) DistributePacket(sessionIndex int32, 14 | sessionUniqueId uint64, 15 | packetData []byte, 16 | ) { 17 | packetID := protocol.PeekPacketID(packetData) 18 | bodySize, bodyData := protocol.PeekPacketBody(packetData) 19 | 20 | packet := protocol.Packet{Id: packetID} 21 | packet.UserSessionIndex = sessionIndex 22 | packet.UserSessionUniqueId = sessionUniqueId 23 | packet.Id = packetID 24 | packet.DataSize = bodySize 25 | packet.Data = make([]byte, packet.DataSize) 26 | copy(packet.Data, bodyData) 27 | 28 | server.PacketChan <- packet 29 | } 30 | 31 | 32 | func (server *ChatServer) PacketProcess_goroutine() { 33 | for { 34 | if server.PacketProcess_goroutine_Impl() { 35 | OutPutLog(LOG_LEVEL_INFO,"", 0, "Wanted Stop PacketProcess goroutine") 36 | break 37 | } 38 | } 39 | 40 | OutPutLog(LOG_LEVEL_INFO,"", 0, "Stop rooms PacketProcess goroutine") 41 | } 42 | 43 | func (server *ChatServer) PacketProcess_goroutine_Impl() bool { 44 | IsWantedTermination := false // 여기에서는 의미 없음. 서버 종료를 명시적으로 하는 경우만 유용 45 | defer PrintPanicStack() 46 | 47 | 48 | for { 49 | packet := <-server.PacketChan 50 | sessionIndex := packet.UserSessionIndex 51 | sessionUniqueId := packet.UserSessionUniqueId 52 | bodySize := packet.DataSize 53 | bodyData := packet.Data 54 | 55 | if packet.Id == protocol.PACKET_ID_LOGIN_REQ { 56 | ProcessPacketLogin(sessionIndex, sessionUniqueId, bodySize, bodyData) 57 | } else if packet.Id == protocol.PACKET_ID_SESSION_CLOSE_SYS { 58 | ProcessPacketSessionClosed(server, sessionIndex, sessionUniqueId) 59 | } else { 60 | roomNumber, _ := connectedSessions.GetRoomNumber(sessionIndex) 61 | server.RoomMgr.PacketProcess(roomNumber, packet) 62 | } 63 | } 64 | 65 | return IsWantedTermination 66 | } 67 | 68 | func ProcessPacketLogin(sessionIndex int32, 69 | sessionUniqueId uint64, 70 | bodySize uint16, 71 | bodyData []byte ) { 72 | //DB와 연동하지 않으므로 중복 로그인만 아니면 다 성공으로 한다 73 | var request protocol.LoginReqPacket 74 | err := msgpack.Unmarshal(bodyData, &request) 75 | if err != nil { 76 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_PACKET_DECODING_FAIL) 77 | return 78 | } 79 | 80 | userID := []byte(request.UserID) 81 | curTime := time.Now().Unix() 82 | 83 | if connectedSessions.SetLogin(sessionIndex, sessionUniqueId, userID, curTime) == false { 84 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_LOGIN_USER_DUPLICATION) 85 | return 86 | } 87 | 88 | _sendLoginResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_NONE) 89 | } 90 | 91 | func _sendLoginResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 92 | var response protocol.LoginResPacket 93 | response.Result = int64(result) 94 | 95 | bodyData, err := msgpack.Marshal(response) 96 | if err != nil { 97 | panic(err) 98 | } 99 | 100 | sendPacket := protocol.EncodingPacketHeaderInfo(bodyData, uint16(protocol.PACKET_ID_LOGIN_RES), 0) 101 | 102 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 103 | } 104 | 105 | 106 | func ProcessPacketSessionClosed(server *ChatServer, sessionIndex int32, sessionUniqueId uint64) { 107 | roomNumber, _ := connectedSessions.GetRoomNumber(sessionIndex) 108 | 109 | if roomNumber > -1 { 110 | packet := protocol.Packet{ 111 | sessionIndex, 112 | sessionUniqueId, 113 | protocol.PACKET_ID_ROOM_LEAVE_REQ, 114 | 0, 115 | nil, 116 | } 117 | 118 | server.RoomMgr.PacketProcess(roomNumber, packet) 119 | } 120 | 121 | connectedSessions.RemoveSession(sessionIndex, true) 122 | } 123 | 124 | 125 | -------------------------------------------------------------------------------- /chatServer_msgpack/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/pkg/errors v0.8.1 // indirect 7 | github.com/stretchr/testify v1.4.0 // indirect 8 | github.com/vmihailenco/msgpack/v4 v4.2.1 9 | go.uber.org/atomic v1.4.0 // indirect 10 | go.uber.org/multierr v1.1.0 // indirect 11 | go.uber.org/zap v1.10.0 12 | gohipernetFake v0.0.0 13 | gopkg.in/yaml.v2 v2.2.7 // indirect 14 | ) 15 | 16 | replace gohipernetFake v0.0.0 => ../gohipernetFake 17 | -------------------------------------------------------------------------------- /chatServer_msgpack/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | . "gohipernetFake" 6 | ) 7 | 8 | 9 | func main() { 10 | netConfig, appConfig := parseAppConfig() 11 | netConfig.WriteNetworkConfig(true) 12 | 13 | // 아래 함수를 호출하면 강제적으로 종료 시킬 때까지 대기 상태가 된다. 14 | createAnsStartServer(netConfig, appConfig) 15 | } 16 | 17 | func parseAppConfig() (NetworkConfig, configAppServer) { 18 | appConfig := configAppServer { 19 | "chatServer", 20 | 1000, 21 | 0, 22 | 4, 23 | } 24 | 25 | 26 | netConfig := NetworkConfig{} 27 | 28 | flag.BoolVar(&netConfig.IsTcp4Addr,"c_IsTcp4Addr", true, "bool flag") 29 | flag.StringVar(&netConfig.BindAddress,"c_BindAddress", "127.0.0.1:11021", "string flag") 30 | flag.IntVar(&netConfig.MaxSessionCount,"c_MaxSessionCount", 0, "int flag") 31 | flag.IntVar(&netConfig.MaxPacketSize,"c_MaxPacketSize", 0, "int flag") 32 | 33 | flag.Parse() 34 | return netConfig, appConfig 35 | } -------------------------------------------------------------------------------- /chatServer_msgpack/protocol/errorcode.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | const ( 4 | ERROR_CODE_NONE = 0 5 | 6 | ERROR_CODE_PACKET_DECODING_FAIL = 51 7 | ERROR_CODE_PACKET_ENCODING_FAIL = 52 8 | ERROR_CODE_PACKET_NOT_LOGIN_USER = 53 9 | ERROR_CODE_ROOM_NOT_REGISTED_PACKET_ID = 54 10 | ERROR_CODE_USER_NOT_IN_ROOM = 55 11 | 12 | ERROR_CODE_DISCONNECT_UNAUTHENTICATED_USER = 61 13 | 14 | ERROR_CODE_LOGIN_USER_DUPLICATION = 111 15 | ERROR_CODE_LOGIN_USER_INVALID_ID = 112 16 | 17 | ERROR_CODE_ROOM_NOT_IN_USER = 121 18 | ERROR_CODE_ROOM_INVALIDE_NUMBER = 122 19 | 20 | ERROR_CODE_ENTER_ROOM_ALREADY = 131 21 | ERROR_CODE_ENTER_ROOM_PREV_WORKING = 132 22 | ERROR_CODE_ENTER_ROOM_INVALID_USER_ID = 133 23 | ERROR_CODE_ENTER_ROOM_USER_FULL = 134 24 | ERROR_CODE_ENTER_ROOM_DUPLCATION_USER = 135 25 | ERROR_CODE_ENTER_ROOM_INVALID_SESSION_STATE = 136 26 | ERROR_CODE_ENTER_ROOM_AUTO_ROOM_NUMBER = 137 27 | 28 | ERROR_CODE_LEAVE_ROOM_INTERNAL_INVALID_USER = 141 29 | 30 | ERROR_CODE_ROOM_CHAT_CHAT_MSG_LEN = 151 31 | 32 | ERROR_CODE_ROOM_RELAY_FAIL_DECPDING = 161 33 | 34 | ERROR_CODE_ROOM_NOT_REGISTED_INTERNAL_PACKET_ID = 252 // 번역 필요없음 35 | 36 | 37 | ) 38 | -------------------------------------------------------------------------------- /chatServer_msgpack/protocol/packetID.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | 4 | const ( 5 | PACKET_ID_PING_REQ = 201 6 | PACKET_ID_PING_RES = 202 7 | 8 | PACKET_ID_ERROR_NTF = 203 9 | 10 | PACKET_ID_SESSION_CLOSE_SYS = 211 11 | 12 | 13 | PACKET_ID_LOGIN_REQ = 701 14 | PACKET_ID_LOGIN_RES = 702 15 | 16 | PACKET_ID_ROOM_ENTER_REQ = 721 17 | PACKET_ID_ROOM_ENTER_RES = 722 18 | PACKET_ID_ROOM_USER_LIST_NTF = 723 19 | PACKET_ID_ROOM_NEW_USER_NTF = 724 20 | 21 | PACKET_ID_ROOM_LEAVE_REQ = 726 22 | PACKET_ID_ROOM_LEAVE_RES = 727 23 | PACKET_ID_ROOM_LEAVE_USER_NTF = 728 24 | 25 | PACKET_ID_ROOM_CHAT_REQ = 731 26 | PACKET_ID_ROOM_CHAT_RES = 732 27 | PACKET_ID_ROOM_CHAT_NOTIFY = 733 28 | 29 | PACKET_ID_ROOM_RELAY_REQ = 741 30 | PACKET_ID_ROOM_RELAY_NTF = 742 31 | 32 | 33 | INTERNAL_PACKET_ID_DISCONNECTED_USER_TO_ROOM = 1602 34 | ) 35 | 36 | 37 | -------------------------------------------------------------------------------- /chatServer_msgpack/roomPkg/define.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import "main/protocol" 4 | 5 | type RoomConfig struct { 6 | StartRoomNumber int32 7 | MaxRoomCount int32 8 | MaxUserCount int32 9 | } 10 | 11 | type roomUser struct { 12 | netSessionIndex int32 13 | netSessionUniqueId uint64 14 | 15 | // <<< 다른 유저에게 알려줘야 하는 정보 16 | RoomUniqueId uint64 17 | IDLen int8 18 | ID [protocol.MAX_USER_ID_BYTE_LENGTH]byte 19 | // >>> 다른 유저에게 알려줘야 하는 정보 20 | packetDataSize int16 // 다른 유저에게 알려줘야 하는 정보 의 크기 21 | } 22 | 23 | func (user *roomUser) init(userID []byte, uniqueId uint64) { 24 | idlen := len(userID) 25 | 26 | user.IDLen = int8(idlen) 27 | copy(user.ID[:], userID) 28 | 29 | user.RoomUniqueId = uniqueId 30 | } 31 | 32 | func (user *roomUser) SetNetworkInfo(sessionIndex int32, sessionUniqueId uint64) { 33 | user.netSessionIndex = sessionIndex 34 | user.netSessionUniqueId = sessionUniqueId 35 | } 36 | 37 | func (user *roomUser) PacketDataSize() int16 { 38 | return int16(1) + int16(user.IDLen) + 8 39 | } 40 | 41 | type addRoomUserInfo struct { 42 | userID []byte 43 | 44 | netSessionIndex int32 45 | netSessionUniqueId uint64 46 | } 47 | -------------------------------------------------------------------------------- /chatServer_msgpack/roomPkg/roomManager.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "github.com/vmihailenco/msgpack/v4" 5 | 6 | "main/protocol" 7 | ) 8 | 9 | type RoomManager struct { 10 | _roomStartNum int32 11 | _maxRoomCount int32 12 | _roomCountList []int16 13 | _roomList []baseRoom 14 | } 15 | 16 | func NewRoomManager(config RoomConfig) *RoomManager { 17 | roomManager := new(RoomManager) 18 | roomManager._initialize(config) 19 | return roomManager 20 | } 21 | 22 | func (roomMgr *RoomManager) _initialize(config RoomConfig) { 23 | roomMgr._roomStartNum = config.StartRoomNumber 24 | roomMgr._maxRoomCount = config.MaxRoomCount 25 | roomMgr._roomCountList = make([]int16, config.MaxRoomCount) 26 | roomMgr._roomList = make([]baseRoom, config.MaxRoomCount) 27 | 28 | for i := int32(0); i < roomMgr._maxRoomCount; i++ { 29 | roomMgr._roomList[i].initialize(i, config) 30 | roomMgr._roomList[i].settingPacketFunction() 31 | } 32 | } 33 | 34 | func (roomMgr *RoomManager) GetAllChannelUserCount() []int16 { 35 | maxRoomCount := roomMgr._maxRoomCount 36 | for i := int32(0); i < maxRoomCount; i++ { 37 | roomMgr._roomCountList[i] = (int16)(roomMgr._getRoomUserCount(i)) 38 | } 39 | 40 | return roomMgr._roomCountList 41 | } 42 | 43 | func (roomMgr *RoomManager) getRoomByNumber(roomNumber int32) *baseRoom { 44 | roomIndex := roomNumber - roomMgr._roomStartNum 45 | 46 | if roomNumber < 0 || roomIndex >= roomMgr._maxRoomCount { 47 | return nil 48 | } 49 | 50 | return &roomMgr._roomList[roomIndex] 51 | } 52 | 53 | // 이 함수를 호출할 때의 채널 인덱스는 꼭 호출자가 유효범위인 것을 보증해야 한다. 54 | func (roomMgr *RoomManager) _getRoomUserCount(roomId int32) int32 { 55 | return roomMgr._roomList[roomId].getCurUserCount() 56 | } 57 | 58 | func (roomMgr *RoomManager) PacketProcess(roomNumber int32, packet protocol.Packet) { 59 | isRoomEnterReq := false 60 | 61 | if roomNumber == -1 && packet.Id == protocol.PACKET_ID_ROOM_ENTER_REQ { 62 | isRoomEnterReq = true 63 | 64 | var requestPacket protocol.RoomEnterReqPacket 65 | err := msgpack.Unmarshal(packet.Data, &requestPacket) 66 | if err != nil { 67 | return 68 | } 69 | 70 | roomNumber = int32(requestPacket.RoomNumber) 71 | } 72 | 73 | room := roomMgr.getRoomByNumber(roomNumber) 74 | if room == nil { 75 | protocol.NotifyErrorPacket(packet.UserSessionIndex, packet.UserSessionUniqueId, 76 | protocol.ERROR_CODE_ROOM_INVALIDE_NUMBER) 77 | return 78 | } 79 | 80 | user := room.getUser(packet.UserSessionUniqueId) 81 | if user == nil && isRoomEnterReq == false { 82 | protocol.NotifyErrorPacket(packet.UserSessionIndex, packet.UserSessionUniqueId, 83 | protocol.ERROR_CODE_ROOM_NOT_IN_USER) 84 | return 85 | } 86 | 87 | funcCount := len(room._funcPackeIdlist) 88 | for i := 0; i < funcCount; i++ { 89 | if room._funcPackeIdlist[i] != packet.Id { 90 | continue 91 | } 92 | 93 | room._funclist[i](user, packet) 94 | return 95 | } 96 | } 97 | 98 | -------------------------------------------------------------------------------- /chatServer_msgpack/roomPkg/room_Packet.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "main/protocol" 5 | ) 6 | 7 | func (room *baseRoom) _packetProcess_Relay(user *roomUser, packet protocol.Packet) int16 { 8 | /*var relayNotify protocol.RoomRelayNtfPacket 9 | relayNotify.RoomUserUniqueId = user.RoomUniqueId 10 | relayNotify.Data = packet.Data 11 | notifySendBuf, packetSize := relayNotify.EncodingPacket(packet.DataSize) 12 | room.broadcastPacket(packetSize, notifySendBuf, 0) 13 | 14 | NTELIB_LOG_DEBUG("Room Relay", zap.String("Sender", string(user.ID[:])))*/ 15 | return protocol.ERROR_CODE_NONE 16 | } 17 | -------------------------------------------------------------------------------- /chatServer_msgpack/roomPkg/room_PacketChat.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "github.com/vmihailenco/msgpack/v4" 5 | . "gohipernetFake" 6 | 7 | "main/protocol" 8 | ) 9 | 10 | func (room *baseRoom) _packetProcess_Chat(user *roomUser, packet protocol.Packet) int16 { 11 | sessionIndex := packet.UserSessionIndex 12 | sessionUniqueId := packet.UserSessionUniqueId 13 | 14 | var chatPacket protocol.RoomChatReqPacket 15 | err := msgpack.Unmarshal(packet.Data, &chatPacket) 16 | if err != nil { 17 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_PACKET_DECODING_FAIL) 18 | return protocol.ERROR_CODE_PACKET_DECODING_FAIL 19 | } 20 | 21 | var chatNotifyResponse protocol.RoomChatNtfPacket 22 | chatNotifyResponse.UserUniqueId = user.RoomUniqueId 23 | chatNotifyResponse.Msg = chatPacket.Msg 24 | bodyData, err := msgpack.Marshal(chatNotifyResponse) 25 | if err != nil { 26 | return protocol.ERROR_CODE_PACKET_ENCODING_FAIL 27 | } 28 | 29 | notifySendBuf := protocol.EncodingPacketHeaderInfo(bodyData, uint16(protocol.PACKET_ID_ROOM_CHAT_NOTIFY), 0) 30 | packetSize := uint16(len(notifySendBuf)) 31 | room.broadcastPacket(packetSize, notifySendBuf, 0) 32 | 33 | _sendRoomChatResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_NONE) 34 | 35 | return protocol.ERROR_CODE_NONE 36 | } 37 | 38 | func _sendRoomChatResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 39 | response := protocol.RoomChatResPacket{int64(result)} 40 | bodyData, err := msgpack.Marshal(response) 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | sendPacket := protocol.EncodingPacketHeaderInfo(bodyData, uint16(protocol.PACKET_ID_ROOM_CHAT_RES), 0) 46 | 47 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 48 | } 49 | -------------------------------------------------------------------------------- /chatServer_msgpack/roomPkg/room_PacketEnter.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "github.com/vmihailenco/msgpack/v4" 5 | "time" 6 | 7 | . "gohipernetFake" 8 | 9 | "main/connectedSessions" 10 | "main/protocol" 11 | ) 12 | 13 | func (room *baseRoom) _packetProcess_EnterUser(inValidUser *roomUser, packet protocol.Packet) int16 { 14 | curTime := time.Now().Unix() 15 | sessionIndex := packet.UserSessionIndex 16 | sessionUniqueId := packet.UserSessionUniqueId 17 | 18 | var requestPacket protocol.RoomEnterReqPacket 19 | err := msgpack.Unmarshal(packet.Data, &requestPacket) 20 | if err != nil { 21 | return protocol.ERROR_CODE_PACKET_DECODING_FAIL 22 | } 23 | 24 | userID, ok := connectedSessions.GetUserID(sessionIndex) 25 | if ok == false { 26 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, 0, protocol.ERROR_CODE_ENTER_ROOM_INVALID_USER_ID) 27 | return protocol.ERROR_CODE_ENTER_ROOM_INVALID_USER_ID 28 | } 29 | 30 | userInfo := addRoomUserInfo{ 31 | userID, 32 | sessionIndex, 33 | sessionUniqueId, 34 | } 35 | newUser, addResult := room.addUser(userInfo) 36 | 37 | if addResult != protocol.ERROR_CODE_NONE { 38 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, 0, addResult) 39 | return addResult 40 | } 41 | 42 | if connectedSessions.SetRoomNumber(sessionIndex, sessionUniqueId, room.getNumber(), curTime) == false { 43 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, 0, protocol.ERROR_CODE_ENTER_ROOM_INVALID_SESSION_STATE) 44 | return protocol.ERROR_CODE_ENTER_ROOM_INVALID_SESSION_STATE 45 | } 46 | 47 | if room.getCurUserCount() > 1 { 48 | //룸의 다른 유저에게 통보한다. 49 | room._sendNewUserInfoPacket(newUser) 50 | 51 | // 지금 들어온 유저에게 이미 채널에 있는 유저들의 정보를 보낸다 52 | room._sendUserInfoListPacket(newUser) 53 | } 54 | 55 | _sendRoomEnterResult(sessionIndex, sessionUniqueId, newUser.RoomUniqueId, protocol.ERROR_CODE_NONE) 56 | return protocol.ERROR_CODE_NONE 57 | } 58 | 59 | func _sendRoomEnterResult(sessionIndex int32, sessionUniqueId uint64, userUniqueId uint64, result int16) { 60 | response := protocol.RoomEnterResPacket{ 61 | int64(result), 62 | userUniqueId, 63 | } 64 | 65 | bodyData, err := msgpack.Marshal(response) 66 | if err != nil { 67 | return 68 | } 69 | 70 | sendPacket := protocol.EncodingPacketHeaderInfo(bodyData, uint16(protocol.PACKET_ID_ROOM_ENTER_RES), 0) 71 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 72 | } 73 | 74 | func (room *baseRoom) _sendUserInfoListPacket(user *roomUser) { 75 | userCount, uniqueIdList, idList := room.allocAllUserInfo(user.netSessionUniqueId) 76 | 77 | var response protocol.RoomUserListNtfPacket 78 | response.UserCount = int64(userCount) 79 | response.UniqueId = uniqueIdList 80 | response.ID = idList 81 | /*response.UniqueId = make([]uint64, 0, 0) 82 | response.ID = make([]string, 0, 0) 83 | for i := 0; i < 2; i++ { 84 | response.UniqueId = append(response.UniqueId, 1) 85 | response.ID = append(response.ID, "test1") 86 | }*/ 87 | 88 | bodyData, err := msgpack.Marshal(response) 89 | if err != nil { 90 | return 91 | } 92 | sendPacket := protocol.EncodingPacketHeaderInfo(bodyData, uint16(protocol.PACKET_ID_ROOM_USER_LIST_NTF), 0) 93 | 94 | NetLibIPostSendToClient(user.netSessionIndex, user.netSessionUniqueId, sendPacket) 95 | } 96 | 97 | func (room *baseRoom) _sendNewUserInfoPacket(user *roomUser) { 98 | var response protocol.RoomNewUserNtfPacket 99 | response.ID = string(user.ID[0:user.IDLen]) 100 | response.UniqueId = user.RoomUniqueId 101 | 102 | bodyData, err := msgpack.Marshal(response) 103 | if err != nil { 104 | panic(err) 105 | } 106 | 107 | sendPacket := protocol.EncodingPacketHeaderInfo(bodyData, uint16(protocol.PACKET_ID_ROOM_NEW_USER_NTF), 0) 108 | packetSize := uint16(len(sendPacket)) 109 | room.broadcastPacket(packetSize, sendPacket, user.netSessionUniqueId) // 자신을 제외하고 모든 유저에게 Send 110 | } 111 | -------------------------------------------------------------------------------- /chatServer_msgpack/roomPkg/room_PacketLeave.go: -------------------------------------------------------------------------------- 1 | package roomPkg 2 | 3 | import ( 4 | "github.com/vmihailenco/msgpack/v4" 5 | "time" 6 | 7 | . "gohipernetFake" 8 | 9 | "main/connectedSessions" 10 | "main/protocol" 11 | ) 12 | 13 | func (room *baseRoom) _packetProcess_LeaveUser(user *roomUser, packet protocol.Packet) int16 { 14 | room._leaveUserProcess(user) 15 | 16 | sessionIndex := packet.UserSessionIndex 17 | sessionUniqueId := packet.UserSessionUniqueId 18 | _sendRoomLeaveResult(sessionIndex, sessionUniqueId, protocol.ERROR_CODE_NONE) 19 | return protocol.ERROR_CODE_NONE 20 | } 21 | 22 | func (room *baseRoom) _leaveUserProcess(user *roomUser) { 23 | roomUserUniqueId := user.RoomUniqueId 24 | userSessionIndex := user.netSessionIndex 25 | userSessionUniqueId := user.netSessionUniqueId 26 | 27 | room._removeUser(user) 28 | 29 | room._sendRoomLeaveUserNotify(roomUserUniqueId, userSessionUniqueId) 30 | 31 | curTime := time.Now().Unix() 32 | connectedSessions.SetRoomNumber(userSessionIndex, userSessionUniqueId, -1, curTime) 33 | } 34 | 35 | func _sendRoomLeaveResult(sessionIndex int32, sessionUniqueId uint64, result int16) { 36 | response := protocol.RoomLeaveResPacket{int64(result)} 37 | bodyData, err := msgpack.Marshal(response) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | sendPacket := protocol.EncodingPacketHeaderInfo(bodyData, uint16(protocol.PACKET_ID_ROOM_LEAVE_RES), 0) 43 | NetLibIPostSendToClient(sessionIndex, sessionUniqueId, sendPacket) 44 | } 45 | 46 | func (room *baseRoom) _sendRoomLeaveUserNotify(roomUserUniqueId uint64, userSessionUniqueId uint64) { 47 | notifyPacket := protocol.RoomLeaveUserNtfPacket{roomUserUniqueId} 48 | 49 | bodyData, err := msgpack.Marshal(notifyPacket) 50 | if err != nil { 51 | return 52 | } 53 | 54 | sendPacket := protocol.EncodingPacketHeaderInfo(bodyData, uint16(protocol.PACKET_ID_ROOM_LEAVE_USER_NTF), 0) 55 | packetSize := uint16(len(sendPacket)) 56 | 57 | room.broadcastPacket(packetSize, sendPacket, userSessionUniqueId) 58 | } 59 | -------------------------------------------------------------------------------- /csharp_test_client/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /csharp_test_client/ClientSimpleTcp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | using System.Net; 4 | 5 | namespace csharp_test_client 6 | { 7 | public class ClientSimpleTcp 8 | { 9 | public Socket Sock = null; 10 | public string LatestErrorMsg; 11 | 12 | 13 | //소켓연결 14 | public bool Connect(string ip, int port) 15 | { 16 | try 17 | { 18 | IPAddress serverIP = IPAddress.Parse(ip); 19 | int serverPort = port; 20 | 21 | Sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 22 | Sock.Connect(new IPEndPoint(serverIP, serverPort)); 23 | 24 | if (Sock == null || Sock.Connected == false) 25 | { 26 | return false; 27 | } 28 | 29 | return true; 30 | } 31 | catch (Exception ex) 32 | { 33 | LatestErrorMsg = ex.Message; 34 | return false; 35 | } 36 | } 37 | 38 | public Tuple Receive() 39 | { 40 | 41 | try 42 | { 43 | byte[] ReadBuffer = new byte[2048]; 44 | var nRecv = Sock.Receive(ReadBuffer, 0, ReadBuffer.Length, SocketFlags.None); 45 | 46 | if (nRecv == 0) 47 | { 48 | return null; 49 | } 50 | 51 | return Tuple.Create(nRecv,ReadBuffer); 52 | } 53 | catch (SocketException se) 54 | { 55 | LatestErrorMsg = se.Message; 56 | } 57 | 58 | return null; 59 | } 60 | 61 | //스트림에 쓰기 62 | public void Send(byte[] sendData) 63 | { 64 | try 65 | { 66 | if (Sock != null && Sock.Connected) //연결상태 유무 확인 67 | { 68 | Sock.Send(sendData, 0, sendData.Length, SocketFlags.None); 69 | } 70 | else 71 | { 72 | LatestErrorMsg = "먼저 채팅서버에 접속하세요!"; 73 | } 74 | } 75 | catch (SocketException se) 76 | { 77 | LatestErrorMsg = se.Message; 78 | } 79 | } 80 | 81 | //소켓과 스트림 닫기 82 | public void Close() 83 | { 84 | if (Sock != null && Sock.Connected) 85 | { 86 | //Sock.Shutdown(SocketShutdown.Both); 87 | Sock.Close(); 88 | } 89 | } 90 | 91 | public bool IsConnected() { return (Sock != null && Sock.Connected) ? true : false; } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /csharp_test_client/DevLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using System.Runtime.CompilerServices; 8 | using System.Threading; 9 | 10 | namespace csharp_test_client 11 | { 12 | public class DevLog 13 | { 14 | static System.Collections.Concurrent.ConcurrentQueue logMsgQueue = new System.Collections.Concurrent.ConcurrentQueue(); 15 | 16 | static Int64 출력가능_로그레벨 = (Int64)LOG_LEVEL.TRACE; 17 | 18 | 19 | 20 | static public void Init(LOG_LEVEL logLevel) 21 | { 22 | ChangeLogLevel(logLevel); 23 | } 24 | 25 | static public void ChangeLogLevel(LOG_LEVEL logLevel) 26 | { 27 | Interlocked.Exchange(ref 출력가능_로그레벨, (int)logLevel); 28 | } 29 | 30 | public static LOG_LEVEL CurrentLogLevel() 31 | { 32 | var curLogLevel = (LOG_LEVEL)Interlocked.Read(ref 출력가능_로그레벨); 33 | return curLogLevel; 34 | } 35 | 36 | static public void Write(string msg, LOG_LEVEL logLevel = LOG_LEVEL.TRACE, 37 | [CallerFilePath] string fileName = "", 38 | [CallerMemberName] string methodName = "", 39 | [CallerLineNumber] int lineNumber = 0) 40 | { 41 | if (CurrentLogLevel() <= logLevel) 42 | { 43 | logMsgQueue.Enqueue(string.Format("{0}:{1}| {2}", DateTime.Now, methodName, msg)); 44 | } 45 | } 46 | 47 | static public bool GetLog(out string msg) 48 | { 49 | if (logMsgQueue.TryDequeue(out msg)) 50 | { 51 | return true; 52 | } 53 | 54 | return false; 55 | } 56 | 57 | } 58 | 59 | 60 | public enum LOG_LEVEL 61 | { 62 | TRACE, 63 | DEBUG, 64 | INFO, 65 | WARN, 66 | ERROR, 67 | DISABLE 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /csharp_test_client/PacketBufferManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace csharp_test_client 8 | { 9 | class PacketBufferManager 10 | { 11 | int BufferSize = 0; 12 | int ReadPos = 0; 13 | int WritePos = 0; 14 | 15 | int HeaderSize = 0; 16 | int MaxPacketSize = 0; 17 | byte[] PacketData; 18 | byte[] PacketDataTemp; 19 | 20 | public bool Init(int size, int headerSize, int maxPacketSize) 21 | { 22 | if (size < (maxPacketSize * 2) || size < 1 || headerSize < 1 || maxPacketSize < 1) 23 | { 24 | return false; 25 | } 26 | 27 | BufferSize = size; 28 | PacketData = new byte[size]; 29 | PacketDataTemp = new byte[size]; 30 | HeaderSize = headerSize; 31 | MaxPacketSize = maxPacketSize; 32 | 33 | return true; 34 | } 35 | 36 | public bool Write(byte[] data, int pos, int size) 37 | { 38 | if (data == null || (data.Length < (pos + size))) 39 | { 40 | return false; 41 | } 42 | 43 | var remainBufferSize = BufferSize - WritePos; 44 | 45 | if (remainBufferSize < size) 46 | { 47 | return false; 48 | } 49 | 50 | Buffer.BlockCopy(data, pos, PacketData, WritePos, size); 51 | WritePos += size; 52 | 53 | if (NextFree() == false) 54 | { 55 | BufferRelocate(); 56 | } 57 | return true; 58 | } 59 | 60 | public ArraySegment Read() 61 | { 62 | var enableReadSize = WritePos - ReadPos; 63 | 64 | if (enableReadSize < HeaderSize) 65 | { 66 | return new ArraySegment(); 67 | } 68 | 69 | var packetDataSize = BitConverter.ToInt16(PacketData, ReadPos); 70 | if (enableReadSize < packetDataSize) 71 | { 72 | return new ArraySegment(); 73 | } 74 | 75 | var completePacketData = new ArraySegment(PacketData, ReadPos, packetDataSize); 76 | ReadPos += packetDataSize; 77 | return completePacketData; 78 | } 79 | 80 | bool NextFree() 81 | { 82 | var enableWriteSize = BufferSize - WritePos; 83 | 84 | if (enableWriteSize < MaxPacketSize) 85 | { 86 | return false; 87 | } 88 | 89 | return true; 90 | } 91 | 92 | void BufferRelocate() 93 | { 94 | var enableReadSize = WritePos - ReadPos; 95 | 96 | Buffer.BlockCopy(PacketData, ReadPos, PacketDataTemp, 0, enableReadSize); 97 | Buffer.BlockCopy(PacketDataTemp, 0, PacketData, 0, enableReadSize); 98 | 99 | ReadPos = 0; 100 | WritePos = enableReadSize; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /csharp_test_client/PacketDefine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace csharp_test_client 8 | { 9 | class PacketDef 10 | { 11 | public const Int16 PACKET_HEADER_SIZE = 5; 12 | public const int MAX_USER_ID_BYTE_LENGTH = 16; 13 | public const int MAX_USER_PW_BYTE_LENGTH = 16; 14 | } 15 | 16 | public enum PACKET_ID : ushort 17 | { 18 | PACKET_ID_ECHO = 101, 19 | 20 | // Ping(Heart-beat) 21 | PACKET_ID_PING_REQ = 201, 22 | PACKET_ID_PING_RES = 202, 23 | 24 | PACKET_ID_ERROR_NTF = 203, 25 | 26 | 27 | // 로그인 28 | PACKET_ID_LOGIN_REQ = 701, 29 | PACKET_ID_LOGIN_RES = 702, 30 | 31 | 32 | PACKET_ID_ROOM_ENTER_REQ = 721, 33 | PACKET_ID_ROOM_ENTER_RES = 722, 34 | PACKET_ID_ROOM_USER_LIST_NTF = 723, 35 | PACKET_ID_ROOM_NEW_USER_NTF = 724, 36 | 37 | PACKET_ID_ROOM_LEAVE_REQ = 726, 38 | PACKET_ID_ROOM_LEAVE_RES = 727, 39 | PACKET_ID_ROOM_LEAVE_USER_NTF = 728, 40 | 41 | PACKET_ID_ROOM_CHAT_REQ = 731, 42 | PACKET_ID_ROOM_CHAT_RES = 732, 43 | PACKET_ID_ROOM_CHAT_NOTIFY = 733, 44 | 45 | PACKET_ID_ROOM_RELAY_REQ = 741, 46 | PACKET_ID_ROOM_RELAY_NTF = 742, 47 | } 48 | 49 | 50 | public enum ERROR_CODE : Int16 51 | { 52 | ERROR_NONE = 0, 53 | 54 | 55 | 56 | ERROR_CODE_USER_MGR_INVALID_USER_UNIQUEID = 112, 57 | 58 | ERROR_CODE_PUBLIC_CHANNEL_IN_USER = 114, 59 | 60 | ERROR_CODE_PUBLIC_CHANNEL_INVALIDE_NUMBER = 115, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /csharp_test_client/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Versioning; 5 | using System.Threading.Tasks; 6 | using System.Windows.Forms; 7 | 8 | namespace csharp_test_client 9 | { 10 | [SupportedOSPlatform("windows10.0.177630")] 11 | static class Program 12 | { 13 | /// 14 | /// 해당 응용 프로그램의 주 진입점입니다. 15 | /// 16 | [STAThread] 17 | static void Main() 18 | { 19 | Application.EnableVisualStyles(); 20 | Application.SetCompatibleTextRenderingDefault(false); 21 | Application.Run(new mainForm()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /csharp_test_client/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해 6 | // 제어됩니다. 어셈블리와 관련된 정보를 수정하려면 7 | // 이러한 특성 값을 변경하세요. 8 | [assembly: AssemblyTitle("csharp_test_client")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("csharp_test_client")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에 18 | // 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면 19 | // 해당 형식에 대해 ComVisible 특성을 true로 설정하세요. 20 | [assembly: ComVisible(false)] 21 | 22 | // 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다. 23 | [assembly: Guid("9e4b5e72-4e76-4e22-90b0-e53275a99018")] 24 | 25 | // 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다. 26 | // 27 | // 주 버전 28 | // 부 버전 29 | // 빌드 번호 30 | // 수정 버전 31 | // 32 | // 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로 33 | // 지정되도록 할 수 있습니다. 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /csharp_test_client/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 이 코드는 도구를 사용하여 생성되었습니다. 4 | // 런타임 버전:4.0.30319.42000 5 | // 6 | // 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 7 | // 이러한 변경 내용이 손실됩니다. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace csharp_test_client.Properties 12 | { 13 | 14 | 15 | /// 16 | /// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다. 17 | /// 18 | // 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder 19 | // 클래스에서 자동으로 생성되었습니다. 20 | // 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여 21 | // ResGen을 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("csharp_test_client.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대해 56 | /// 현재 스레드의 CurrentUICulture 속성을 재정의합니다. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /csharp_test_client/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace csharp_test_client.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /csharp_test_client/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /csharp_test_client/csharp_test_client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0-windows10.0.17763.0 4 | WinExe 5 | false 6 | true 7 | true 8 | 9 | 10 | ..\bin\ 11 | 12 | 13 | 14 | 15 | 16 | all 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /csharp_test_client/csharp_test_client.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp_test_client", "csharp_test_client.csproj", "{9E4B5E72-4E76-4E22-90B0-E53275A99018}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/ClientSimpleTcp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | using System.Net; 4 | 5 | namespace csharp_test_client 6 | { 7 | public class ClientSimpleTcp 8 | { 9 | public Socket Sock = null; 10 | public string LatestErrorMsg; 11 | 12 | 13 | //소켓연결 14 | public bool Connect(string ip, int port) 15 | { 16 | try 17 | { 18 | IPAddress serverIP = IPAddress.Parse(ip); 19 | int serverPort = port; 20 | 21 | Sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 22 | Sock.Connect(new IPEndPoint(serverIP, serverPort)); 23 | 24 | if (Sock == null || Sock.Connected == false) 25 | { 26 | return false; 27 | } 28 | 29 | return true; 30 | } 31 | catch (Exception ex) 32 | { 33 | LatestErrorMsg = ex.Message; 34 | return false; 35 | } 36 | } 37 | 38 | public Tuple Receive() 39 | { 40 | 41 | try 42 | { 43 | byte[] ReadBuffer = new byte[2048]; 44 | var nRecv = Sock.Receive(ReadBuffer, 0, ReadBuffer.Length, SocketFlags.None); 45 | 46 | if (nRecv == 0) 47 | { 48 | return null; 49 | } 50 | 51 | return Tuple.Create(nRecv,ReadBuffer); 52 | } 53 | catch (SocketException se) 54 | { 55 | LatestErrorMsg = se.Message; 56 | } 57 | 58 | return null; 59 | } 60 | 61 | //스트림에 쓰기 62 | public void Send(byte[] sendData) 63 | { 64 | try 65 | { 66 | if (Sock != null && Sock.Connected) //연결상태 유무 확인 67 | { 68 | Sock.Send(sendData, 0, sendData.Length, SocketFlags.None); 69 | } 70 | else 71 | { 72 | LatestErrorMsg = "먼저 채팅서버에 접속하세요!"; 73 | } 74 | } 75 | catch (SocketException se) 76 | { 77 | LatestErrorMsg = se.Message; 78 | } 79 | } 80 | 81 | //소켓과 스트림 닫기 82 | public void Close() 83 | { 84 | if (Sock != null && Sock.Connected) 85 | { 86 | //Sock.Shutdown(SocketShutdown.Both); 87 | Sock.Close(); 88 | } 89 | } 90 | 91 | public bool IsConnected() { return (Sock != null && Sock.Connected) ? true : false; } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/DevLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using System.Runtime.CompilerServices; 8 | using System.Threading; 9 | 10 | namespace csharp_test_client 11 | { 12 | public class DevLog 13 | { 14 | static System.Collections.Concurrent.ConcurrentQueue logMsgQueue = new System.Collections.Concurrent.ConcurrentQueue(); 15 | 16 | static Int64 출력가능_로그레벨 = (Int64)LOG_LEVEL.TRACE; 17 | 18 | 19 | 20 | static public void Init(LOG_LEVEL logLevel) 21 | { 22 | ChangeLogLevel(logLevel); 23 | } 24 | 25 | static public void ChangeLogLevel(LOG_LEVEL logLevel) 26 | { 27 | Interlocked.Exchange(ref 출력가능_로그레벨, (int)logLevel); 28 | } 29 | 30 | public static LOG_LEVEL CurrentLogLevel() 31 | { 32 | var curLogLevel = (LOG_LEVEL)Interlocked.Read(ref 출력가능_로그레벨); 33 | return curLogLevel; 34 | } 35 | 36 | static public void Write(string msg, LOG_LEVEL logLevel = LOG_LEVEL.TRACE, 37 | [CallerFilePath] string fileName = "", 38 | [CallerMemberName] string methodName = "", 39 | [CallerLineNumber] int lineNumber = 0) 40 | { 41 | if (CurrentLogLevel() <= logLevel) 42 | { 43 | logMsgQueue.Enqueue(string.Format("{0}:{1}| {2}", DateTime.Now, methodName, msg)); 44 | } 45 | } 46 | 47 | static public bool GetLog(out string msg) 48 | { 49 | if (logMsgQueue.TryDequeue(out msg)) 50 | { 51 | return true; 52 | } 53 | 54 | return false; 55 | } 56 | 57 | } 58 | 59 | 60 | public enum LOG_LEVEL 61 | { 62 | TRACE, 63 | DEBUG, 64 | INFO, 65 | WARN, 66 | ERROR, 67 | DISABLE 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/PacketBufferManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace csharp_test_client 8 | { 9 | class PacketBufferManager 10 | { 11 | int BufferSize = 0; 12 | int ReadPos = 0; 13 | int WritePos = 0; 14 | 15 | int HeaderSize = 0; 16 | int MaxPacketSize = 0; 17 | byte[] PacketData; 18 | byte[] PacketDataTemp; 19 | 20 | public bool Init(int size, int headerSize, int maxPacketSize) 21 | { 22 | if (size < (maxPacketSize * 2) || size < 1 || headerSize < 1 || maxPacketSize < 1) 23 | { 24 | return false; 25 | } 26 | 27 | BufferSize = size; 28 | PacketData = new byte[size]; 29 | PacketDataTemp = new byte[size]; 30 | HeaderSize = headerSize; 31 | MaxPacketSize = maxPacketSize; 32 | 33 | return true; 34 | } 35 | 36 | public bool Write(byte[] data, int pos, int size) 37 | { 38 | if (data == null || (data.Length < (pos + size))) 39 | { 40 | return false; 41 | } 42 | 43 | var remainBufferSize = BufferSize - WritePos; 44 | 45 | if (remainBufferSize < size) 46 | { 47 | return false; 48 | } 49 | 50 | Buffer.BlockCopy(data, pos, PacketData, WritePos, size); 51 | WritePos += size; 52 | 53 | if (NextFree() == false) 54 | { 55 | BufferRelocate(); 56 | } 57 | return true; 58 | } 59 | 60 | public ArraySegment Read() 61 | { 62 | var enableReadSize = WritePos - ReadPos; 63 | 64 | if (enableReadSize < HeaderSize) 65 | { 66 | return new ArraySegment(); 67 | } 68 | 69 | var packetDataSize = BitConverter.ToInt16(PacketData, ReadPos); 70 | if (enableReadSize < packetDataSize) 71 | { 72 | return new ArraySegment(); 73 | } 74 | 75 | var completePacketData = new ArraySegment(PacketData, ReadPos, packetDataSize); 76 | ReadPos += packetDataSize; 77 | return completePacketData; 78 | } 79 | 80 | bool NextFree() 81 | { 82 | var enableWriteSize = BufferSize - WritePos; 83 | 84 | if (enableWriteSize < MaxPacketSize) 85 | { 86 | return false; 87 | } 88 | 89 | return true; 90 | } 91 | 92 | void BufferRelocate() 93 | { 94 | var enableReadSize = WritePos - ReadPos; 95 | 96 | Buffer.BlockCopy(PacketData, ReadPos, PacketDataTemp, 0, enableReadSize); 97 | Buffer.BlockCopy(PacketDataTemp, 0, PacketData, 0, enableReadSize); 98 | 99 | ReadPos = 0; 100 | WritePos = enableReadSize; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/PacketDefine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace csharp_test_client 8 | { 9 | class PacketDef 10 | { 11 | public const Int16 PACKET_HEADER_SIZE = 5; 12 | public const int MAX_USER_ID_BYTE_LENGTH = 16; 13 | public const int MAX_USER_PW_BYTE_LENGTH = 16; 14 | } 15 | 16 | public enum PACKET_ID : ushort 17 | { 18 | PACKET_ID_ECHO = 101, 19 | 20 | // Ping(Heart-beat) 21 | PACKET_ID_PING_REQ = 201, 22 | PACKET_ID_PING_RES = 202, 23 | 24 | PACKET_ID_ERROR_NTF = 203, 25 | 26 | 27 | // 로그인 28 | PACKET_ID_LOGIN_REQ = 701, 29 | PACKET_ID_LOGIN_RES = 702, 30 | 31 | 32 | PACKET_ID_ROOM_ENTER_REQ = 721, 33 | PACKET_ID_ROOM_ENTER_RES = 722, 34 | PACKET_ID_ROOM_USER_LIST_NTF = 723, 35 | PACKET_ID_ROOM_NEW_USER_NTF = 724, 36 | 37 | PACKET_ID_ROOM_LEAVE_REQ = 726, 38 | PACKET_ID_ROOM_LEAVE_RES = 727, 39 | PACKET_ID_ROOM_LEAVE_USER_NTF = 728, 40 | 41 | PACKET_ID_ROOM_CHAT_REQ = 731, 42 | PACKET_ID_ROOM_CHAT_RES = 732, 43 | PACKET_ID_ROOM_CHAT_NOTIFY = 733, 44 | 45 | PACKET_ID_ROOM_RELAY_REQ = 741, 46 | PACKET_ID_ROOM_RELAY_NTF = 742, 47 | } 48 | 49 | 50 | public enum ERROR_CODE : Int16 51 | { 52 | ERROR_NONE = 0, 53 | 54 | 55 | 56 | ERROR_CODE_USER_MGR_INVALID_USER_UNIQUEID = 112, 57 | 58 | ERROR_CODE_PUBLIC_CHANNEL_IN_USER = 114, 59 | 60 | ERROR_CODE_PUBLIC_CHANNEL_INVALIDE_NUMBER = 115, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace csharp_test_client 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// 해당 응용 프로그램의 주 진입점입니다. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new mainForm()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해 6 | // 제어됩니다. 어셈블리와 관련된 정보를 수정하려면 7 | // 이러한 특성 값을 변경하세요. 8 | [assembly: AssemblyTitle("csharp_test_client")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("csharp_test_client")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에 18 | // 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면 19 | // 해당 형식에 대해 ComVisible 특성을 true로 설정하세요. 20 | [assembly: ComVisible(false)] 21 | 22 | // 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다. 23 | [assembly: Guid("9e4b5e72-4e76-4e22-90b0-e53275a99018")] 24 | 25 | // 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다. 26 | // 27 | // 주 버전 28 | // 부 버전 29 | // 빌드 번호 30 | // 수정 버전 31 | // 32 | // 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로 33 | // 지정되도록 할 수 있습니다. 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 이 코드는 도구를 사용하여 생성되었습니다. 4 | // 런타임 버전:4.0.30319.42000 5 | // 6 | // 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 7 | // 이러한 변경 내용이 손실됩니다. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace csharp_test_client.Properties 12 | { 13 | 14 | 15 | /// 16 | /// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다. 17 | /// 18 | // 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder 19 | // 클래스에서 자동으로 생성되었습니다. 20 | // 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여 21 | // ResGen을 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("csharp_test_client.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대해 56 | /// 현재 스레드의 CurrentUICulture 속성을 재정의합니다. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace csharp_test_client.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/csharp_test_client_msgpack.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0-windows 4 | WinExe 5 | csharp_test_client 6 | false 7 | true 8 | true 9 | 10 | 11 | ..\bin\ 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /csharp_test_client_msgpack/csharp_test_client_msgpack.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29519.87 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp_test_client_msgpack", "csharp_test_client_msgpack.csproj", "{9E4B5E72-4E76-4E22-90B0-E53275A99018}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleMsgPack", "..\thirdparty\SimpleMsgPack.Net\Source\SimpleMsgPack.csproj", "{53CED7AF-BCF5-453D-AC00-36247752EBA8}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {9E4B5E72-4E76-4E22-90B0-E53275A99018}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {53CED7AF-BCF5-453D-AC00-36247752EBA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {53CED7AF-BCF5-453D-AC00-36247752EBA8}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {53CED7AF-BCF5-453D-AC00-36247752EBA8}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {53CED7AF-BCF5-453D-AC00-36247752EBA8}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {617A86AA-2EF2-432C-8980-B03FF79E7A5A} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /echoServer/echoServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | . "gohipernetFake" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | 11 | type EchoServer struct { 12 | ServerIndex int 13 | IP string 14 | Port int 15 | } 16 | 17 | func createServer(netConfig NetworkConfig) { 18 | OutPutLog(LOG_LEVEL_INFO,"", 0,"CreateServer !!!") 19 | 20 | var server EchoServer 21 | 22 | if server.setIPAddress(netConfig.BindAddress) == false { 23 | OutPutLog(LOG_LEVEL_ERROR,"", 0,"fail. server address") 24 | return 25 | } 26 | 27 | 28 | networkFunctor := SessionNetworkFunctors{} 29 | networkFunctor.OnConnect = server.OnConnect 30 | networkFunctor.OnReceive = server.OnReceive 31 | networkFunctor.OnReceiveBufferedData = nil 32 | networkFunctor.OnClose = server.OnClose 33 | networkFunctor.PacketTotalSizeFunc = PacketTotalSize 34 | networkFunctor.PacketHeaderSize = PACKET_HEADER_SIZE 35 | networkFunctor.IsClientSession = true 36 | 37 | NetLibStartNetwork(&netConfig, networkFunctor) 38 | } 39 | 40 | func (server *EchoServer) setIPAddress(ipAddress string) bool { 41 | results := strings.Split(ipAddress, ":") 42 | if len(results) != 2 { 43 | return false 44 | } 45 | 46 | server.IP = results[0] 47 | server.Port, _ = strconv.Atoi(results[1]) 48 | 49 | return true 50 | } 51 | 52 | func (server *EchoServer) OnConnect(sessionIndex int32, sessionUniqueID uint64) { 53 | OutPutLog(LOG_LEVEL_INFO,"", 0,fmt.Sprintf("[OnConnect] sessionIndex: %d", sessionIndex)) 54 | } 55 | 56 | func (server *EchoServer) OnReceive(sessionIndex int32, sessionUniqueID uint64, data []byte) bool { 57 | NetLibISendToClient(sessionIndex, sessionUniqueID, data) 58 | return true 59 | } 60 | 61 | func (server *EchoServer) OnClose(sessionIndex int32, sessionUniqueID uint64) { 62 | OutPutLog(LOG_LEVEL_INFO,"", 0,fmt.Sprintf("[OnClose] sessionIndex: %d", sessionIndex)) 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /echoServer/go.mod: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | go.uber.org/atomic v1.4.0 // indirect 8 | go.uber.org/multierr v1.1.0 // indirect 9 | go.uber.org/zap v1.10.0 10 | 11 | gohipernetFake v0.0.0 12 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect 13 | ) 14 | 15 | replace gohipernetFake v0.0.0 => ../gohipernetFake 16 | -------------------------------------------------------------------------------- /echoServer/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= 4 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 5 | go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= 6 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 7 | go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= 8 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 9 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 10 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 11 | -------------------------------------------------------------------------------- /echoServer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | . "gohipernetFake" 6 | ) 7 | 8 | 9 | func main() { 10 | NetLibInitLog(LOG_LEVEL_DEBUG, nil) 11 | 12 | netConfigClient := parseAppConfig() 13 | netConfigClient.WriteNetworkConfig(true) 14 | 15 | // 아래 함수를 호출하면 강제적으로 종료 시킬 때까지 대기 상태가 된다. 16 | createServer(netConfigClient) 17 | } 18 | 19 | func parseAppConfig() NetworkConfig { 20 | client := NetworkConfig{} 21 | 22 | flag.BoolVar(&client.IsTcp4Addr,"c_IsTcp4Addr", true, "bool flag") 23 | flag.StringVar(&client.BindAddress,"c_BindAddress", "127.0.0.1:11021", "string flag") 24 | flag.IntVar(&client.MaxSessionCount,"c_MaxSessionCount", 0, "int flag") 25 | flag.IntVar(&client.MaxPacketSize,"c_MaxPacketSize", 0, "int flag") 26 | 27 | flag.Parse() 28 | return client 29 | } 30 | 31 | -------------------------------------------------------------------------------- /gohipernetFake/TcpSession.go: -------------------------------------------------------------------------------- 1 | package gohipernetFake 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | 8 | 9 | type TcpSession struct { 10 | Index int32 11 | SeqIndex uint64 12 | TcpConn net.Conn 13 | NetworkFunctor SessionNetworkFunctors 14 | } 15 | 16 | func (session *TcpSession) handleTcpRead(networkFunctor SessionNetworkFunctors) { 17 | session.NetworkFunctor.OnConnect(session.Index, session.SeqIndex) 18 | 19 | 20 | var startRecvPos int16 21 | var result int 22 | recviveBuff := make([]byte, MAX_RECEIVE_BUFFER_SIZE) 23 | 24 | for { 25 | recvBytes, err := session.TcpConn.Read(recviveBuff[startRecvPos:]) 26 | if err != nil { 27 | //TODO 끊는 이유 남기기 28 | session.closeProcess() 29 | return 30 | } 31 | 32 | if recvBytes < PACKET_HEADER_SIZE { 33 | //TODO 끊는 이유 남기기 34 | session.closeProcess() 35 | return 36 | } 37 | 38 | readAbleByte := int16(startRecvPos) + int16(recvBytes) 39 | startRecvPos, result = session.makePacket(readAbleByte, recviveBuff) 40 | if result != NET_ERROR_NONE { 41 | //TODO 끊는 이유 남기기 42 | session.closeProcess() 43 | return 44 | } 45 | 46 | } 47 | } 48 | 49 | func (session *TcpSession) makePacket(readAbleByte int16, recviveBuff []byte) (int16, int) { 50 | sessionIndex := session.Index 51 | sessionUnique := session.SeqIndex 52 | 53 | PacketHeaderSize := session.NetworkFunctor.PacketHeaderSize 54 | PacketTotalSizeFunc := session.NetworkFunctor.PacketTotalSizeFunc 55 | var startRecvPos int16 = 0 56 | var readPos int16 57 | 58 | for { 59 | if readAbleByte < PacketHeaderSize { 60 | break 61 | } 62 | 63 | requireDataSize := PacketTotalSizeFunc(recviveBuff[readPos:]) 64 | 65 | if requireDataSize > readAbleByte { 66 | break 67 | } 68 | 69 | if requireDataSize > MAX_PACKET_SIZE { 70 | return startRecvPos, NET_ERROR_RECV_MAKE_PACKET_TOO_LARGE_PACKET_SIZE 71 | } 72 | 73 | ltvPacket := recviveBuff[readPos:(readPos + requireDataSize)] 74 | readPos += requireDataSize 75 | readAbleByte -= requireDataSize 76 | 77 | 78 | session.NetworkFunctor.OnReceive(sessionIndex, sessionUnique, ltvPacket) 79 | } 80 | 81 | 82 | if readAbleByte > 0 { 83 | copy(recviveBuff, recviveBuff[readPos:(readPos+readAbleByte)]) 84 | } 85 | 86 | startRecvPos = readAbleByte 87 | return startRecvPos, NET_ERROR_NONE 88 | } 89 | 90 | func (session *TcpSession) closeProcess() { 91 | session.TcpConn.Close() 92 | session.NetworkFunctor.OnClose(session.Index, session.SeqIndex) 93 | 94 | _tcpSessionManager.removeSession(session.Index, session.SeqIndex) 95 | } 96 | 97 | // Send bytes to client 98 | func (session *TcpSession) sendPacket(b []byte) error { 99 | _, err := session.TcpConn.Write(b) 100 | return err 101 | } 102 | 103 | func (session *TcpSession) close() error { 104 | return session.TcpConn.Close() 105 | } -------------------------------------------------------------------------------- /gohipernetFake/clientSessionManager.go: -------------------------------------------------------------------------------- 1 | package gohipernetFake 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | ) 7 | 8 | type tcpClientSessionManager struct { 9 | _networkFunctor SessionNetworkFunctors 10 | 11 | _sessionMap sync.Map 12 | _curSessionCount int32 // 멀티스레드에서 호출된다 13 | 14 | sessionIndexPool *Deque 15 | } 16 | 17 | func newClientSessionManager(config *NetworkConfig, 18 | networkFunctor SessionNetworkFunctors) *tcpClientSessionManager { 19 | sessionMgr := new(tcpClientSessionManager) 20 | sessionMgr._networkFunctor = networkFunctor 21 | sessionMgr._sessionMap = sync.Map{} 22 | 23 | sessionMgr._createSessionIndexPool(config.MaxSessionCount) 24 | 25 | return sessionMgr 26 | } 27 | 28 | func (sessionMgr *tcpClientSessionManager) _createSessionIndexPool(poolSize int) { 29 | sessionMgr.sessionIndexPool = NewCappedDeque(poolSize) 30 | 31 | for i := 0; i < poolSize; i++ { 32 | sessionMgr.sessionIndexPool.Append(int32(i)) 33 | } 34 | } 35 | 36 | func (sessionMgr *tcpClientSessionManager) _allocSessionIndex() int32 { 37 | index := sessionMgr.sessionIndexPool.Shift() 38 | 39 | if index == nil { 40 | return -1 41 | } 42 | 43 | return index.(int32) 44 | } 45 | 46 | func (sessionMgr *tcpClientSessionManager) _freeSessionIndex(sessionIndex int32) { 47 | sessionMgr.sessionIndexPool.Append(sessionIndex) 48 | } 49 | 50 | func (sessionMgr *tcpClientSessionManager) addSession(session *TcpSession) bool { 51 | sessionUniqueId := session.SeqIndex 52 | sessionIndex := sessionMgr._allocSessionIndex() 53 | 54 | if sessionIndex == -1 { 55 | return false 56 | } 57 | 58 | _, result := sessionMgr._findSession(sessionIndex, sessionUniqueId) 59 | if result { 60 | return false 61 | } 62 | 63 | session.Index = sessionIndex 64 | sessionMgr._sessionMap.Store(sessionUniqueId, session) 65 | return true 66 | } 67 | 68 | func (sessionMgr *tcpClientSessionManager) removeSession(sessionIndex int32, sessionUniqueId uint64) { 69 | sessionMgr._freeSessionIndex(sessionIndex) 70 | sessionMgr._sessionMap.Delete(sessionUniqueId) 71 | } 72 | 73 | func (sessionMgr *tcpClientSessionManager) sendPacket(sessionIndex int32, 74 | sessionUniqueId uint64, 75 | sendData []byte) bool { 76 | session, result := sessionMgr._findSession(sessionIndex, sessionUniqueId) 77 | if result == false { 78 | return false 79 | } 80 | 81 | session.sendPacket(sendData) 82 | return true 83 | } 84 | 85 | func (sessionMgr *tcpClientSessionManager) sendPacketAllClient(sendData []byte) { 86 | sessionMgr._sessionMap.Range(func(_, value interface{}) bool { 87 | value.(*TcpSession).sendPacket(sendData) 88 | return true 89 | }) 90 | } 91 | 92 | func (sessionMgr *tcpClientSessionManager) _connectedessionCount() int32 { 93 | count := atomic.LoadInt32(&sessionMgr._curSessionCount) 94 | return count 95 | } 96 | 97 | func (sessionMgr *tcpClientSessionManager) _IncConnectedessionCount() { 98 | atomic.AddInt32(&sessionMgr._curSessionCount, 1) 99 | } 100 | 101 | func (sessionMgr *tcpClientSessionManager) _DecConnectedessionCount() { 102 | atomic.AddInt32(&sessionMgr._curSessionCount, -1) 103 | } 104 | 105 | func (sessionMgr *tcpClientSessionManager) _findSession(sessionIndex int32, 106 | sessionUniqueId uint64, 107 | ) (*TcpSession, bool) { 108 | if session, ok := sessionMgr._sessionMap.Load(sessionUniqueId); ok { 109 | return session.(*TcpSession), true 110 | } 111 | 112 | return nil, false 113 | } 114 | 115 | func (sessionMgr *tcpClientSessionManager) forceDisconnectClient(sessionIndex int32, 116 | sessionUniqueId uint64) bool { 117 | 118 | session, resut := sessionMgr._findSession(sessionIndex, sessionUniqueId) 119 | if resut == false { 120 | return false 121 | } 122 | 123 | session.closeProcess() 124 | return true 125 | } 126 | 127 | func (sessionMgr *tcpClientSessionManager) _forceCloseAllSession() { 128 | sessionMgr._sessionMap.Range(func(_, value interface{}) bool { 129 | value.(*TcpSession).closeProcess() 130 | return true 131 | }) 132 | } 133 | -------------------------------------------------------------------------------- /gohipernetFake/configNetwork.go: -------------------------------------------------------------------------------- 1 | package gohipernetFake 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | 8 | type NetworkConfig struct { 9 | IsTcp4Addr bool 10 | BindAddress string // 예) localhost:19999 11 | MaxSessionCount int // 최대 클라이언트 세션 수. 넉넉하게 많이 해도 괜찮다 12 | MaxPacketSize int // 최대 패킷 크기 13 | MaxReceiveBufferSize int // 받기 버퍼 크기. 최소 MaxPacketSize 2배 이상 추천. 14 | 15 | } 16 | 17 | func (config NetworkConfig) WriteNetworkConfig(isClientSide bool) { 18 | logInfo("", 0, fmt.Sprintf("config - isClientSide: %t", isClientSide)) 19 | logInfo("", 0, fmt.Sprintf("config - IsTcp4Addr: %t", config.IsTcp4Addr)) 20 | logInfo("", 0, fmt.Sprintf("config - ClientAddress: %s", config.BindAddress)) 21 | logInfo("", 0, fmt.Sprintf("config - MaxSessionCount: %d", config.MaxSessionCount)) 22 | logInfo("", 0, fmt.Sprintf("config - MaxPacketSize: %d", config.MaxPacketSize)) 23 | logInfo("", 0, fmt.Sprintf("config - MaxReceiveBufferSize: %d", config.MaxReceiveBufferSize)) 24 | } 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /gohipernetFake/define.go: -------------------------------------------------------------------------------- 1 | package gohipernetFake 2 | 3 | 4 | const ( 5 | MAX_RECEIVE_BUFFER_SIZE = 8012 6 | PACKET_HEADER_SIZE = 5 7 | MAX_PACKET_SIZE = 1024 8 | ) 9 | 10 | const ( 11 | NET_ERROR_NONE = 0 12 | NET_ERROR_RECV_MAKE_PACKET_TOO_LARGE_PACKET_SIZE = 1 13 | 14 | ) 15 | const ( 16 | NET_CLOSE_REMOTE = 1 17 | NET_CLOSE_RECV_TOO_SMALL_RECV_DATA = 2 18 | ) 19 | 20 | 21 | 22 | type SessionNetworkFunctors struct { 23 | OnConnect func(int32, uint64) 24 | 25 | OnClose func(int32, uint64) 26 | 27 | // 데이터 도착 이벤트 28 | OnReceive func(int32, uint64, []byte) bool 29 | 30 | // 데이터 도착 이벤트. []byte가 링버퍼에 저장되어 있다 31 | OnReceiveBufferedData func(int32, uint64, []byte) bool 32 | 33 | 34 | // 데이터를 분석하여 패킷 크기를 반환한다. 35 | PacketTotalSizeFunc func([]byte) int16 36 | 37 | // 패킷 헤더의 크기 38 | PacketHeaderSize int16 39 | 40 | // true 이면 client와 연결한 세션이다. 41 | IsClientSession bool 42 | } -------------------------------------------------------------------------------- /gohipernetFake/go.mod: -------------------------------------------------------------------------------- 1 | module gohipernetFake 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 7 | ) 8 | -------------------------------------------------------------------------------- /gohipernetFake/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 5 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 9 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 10 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 11 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 12 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 13 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 14 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 15 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 16 | go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= 17 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 20 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 21 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 22 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 24 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | -------------------------------------------------------------------------------- /gohipernetFake/goHiperNet.go: -------------------------------------------------------------------------------- 1 | // 애플리케이션에서 네트워크 라이브러리에 접근할 함수는 모두 여기에만 정의한다. 2 | package gohipernetFake 3 | 4 | 5 | 6 | func NetLibInitLog(loglevel int, logFunc func(int, string, uint64, string)) { 7 | _logLevel = loglevel 8 | 9 | if logFunc != nil { 10 | OutPutLog = logFunc 11 | } 12 | } 13 | 14 | // 네트워크 시작 15 | func NetLibStartNetwork(clientConfig *NetworkConfig, networkFunctor SessionNetworkFunctors) { 16 | start_Network_Impl(clientConfig, networkFunctor) 17 | } 18 | 19 | func NetLibStopListen() { 20 | stopListen_impl() 21 | } 22 | 23 | // 특정 클라이언트에게 데이터를 보낸다 24 | var NetLibISendToClient func(int32, uint64, []byte) bool 25 | // 접속된 모든 클라이언트에게 데이터를 보낸다. 26 | var NetLibISendToAllClient func([]byte) 27 | 28 | var NetLibIPostSendToAllClient func([]byte) 29 | var NetLibIPostSendToClient func(int32, uint64, []byte) bool 30 | 31 | 32 | // 클라이언트 접속을 강제로 짜른다. 33 | func NetLibForceDisconnectClient(sessionIndex int32, sessionUnqiueID uint64) { 34 | _tcpSessionManager.forceDisconnectClient(sessionIndex, sessionUnqiueID) 35 | } -------------------------------------------------------------------------------- /gohipernetFake/goHiperNet_Impl.go: -------------------------------------------------------------------------------- 1 | package gohipernetFake 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "sync/atomic" 7 | ) 8 | 9 | 10 | func start_Network_Impl(clientConfig *NetworkConfig, networkFunctor SessionNetworkFunctors) { 11 | defer PrintPanicStack() 12 | 13 | // 아래 함수가 호출되면 무한 대기에 들어간다 14 | _tcpSessionManager = newClientSessionManager(clientConfig, networkFunctor) 15 | _start_TCPServer_block(clientConfig, networkFunctor) 16 | } 17 | 18 | func stopListen_impl() { 19 | _ = _mClientListener.Close() 20 | } 21 | 22 | func _start_TCPServer_block(config *NetworkConfig, networkFunctor SessionNetworkFunctors) { 23 | defer PrintPanicStack() 24 | logInfo("", 0, "tcpServerStart - Start") 25 | 26 | var err error 27 | tcpAddr, _ := net.ResolveTCPAddr("tcp", config.BindAddress) 28 | _mClientListener, err = net.ListenTCP("tcp", tcpAddr) 29 | 30 | if err != nil { 31 | log.Fatal("Error starting TCP server.") 32 | } 33 | defer _mClientListener.Close() 34 | 35 | log.Println("Server Listen ...") 36 | 37 | for { 38 | conn, _ := _mClientListener.Accept() 39 | client := &TcpSession{ 40 | SeqIndex: SeqNumIncrement(), 41 | TcpConn: conn, 42 | NetworkFunctor: networkFunctor, 43 | } 44 | 45 | _tcpSessionManager.addSession(client) 46 | 47 | go client.handleTcpRead(networkFunctor) 48 | } 49 | 50 | logInfo("", 0, "tcpServerStart - End") 51 | } 52 | 53 | // 보내기 함수(선언만 있는. 일종의 인터페이스)에 실제 동작함수를 연결한다 54 | func _InitNetworkSendFunction() { 55 | NetLibISendToClient = sendToClient 56 | NetLibISendToAllClient = sendToAllClient 57 | NetLibIPostSendToAllClient = postSendToAllClient 58 | NetLibIPostSendToClient = postSendToClient 59 | 60 | logInfo("", 0, "call _InitNetworkSendFunction") 61 | } 62 | 63 | func sendToClient(sessionIndex int32, sessionUniqueID uint64, data []byte) bool { 64 | result := _tcpSessionManager.sendPacket(sessionIndex, sessionUniqueID, data) 65 | return result 66 | } 67 | 68 | func sendToAllClient(sendData []byte) { 69 | _tcpSessionManager.sendPacketAllClient(sendData) 70 | } 71 | 72 | func postSendToClient(sessionIndex int32, sessionUniqueID uint64, data []byte) bool { 73 | return sendToClient(sessionIndex, sessionUniqueID, data) 74 | } 75 | 76 | func postSendToAllClient(sendData []byte) { 77 | _tcpSessionManager.sendPacketAllClient(sendData) 78 | } 79 | 80 | func sendPacketToServer(sessionIndex int32, data []byte) bool { 81 | return false 82 | } 83 | 84 | func postSendPacketToServer(sessionIndex int32, data []byte) bool { 85 | return false 86 | } 87 | 88 | 89 | 90 | var _seqNumber uint64 // 절대 이것을 바로 사용하면 안 된다!!! 91 | 92 | func SeqNumIncrement() uint64 { 93 | newValue := atomic.AddUint64(&_seqNumber, 1) 94 | return newValue 95 | } 96 | 97 | var _tcpSessionManager *tcpClientSessionManager 98 | var _mClientListener *net.TCPListener 99 | -------------------------------------------------------------------------------- /gohipernetFake/log.go: -------------------------------------------------------------------------------- 1 | package gohipernetFake 2 | 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | const ( 10 | LOG_LEVEL_TRACE = 0 11 | LOG_LEVEL_DEBUG = 1 12 | LOG_LEVEL_INFO = 2 13 | LOG_LEVEL_WARN = 3 14 | LOG_LEVEL_ERROR = 4 15 | LOG_LEVEL_FATAL = 5 16 | ) 17 | 18 | var logLevelStr = [6]string{"trace", "debug", "info", "warn", "error", "fatal"} 19 | 20 | var ( 21 | OutPutLog = _emptyExportLog 22 | ) 23 | 24 | func logTrace(userID string, sessionUID uint64, msg string) { 25 | OutPutLog(LOG_LEVEL_TRACE, userID, sessionUID, msg) 26 | } 27 | func logDebug(userID string, sessionUID uint64, msg string) { 28 | OutPutLog(LOG_LEVEL_DEBUG, userID, sessionUID, msg) 29 | } 30 | func logInfo(userID string, sessionUID uint64, msg string) { 31 | OutPutLog(LOG_LEVEL_INFO, userID, sessionUID, msg) 32 | } 33 | func logError(userID string, sessionUID uint64, msg string) { 34 | OutPutLog(LOG_LEVEL_ERROR, userID, sessionUID, msg) 35 | } 36 | 37 | // 비공개 함수 38 | func _emptyExportLog(level int, userID string, sessionUID uint64, msg string) { 39 | if level < _logLevel { 40 | return 41 | } 42 | 43 | fmt.Fprintf(os.Stdout, "[ %s ] %s\n", logLevelStr[level], msg) 44 | } 45 | 46 | var _logLevel int 47 | -------------------------------------------------------------------------------- /gohipernetFake/utilDeque.go: -------------------------------------------------------------------------------- 1 | package gohipernetFake 2 | 3 | import ( 4 | "container/list" 5 | "sync" 6 | ) 7 | 8 | //이 라이브러리의 출처(https://github.com/oleiade/lane) 9 | // Deque is a head-tail linked list data structure implementation. 10 | // It is based on a doubly linked list container, so that every 11 | // operations time complexity is O(1). 12 | // 13 | // every operations over an instiated Deque are synchronized and 14 | // safe for concurrent usage. 15 | type Deque struct { 16 | sync.RWMutex 17 | container *list.List 18 | capacity int 19 | } 20 | 21 | // NewDeque creates a Deque. 22 | func NewDeque() *Deque { 23 | return NewCappedDeque(-1) 24 | } 25 | 26 | // NewCappedDeque creates a Deque with the specified capacity limit. 27 | func NewCappedDeque(capacity int) *Deque { 28 | return &Deque{ 29 | container: list.New(), 30 | capacity: capacity, 31 | } 32 | } 33 | 34 | //원소를 뒤에 넣는다 35 | // Append inserts element at the back of the Deque in a O(1) time complexity, 36 | // returning true if successful or false if the deque is at capacity. 37 | func (s *Deque) Append(item interface{}) (int, bool) { 38 | s.Lock() 39 | defer s.Unlock() 40 | 41 | count := s.container.Len() 42 | if s.capacity < 0 || count < s.capacity { 43 | s.container.PushBack(item) 44 | count += 1 45 | return count, true 46 | } 47 | 48 | return 0, false 49 | } 50 | 51 | // 앞에 원소를 넣는다 52 | // Prepend inserts element at the Deques front in a O(1) time complexity, 53 | // returning true if successful or false if the deque is at capacity. 54 | func (s *Deque) Prepend(item interface{}) (int, bool) { 55 | s.Lock() 56 | defer s.Unlock() 57 | 58 | count := s.container.Len() 59 | if s.capacity < 0 || count < s.capacity { 60 | s.container.PushFront(item) 61 | count += 1 62 | return count, true 63 | } 64 | 65 | return 0, false 66 | } 67 | 68 | // 제일 마지막에 넣은 원소를 뺀다 69 | // Pop removes the last element of the deque in a O(1) time complexity 70 | func (s *Deque) Pop() interface{} { 71 | s.Lock() 72 | defer s.Unlock() 73 | 74 | var item interface{} = nil 75 | var lastContainerItem *list.Element = nil 76 | 77 | lastContainerItem = s.container.Back() 78 | if lastContainerItem != nil { 79 | item = s.container.Remove(lastContainerItem) 80 | } 81 | 82 | return item 83 | } 84 | 85 | // 앞에 넣은 원소를 뺀다 86 | // Shift removes the first element of the deque in a O(1) time complexity 87 | func (s *Deque) Shift() interface{} { 88 | s.Lock() 89 | defer s.Unlock() 90 | 91 | var item interface{} = nil 92 | var firstContainerItem *list.Element = nil 93 | 94 | firstContainerItem = s.container.Front() 95 | if firstContainerItem != nil { 96 | item = s.container.Remove(firstContainerItem) 97 | } 98 | 99 | return item 100 | } 101 | 102 | // First returns the first value stored in the deque in a O(1) time complexity 103 | func (s *Deque) First() interface{} { 104 | s.RLock() 105 | defer s.RUnlock() 106 | 107 | item := s.container.Front() 108 | if item != nil { 109 | return item.Value 110 | } else { 111 | return nil 112 | } 113 | } 114 | 115 | // Last returns the last value stored in the deque in a O(1) time complexity 116 | func (s *Deque) Last() interface{} { 117 | s.RLock() 118 | defer s.RUnlock() 119 | 120 | item := s.container.Back() 121 | if item != nil { 122 | return item.Value 123 | } else { 124 | return nil 125 | } 126 | } 127 | 128 | // Size returns the actual deque size 129 | func (s *Deque) Size() int { 130 | s.RLock() 131 | defer s.RUnlock() 132 | 133 | return s.container.Len() 134 | } 135 | 136 | // Capacity returns the capacity of the deque, or -1 if unlimited 137 | func (s *Deque) Capacity() int { 138 | s.RLock() 139 | defer s.RUnlock() 140 | return s.capacity 141 | } 142 | 143 | // Empty checks if the deque is empty 144 | func (s *Deque) Empty() bool { 145 | s.RLock() 146 | defer s.RUnlock() 147 | 148 | return s.container.Len() == 0 149 | } 150 | 151 | // Full checks if the deque is full 152 | func (s *Deque) Full() bool { 153 | s.RLock() 154 | defer s.RUnlock() 155 | 156 | return s.capacity >= 0 && s.container.Len() >= s.capacity 157 | } 158 | -------------------------------------------------------------------------------- /gohipernetFake/utilPrintPanicStack.go: -------------------------------------------------------------------------------- 1 | package gohipernetFake 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "github.com/davecgh/go-spew/spew" 8 | ) 9 | 10 | func PrintPanicStack(extras ...interface{}) { 11 | if x := recover(); x != nil { 12 | logError("", 0, fmt.Sprintf("%v", x)) 13 | 14 | i := 0 15 | funcName, file, line, ok := runtime.Caller(i) 16 | 17 | for ok { 18 | msg := fmt.Sprintf("PrintPanicStack. [func]: %s, [file]: %s, [line]: %d\n", runtime.FuncForPC(funcName).Name(), file, line) 19 | logError("", 0, msg) 20 | i++ 21 | funcName, file, line, ok = runtime.Caller(i) 22 | } 23 | 24 | for k := range extras { 25 | msg := fmt.Sprintf("EXRAS#%v DATA:%v\n", k, spew.Sdump(extras[k])) 26 | logError("", 0, msg) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib_opensource/1m-go-tcp-server-master.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacking75/edu_GolangSocketGameServer/ff8782cfbf0bd5a321ffbe3fe6716de2f6c80e4e/lib_opensource/1m-go-tcp-server-master.zip -------------------------------------------------------------------------------- /lib_opensource/1m-go-websockets-master.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacking75/edu_GolangSocketGameServer/ff8782cfbf0bd5a321ffbe3fe6716de2f6c80e4e/lib_opensource/1m-go-websockets-master.zip -------------------------------------------------------------------------------- /lib_opensource/gnet-dev.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacking75/edu_GolangSocketGameServer/ff8782cfbf0bd5a321ffbe3fe6716de2f6c80e4e/lib_opensource/gnet-dev.zip -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | [Rr]eleases/ 14 | x64/ 15 | x86/ 16 | build/ 17 | bld/ 18 | [Bb]in/ 19 | [Oo]bj/ 20 | 21 | # Roslyn cache directories 22 | *.ide/ 23 | 24 | # MSTest test Results 25 | [Tt]est[Rr]esult*/ 26 | [Bb]uild[Ll]og.* 27 | 28 | #NUNIT 29 | *.VisualState.xml 30 | TestResult.xml 31 | 32 | # Build Results of an ATL Project 33 | [Dd]ebugPS/ 34 | [Rr]eleasePS/ 35 | dlldata.c 36 | 37 | *_i.c 38 | *_p.c 39 | *_i.h 40 | *.ilk 41 | *.meta 42 | *.obj 43 | *.pch 44 | *.pdb 45 | *.pgc 46 | *.pgd 47 | *.rsp 48 | *.sbr 49 | *.tlb 50 | *.tli 51 | *.tlh 52 | *.tmp 53 | *.tmp_proj 54 | *.log 55 | *.vspscc 56 | *.vssscc 57 | .builds 58 | *.pidb 59 | *.svclog 60 | *.scc 61 | 62 | # Chutzpah Test files 63 | _Chutzpah* 64 | 65 | # Visual C++ cache files 66 | ipch/ 67 | *.aps 68 | *.ncb 69 | *.opensdf 70 | *.sdf 71 | *.cachefile 72 | 73 | # Visual Studio profiler 74 | *.psess 75 | *.vsp 76 | *.vspx 77 | 78 | # TFS 2012 Local Workspace 79 | $tf/ 80 | 81 | # Guidance Automation Toolkit 82 | *.gpState 83 | 84 | # ReSharper is a .NET coding add-in 85 | _ReSharper*/ 86 | *.[Rr]e[Ss]harper 87 | *.DotSettings.user 88 | 89 | # JustCode is a .NET coding addin-in 90 | .JustCode 91 | 92 | # TeamCity is a build add-in 93 | _TeamCity* 94 | 95 | # DotCover is a Code Coverage Tool 96 | *.dotCover 97 | 98 | # NCrunch 99 | _NCrunch_* 100 | .*crunch*.local.xml 101 | 102 | # MightyMoose 103 | *.mm.* 104 | AutoTest.Net/ 105 | 106 | # Web workbench (sass) 107 | .sass-cache/ 108 | 109 | # Installshield output folder 110 | [Ee]xpress/ 111 | 112 | # DocProject is a documentation generator add-in 113 | DocProject/buildhelp/ 114 | DocProject/Help/*.HxT 115 | DocProject/Help/*.HxC 116 | DocProject/Help/*.hhc 117 | DocProject/Help/*.hhk 118 | DocProject/Help/*.hhp 119 | DocProject/Help/Html2 120 | DocProject/Help/html 121 | 122 | # Click-Once directory 123 | publish/ 124 | 125 | # Publish Web Output 126 | *.[Pp]ublish.xml 127 | *.azurePubxml 128 | # TODO: Comment the next line if you want to checkin your web deploy settings 129 | # but database connection strings (with potential passwords) will be unencrypted 130 | *.pubxml 131 | *.publishproj 132 | 133 | # NuGet Packages 134 | *.nupkg 135 | # The packages folder can be ignored because of Package Restore 136 | **/packages/* 137 | # except build/, which is used as an MSBuild target. 138 | !**/packages/build/ 139 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 140 | #!**/packages/repositories.config 141 | 142 | # Windows Azure Build Output 143 | csx/ 144 | *.build.csdef 145 | 146 | # Windows Store app package directory 147 | AppPackages/ 148 | 149 | # Others 150 | sql/ 151 | *.Cache 152 | ClientBin/ 153 | [Ss]tyle[Cc]op.* 154 | ~$* 155 | *~ 156 | *.dbmdl 157 | *.dbproj.schemaview 158 | *.pfx 159 | *.publishsettings 160 | node_modules/ 161 | 162 | # RIA/Silverlight projects 163 | Generated_Code/ 164 | 165 | # Backup & report files from converting an old project file 166 | # to a newer Visual Studio version. Backup files are not needed, 167 | # because we have git ;-) 168 | _UpgradeReport_Files/ 169 | Backup*/ 170 | UpgradeLog*.XML 171 | UpgradeLog*.htm 172 | 173 | # SQL Server files 174 | *.mdf 175 | *.ldf 176 | 177 | # Business Intelligence projects 178 | *.rdl.data 179 | *.bim.layout 180 | *.bim_*.settings 181 | 182 | # Microsoft Fakes 183 | FakesAssemblies/ 184 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, ymofen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of SimpleMsgPack.Net nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/README.md: -------------------------------------------------------------------------------- 1 | SimpleMsgPack.Net 2 | ================= 3 | 4 | MessagePack implementation for C# / msgpack.org[C#] 5 | 6 | Binary files distributed via the NuGet package [SimpleMsgPack](http://www.nuget.org/packages/SimpleMsgPack/). 7 | 8 | It's like JSON but small and fast. 9 | 10 | ``` 11 | unit Owner: D10.Mofen 12 | contact: 13 | qq:185511468, 14 | email:ymofen@diocp.org 15 | homepage:www.diocp.org 16 | if you find any bug, please contact me! 17 | ``` 18 | 19 | Works with 20 | -------- 21 | .NET Framework 4.x 22 | 23 | 24 | ### Code Example 25 | ```C# 26 | 27 | MsgPack msgpack = new MsgPack(); 28 | msgpack.ForcePathObject("p.name").AsString = "张三"; 29 | msgpack.ForcePathObject("p.age").AsInteger = 25; 30 | msgpack.ForcePathObject("p.datas").AsArray.Add(90); 31 | msgpack.ForcePathObject("p.datas").AsArray.Add(80); 32 | msgpack.ForcePathObject("p.datas").AsArray.Add("李四"); 33 | msgpack.ForcePathObject("p.datas").AsArray.Add(3.1415926); 34 | 35 | // pack file 36 | msgpack.ForcePathObject("p.filedata").LoadFileAsBytes("C:\\a.png"); 37 | 38 | // pack msgPack binary 39 | byte[] packData = msgpack.Encode2Bytes(); 40 | 41 | MsgPack unpack_msgpack = new MsgPack(); 42 | 43 | // unpack msgpack 44 | unpack_msgpack.DecodeFromBytes(packData); 45 | 46 | System.Console.WriteLine("name:{0}, age:{1}", 47 | unpack_msgpack.ForcePathObject("p.name").AsString, 48 | unpack_msgpack.ForcePathObject("p.age").AsInteger); 49 | 50 | Console.WriteLine("=================================="); 51 | System.Console.WriteLine("use index property, Length{0}:{1}", 52 | unpack_msgpack.ForcePathObject("p.datas").AsArray.Length, 53 | unpack_msgpack.ForcePathObject("p.datas").AsArray[0].AsString 54 | ); 55 | 56 | Console.WriteLine("=================================="); 57 | Console.WriteLine("use foreach statement:"); 58 | foreach (MsgPack item in unpack_msgpack.ForcePathObject("p.datas")) 59 | { 60 | Console.WriteLine(item.AsString); 61 | } 62 | 63 | // unpack filedata 64 | unpack_msgpack.ForcePathObject("p.filedata").SaveBytesToFile("C:\\b.png"); 65 | Console.Read(); 66 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/Samples/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/Samples/Program.cs: -------------------------------------------------------------------------------- 1 | using SimpleMsgPack; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace SimpleMsgPackTester 10 | { 11 | class Program 12 | { 13 | static void Test1() 14 | { 15 | MsgPack msgpack = new MsgPack(); 16 | msgpack.ForcePathObject("p.name").AsString = "张三一二三四五六七八九十"; 17 | msgpack.ForcePathObject("p.age").AsInteger = 132123456874125; 18 | msgpack.ForcePathObject("p.datas").AsArray.Add(90); 19 | msgpack.ForcePathObject("p.datas").AsArray.Add(80); 20 | msgpack.ForcePathObject("p.datas").AsArray.Add("李四"); 21 | msgpack.ForcePathObject("p.datas").AsArray.Add(3.1415926); 22 | msgpack.ForcePathObject("Game.iGameID").AsInteger = 1; 23 | 24 | // 可以直接打包文件数据 25 | // msgpack.ForcePathObject("p.filedata").LoadFileAsBytes("C:\\a.png"); 26 | 27 | // 打包成msgPack协议格式数据 28 | byte[] packData = msgpack.Encode2Bytes(); 29 | 30 | FileStream fs = new FileStream("d:\\simplemsgpack.dat", FileMode.Append); 31 | fs.Write(packData, 0, packData.Length); 32 | fs.Close(); 33 | 34 | //Console.WriteLine("msgpack序列化数据:\n{0}", BytesTools.BytesAsHexString(packData)); 35 | 36 | MsgPack unpack_msgpack = new MsgPack(); 37 | // 从msgPack协议格式数据中还原 38 | unpack_msgpack.DecodeFromBytes(packData); 39 | 40 | System.Console.WriteLine("name:{0}, age:{1}", 41 | unpack_msgpack.ForcePathObject("p.name").AsString, 42 | unpack_msgpack.ForcePathObject("p.age").AsInteger); 43 | 44 | Console.WriteLine("=================================="); 45 | System.Console.WriteLine("use index property, Length{0}:{1}", 46 | unpack_msgpack.ForcePathObject("p.datas").AsArray.Length, 47 | unpack_msgpack.ForcePathObject("p.datas").AsArray[0].AsString 48 | ); 49 | 50 | Console.WriteLine("=================================="); 51 | Console.WriteLine("use foreach statement:"); 52 | foreach (MsgPack item in unpack_msgpack.ForcePathObject("p.datas")) 53 | { 54 | Console.WriteLine(item.AsString); 55 | } 56 | 57 | Console.WriteLine(unpack_msgpack.ForcePathObject("Game.iGameID").AsInteger); 58 | 59 | // unpack filedata 60 | //unpack_msgpack.ForcePathObject("p.filedata").SaveBytesToFile("C:\\b.png"); 61 | Console.Read(); 62 | } 63 | 64 | static void Test2() 65 | { 66 | MsgPack msgpack = new MsgPack(); 67 | msgpack.AsString = "张三一二三四五六七八九十"; 68 | 69 | // 打包成msgPack协议格式数据 70 | byte[] packData = msgpack.Encode2Bytes(); 71 | 72 | FileStream fs = new FileStream("d:\\simplemsgpack11.dat", FileMode.Append); 73 | fs.Write(packData, 0, packData.Length); 74 | fs.Close(); 75 | 76 | } 77 | 78 | static void Test3() 79 | { 80 | MsgPack msgpack = new MsgPack(); 81 | msgpack.SetAsUInt64(UInt64.MaxValue - 1); 82 | 83 | // 打包成msgPack协议格式数据 84 | byte[] packData = msgpack.Encode2Bytes(); 85 | 86 | MsgPack unpack_msgpack = new MsgPack(); 87 | // 从msgPack协议格式数据中还原 88 | unpack_msgpack.DecodeFromBytes(packData); 89 | 90 | Console.WriteLine(unpack_msgpack.GetAsUInt64()); 91 | 92 | Console.Read(); 93 | } 94 | static void Main(string[] args) 95 | { 96 | 97 | Test3(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/Samples/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息通过以下 6 | // 特性集控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("SimpleMsgPackTester")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("SimpleMsgPackTester")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 使此程序集中的类型 18 | // 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | // 则将该类型上的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("a94a604b-38b5-404c-ae67-04ff837d1b14")] 24 | 25 | // 程序集的版本信息由下面四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/Samples/SimpleMsgPackTester.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {1A73BD27-0B50-4E31-92F7-602EBFCA6941} 8 | Exe 9 | Properties 10 | SimpleMsgPackTester 11 | SimpleMsgPackTester 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | {53ced7af-bcf5-453d-ac00-36247752eba8} 53 | SimpleMsgPack 54 | 55 | 56 | 57 | 64 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/SimpleMsgPack.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleMsgPack", "Source\SimpleMsgPack.csproj", "{53CED7AF-BCF5-453D-AC00-36247752EBA8}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleMsgPackTester", "Samples\SimpleMsgPackTester.csproj", "{1A73BD27-0B50-4E31-92F7-602EBFCA6941}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {53CED7AF-BCF5-453D-AC00-36247752EBA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {53CED7AF-BCF5-453D-AC00-36247752EBA8}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {53CED7AF-BCF5-453D-AC00-36247752EBA8}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {53CED7AF-BCF5-453D-AC00-36247752EBA8}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {1A73BD27-0B50-4E31-92F7-602EBFCA6941}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {1A73BD27-0B50-4E31-92F7-602EBFCA6941}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {1A73BD27-0B50-4E31-92F7-602EBFCA6941}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {1A73BD27-0B50-4E31-92F7-602EBFCA6941}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/Source/BytesTools.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SimpleMsgPack 8 | { 9 | public class BytesTools 10 | { 11 | static UTF8Encoding utf8Encode = new UTF8Encoding(); 12 | 13 | public static byte[] GetUtf8Bytes(String s) 14 | { 15 | 16 | return utf8Encode.GetBytes(s); 17 | } 18 | 19 | public static String GetString(byte[] utf8Bytes) 20 | { 21 | return utf8Encode.GetString(utf8Bytes); 22 | } 23 | 24 | public static String BytesAsString(byte[] bytes) 25 | { 26 | StringBuilder sb = new StringBuilder(); 27 | foreach (byte b in bytes) 28 | { 29 | sb.Append(String.Format("{0:D3} ", b)); 30 | } 31 | return sb.ToString(); 32 | } 33 | 34 | 35 | public static String BytesAsHexString(byte[] bytes) 36 | { 37 | StringBuilder sb = new StringBuilder(); 38 | foreach (byte b in bytes) 39 | { 40 | sb.Append(String.Format("{0:X2} ", b)); 41 | } 42 | return sb.ToString(); 43 | } 44 | 45 | /// 46 | /// 交换byte数组数据 47 | /// 可用于高低数据交换 48 | /// 49 | /// 要交换的byte数组 50 | /// 返回交换后的数据 51 | public static byte[] SwapBytes(byte[] v) 52 | { 53 | byte[] r = new byte[v.Length]; 54 | int j = v.Length - 1; 55 | for (int i = 0; i < r.Length; i++) 56 | { 57 | r[i] = v[j]; 58 | j--; 59 | } 60 | return r; 61 | } 62 | 63 | public static byte[] SwapInt64(Int64 v) 64 | { 65 | //byte[] r = new byte[8]; 66 | //r[7] = (byte)v; 67 | //r[6] = (byte)(v >> 8); 68 | //r[5] = (byte)(v >> 16); 69 | //r[4] = (byte)(v >> 24); 70 | //r[3] = (byte)(v >> 32); 71 | //r[2] = (byte)(v >> 40); 72 | //r[1] = (byte)(v >> 48); 73 | //r[0] = (byte)(v >> 56); 74 | return SwapBytes(BitConverter.GetBytes(v)); 75 | } 76 | 77 | public static byte[] SwapInt32(Int32 v) 78 | { 79 | byte[] r = new byte[4]; 80 | r[3] = (byte)v; 81 | r[2] = (byte)(v >> 8); 82 | r[1] = (byte)(v >> 16); 83 | r[0] = (byte)(v >> 24); 84 | return r; 85 | } 86 | 87 | 88 | public static byte[] SwapInt16(Int16 v) 89 | { 90 | byte[] r = new byte[2]; 91 | r[1] = (byte)v; 92 | r[0] = (byte)(v >> 8); 93 | return r; 94 | } 95 | 96 | public static byte[] SwapDouble(Double v) 97 | { 98 | return SwapBytes(BitConverter.GetBytes(v)); 99 | } 100 | 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/Source/Consts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SimpleMsgPack 8 | { 9 | public enum MsgPackType 10 | { 11 | Unknown = 0, 12 | Null = 1, 13 | Map = 2, 14 | Array = 3, 15 | String = 4, 16 | Integer = 5, 17 | UInt64 = 6, 18 | Boolean = 7, 19 | Float = 8, 20 | Single = 9, 21 | DateTime= 10, 22 | Binary = 11 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/Source/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SimpleMsgPack 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/Source/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息通过以下 6 | // 特性集控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("SimpleMsgPack")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("SimpleMsgPack")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 使此程序集中的类型 18 | // 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | // 则将该类型上的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("a14687ba-e7ef-4b8a-abc9-8d99b4763b02")] 24 | 25 | // 程序集的版本信息由下面四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/Source/ReadTools.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace SimpleMsgPack 9 | { 10 | class ReadTools 11 | { 12 | public static String ReadString(Stream ms, int len) 13 | { 14 | byte[] rawBytes = new byte[len]; 15 | ms.Read(rawBytes, 0, len); 16 | return BytesTools.GetString(rawBytes); 17 | } 18 | 19 | public static String ReadString(Stream ms) 20 | { 21 | byte strFlag =(byte)ms.ReadByte(); 22 | return ReadString(strFlag, ms); 23 | } 24 | 25 | public static String ReadString(byte strFlag, Stream ms) 26 | { 27 | // 28 | //fixstr stores a byte array whose length is upto 31 bytes: 29 | //+--------+========+ 30 | //|101XXXXX| data | 31 | //+--------+========+ 32 | // 33 | //str 8 stores a byte array whose length is upto (2^8)-1 bytes: 34 | //+--------+--------+========+ 35 | //| 0xd9 |YYYYYYYY| data | 36 | //+--------+--------+========+ 37 | // 38 | //str 16 stores a byte array whose length is upto (2^16)-1 bytes: 39 | //+--------+--------+--------+========+ 40 | //| 0xda |ZZZZZZZZ|ZZZZZZZZ| data | 41 | //+--------+--------+--------+========+ 42 | // 43 | //str 32 stores a byte array whose length is upto (2^32)-1 bytes: 44 | //+--------+--------+--------+--------+--------+========+ 45 | //| 0xdb |AAAAAAAA|AAAAAAAA|AAAAAAAA|AAAAAAAA| data | 46 | //+--------+--------+--------+--------+--------+========+ 47 | // 48 | //where 49 | //* XXXXX is a 5-bit unsigned integer which represents N 50 | //* YYYYYYYY is a 8-bit unsigned integer which represents N 51 | //* ZZZZZZZZ_ZZZZZZZZ is a 16-bit big-endian unsigned integer which represents N 52 | //* AAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA is a 32-bit big-endian unsigned integer which represents N 53 | //* N is the length of data 54 | 55 | byte[] rawBytes = null; 56 | int len = 0; 57 | if ((strFlag >= 0xA0)&&(strFlag<=0xBF)) 58 | { 59 | len = strFlag - 0xA0; 60 | }else if (strFlag == 0xD9) 61 | { 62 | len = ms.ReadByte(); 63 | } 64 | else if (strFlag == 0xDA) 65 | { 66 | rawBytes = new byte[2]; 67 | ms.Read(rawBytes, 0, 2); 68 | rawBytes = BytesTools.SwapBytes(rawBytes); 69 | len = BitConverter.ToInt16(rawBytes, 0); 70 | } 71 | else if (strFlag == 0xDB) 72 | { 73 | rawBytes = new byte[4]; 74 | ms.Read(rawBytes, 0, 4); 75 | rawBytes = BytesTools.SwapBytes(rawBytes); 76 | len = BitConverter.ToInt32(rawBytes, 0); 77 | } 78 | rawBytes = new byte[len]; 79 | ms.Read(rawBytes, 0, len); 80 | return BytesTools.GetString(rawBytes); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /thirdparty/SimpleMsgPack.Net/changes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacking75/edu_GolangSocketGameServer/ff8782cfbf0bd5a321ffbe3fe6716de2f6c80e4e/thirdparty/SimpleMsgPack.Net/changes.txt --------------------------------------------------------------------------------