강동현
Builds for 1 pipeline failed in 1 minute 20 seconds

Merge branch 'feature/room-ping' into develop

...@@ -79,6 +79,9 @@ export class ServerInboundMessageRecordMap { ...@@ -79,6 +79,9 @@ export class ServerInboundMessageRecordMap {
79 x: Number, 79 x: Number,
80 y: Number, 80 y: Number,
81 }); 81 });
82 +
83 + // 핑을 보냅니다.
84 + ping = Record({});
82 } 85 }
83 86
84 type ServerInboundMessageMap = { 87 type ServerInboundMessageMap = {
...@@ -164,6 +167,9 @@ interface ServerOutboundMessageMap { ...@@ -164,6 +167,9 @@ interface ServerOutboundMessageMap {
164 x: number; 167 x: number;
165 y: number; 168 y: number;
166 }; 169 };
170 +
171 + // 핑을 보냅니다.
172 + ping: {};
167 } 173 }
168 174
169 export interface RawMessage { 175 export interface RawMessage {
......
...@@ -26,6 +26,7 @@ export class Connection { ...@@ -26,6 +26,7 @@ export class Connection {
26 this.roomManager = roomManager; 26 this.roomManager = roomManager;
27 socket.setHandler((raw) => this.handleRaw(raw)); 27 socket.setHandler((raw) => this.handleRaw(raw));
28 socket.setDisconnectHandler(() => this.handleDisconnect()); 28 socket.setDisconnectHandler(() => this.handleDisconnect());
29 + socket.setPingHandler(() => this.handlePing());
29 } 30 }
30 31
31 public send<T extends ServerOutboundMessageKey>( 32 public send<T extends ServerOutboundMessageKey>(
...@@ -38,6 +39,10 @@ export class Connection { ...@@ -38,6 +39,10 @@ export class Connection {
38 }); 39 });
39 } 40 }
40 41
42 + public sendPing() {
43 + this.socket.sendPing();
44 + }
45 +
41 public handleRaw(raw: RawMessage): ServerResponse<any> { 46 public handleRaw(raw: RawMessage): ServerResponse<any> {
42 if (!raw || !raw.message || !raw.type) { 47 if (!raw || !raw.message || !raw.type) {
43 return { ok: false }; 48 return { ok: false };
...@@ -93,4 +98,10 @@ export class Connection { ...@@ -93,4 +98,10 @@ export class Connection {
93 public handleDisconnect(): void { 98 public handleDisconnect(): void {
94 this.user?.disconnected(); 99 this.user?.disconnected();
95 } 100 }
101 +
102 + public handlePing(): void {
103 + if (this.user) {
104 + this.user?.room?.pong(this.user);
105 + }
106 + }
96 } 107 }
......
...@@ -4,7 +4,9 @@ import { RawMessage, ServerResponse } from "../../common"; ...@@ -4,7 +4,9 @@ import { RawMessage, ServerResponse } from "../../common";
4 export interface SocketWrapper { 4 export interface SocketWrapper {
5 setHandler: (listener: (raw: RawMessage) => ServerResponse<any>) => void; 5 setHandler: (listener: (raw: RawMessage) => ServerResponse<any>) => void;
6 setDisconnectHandler: (listener: () => void) => void; 6 setDisconnectHandler: (listener: () => void) => void;
7 + setPingHandler: (listener: () => void) => void;
7 send: (raw: RawMessage) => void; 8 send: (raw: RawMessage) => void;
9 + sendPing: () => void;
8 } 10 }
9 11
10 export class SocketIoWrapper implements SocketWrapper { 12 export class SocketIoWrapper implements SocketWrapper {
...@@ -26,7 +28,17 @@ export class SocketIoWrapper implements SocketWrapper { ...@@ -26,7 +28,17 @@ export class SocketIoWrapper implements SocketWrapper {
26 }); 28 });
27 } 29 }
28 30
31 + public setPingHandler(listener: () => void) {
32 + this.socketIo.on("ping", () => {
33 + listener();
34 + });
35 + }
36 +
29 public send(raw: RawMessage) { 37 public send(raw: RawMessage) {
30 this.socketIo.emit("msg", raw); 38 this.socketIo.emit("msg", raw);
31 } 39 }
40 +
41 + public sendPing() {
42 + this.socketIo.emit("ping", {});
43 + }
32 } 44 }
......
...@@ -29,6 +29,8 @@ export class Room { ...@@ -29,6 +29,8 @@ export class Room {
29 29
30 public handler: MessageHandler; 30 public handler: MessageHandler;
31 31
32 + public pingTimeout: NodeJS.Timeout;
33 +
32 constructor( 34 constructor(
33 roomManager: RoomManager, 35 roomManager: RoomManager,
34 name: string, 36 name: string,
...@@ -95,6 +97,8 @@ export class Room { ...@@ -95,6 +97,8 @@ export class Room {
95 if (this.admin) { 97 if (this.admin) {
96 this.connect(this.admin); 98 this.connect(this.admin);
97 } 99 }
100 +
101 + this.pingTimeout = setInterval(() => this.ping(), 1000);
98 } 102 }
99 103
100 public connect(user: User): void { 104 public connect(user: User): void {
...@@ -114,6 +118,8 @@ export class Room { ...@@ -114,6 +118,8 @@ export class Room {
114 118
115 this.users.push(user); 119 this.users.push(user);
116 user.room = this; 120 user.room = this;
121 +
122 + user.lastPong = Date.now();
117 } 123 }
118 124
119 public disconnect(user: User): void { 125 public disconnect(user: User): void {
...@@ -143,6 +149,23 @@ export class Room { ...@@ -143,6 +149,23 @@ export class Room {
143 } 149 }
144 } 150 }
145 151
152 + public ping(): void {
153 + this.users.forEach((u) => {
154 + u.connection.sendPing();
155 + if (Date.now() - u.lastPong > 2000) {
156 + this.disconnect(u);
157 + }
158 + });
159 + }
160 +
161 + public pong(user: User) {
162 + if (!this.users.includes(user)) {
163 + return { ok: false };
164 + }
165 + user.lastPong = Date.now();
166 + return { ok: true };
167 + }
168 +
146 public setAdmin(user: User) { 169 public setAdmin(user: User) {
147 if (this.users.includes(user)) { 170 if (this.users.includes(user)) {
148 const prevAdmin = this.admin; 171 const prevAdmin = this.admin;
...@@ -272,6 +295,7 @@ export class Room { ...@@ -272,6 +295,7 @@ export class Room {
272 this.users.forEach((u) => this.disconnect(u)); 295 this.users.forEach((u) => this.disconnect(u));
273 this.closed = true; 296 this.closed = true;
274 this.roomManager.delete(this.uuid); 297 this.roomManager.delete(this.uuid);
298 + clearInterval(this.pingTimeout);
275 } 299 }
276 } 300 }
277 } 301 }
......
...@@ -15,6 +15,8 @@ export class User { ...@@ -15,6 +15,8 @@ export class User {
15 15
16 public handler: MessageHandler; 16 public handler: MessageHandler;
17 17
18 + public lastPong: number = 0; // 방에서 마지막으로 핑을 받은 시각
19 +
18 constructor(nickname: string, connection: Connection) { 20 constructor(nickname: string, connection: Connection) {
19 this.username = uuidv4(); 21 this.username = uuidv4();
20 this.nickname = nickname; 22 this.nickname = nickname;
......