Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
W
WebBack
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
websystem
WebBack
Commits
e94800de
Commit
e94800de
authored
5 months ago
by
심재엽
Browse files
Options
Downloads
Patches
Plain Diff
feat: 웹소켓 서버 작성 (
#9
)
parent
4e62421d
Branches
Branches containing commit
No related tags found
2 merge requests
!31
Develop
,
!11
[#9] 번개모임, 채팅 로직 구현
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
wsServer.js
+296
-0
296 additions, 0 deletions
wsServer.js
with
296 additions
and
0 deletions
wsServer.js
0 → 100644
+
296
−
0
View file @
e94800de
const
http
=
require
(
'
http
'
);
const
crypto
=
require
(
'
crypto
'
);
// const ChatRoom = require('./models/chatRoom.js');
const
mongoose
=
require
(
'
mongoose
'
);
const
ChatRoom
=
require
(
'
./models/chatRooms
'
);
// WebSocket 관련 데이터
let
clients
=
[];
let
chatRooms
=
{};
// MongoDB 연결 설정
async
function
connectMongoDB
()
{
try
{
await
mongoose
.
connect
(
'
mongodb://localhost:27017/chat
'
,
{
useNewUrlParser
:
true
,
useUnifiedTopology
:
true
,
});
console
.
log
(
'
MongoDB에 성공적으로 연결되었습니다.
'
);
// MongoDB 연결 성공 후 WebSocket 서버 시작
startWebSocketServer
();
}
catch
(
err
)
{
console
.
error
(
'
MongoDB 연결 실패:
'
,
err
);
process
.
exit
(
1
);
}
}
// 채팅방 기록 불러오기 함수
async
function
getChatHistory
(
chatRoomId
)
{
const
chatRoom
=
await
ChatRoom
.
findOne
({
chatRoomId
});
return
chatRoom
?
chatRoom
.
messages
:
[];
}
// WebSocket 서버 생성 및 핸드셰이크 처리
function
startWebSocketServer
()
{
const
wsServer
=
http
.
createServer
((
req
,
res
)
=>
{
res
.
writeHead
(
200
,
{
'
Content-Type
'
:
'
text/plain
'
});
res
.
end
(
'
WebSocket server is running
'
);
});
wsServer
.
on
(
'
upgrade
'
,
(
req
,
socket
,
head
)
=>
{
const
key
=
req
.
headers
[
'
sec-websocket-key
'
];
const
acceptKey
=
generateAcceptValue
(
key
);
const
responseHeaders
=
[
'
HTTP/1.1 101 Switching Protocols
'
,
'
Upgrade: websocket
'
,
'
Connection: Upgrade
'
,
`Sec-WebSocket-Accept:
${
acceptKey
}
`
];
socket
.
write
(
responseHeaders
.
join
(
'
\r\n
'
)
+
'
\r\n\r\n
'
);
// 클라이언트를 clients 배열에 추가
clients
.
push
(
socket
);
let
chatRoomId
=
null
;
let
nickname
=
null
;
socket
.
on
(
'
data
'
,
async
buffer
=>
{
let
message
;
try
{
message
=
parseMessage
(
buffer
);
const
parsedData
=
JSON
.
parse
(
message
);
const
{
type
,
chatRoomId
:
clientChatRoomId
,
nickname
:
clientNickname
,
text
}
=
parsedData
;
console
.
log
(
'
서버에서 수신한 메시지:
'
,
{
type
,
clientChatRoomId
,
clientNickname
,
text
});
if
(
type
===
'
join
'
||
type
===
'
leave
'
)
{
await
ChatRoom
.
updateOne
(
{
chatRoomId
:
clientChatRoomId
},
{
$set
:
{
[
`isOnline.
${
clientNickname
}
`
]:
type
===
'
join
'
}
}
);
const
statusMessage
=
{
type
:
'
status
'
,
chatRoomId
:
clientChatRoomId
,
nickname
:
clientNickname
,
isOnline
:
type
===
'
join
'
,
};
clients
.
forEach
(
client
=>
{
client
.
write
(
constructReply
(
JSON
.
stringify
(
statusMessage
)));
});
}
if
(
type
===
'
join
'
)
{
chatRoomId
=
clientChatRoomId
;
nickname
=
clientNickname
;
console
.
log
(
"
join시 chatRoomId
"
,
chatRoomId
);
console
.
log
(
"
join시 nickname
"
,
nickname
);
await
ChatRoom
.
updateOne
(
{
chatRoomId
},
{
$set
:
{
[
`isOnline.
${
nickname
}
`
]:
true
,
[
`lastReadLogId.
${
nickname
}
`
]:
null
,
},
}
);
if
(
!
chatRooms
[
chatRoomId
])
{
chatRooms
[
chatRoomId
]
=
[];
}
const
chatRoom
=
await
ChatRoom
.
findOne
({
chatRoomId
});
console
.
log
(
"
join시 chatRoom
"
,
chatRoom
);
if
(
!
chatRoom
)
{
console
.
error
(
`ChatRoom을 찾을 수 없습니다: chatRoomId =
${
chatRoomId
}
`
);
}
else
{
console
.
log
(
`ChatRoom 조회 성공:
${
chatRoom
}
`
);
}
const
isAlreadyParticipant
=
chatRoom
.
participants
.
includes
(
nickname
);
if
(
!
isAlreadyParticipant
)
{
const
joinMessage
=
{
message
:
`
${
nickname
}
님이 참가했습니다.`
,
timestamp
:
new
Date
(),
type
:
'
join
'
};
chatRooms
[
chatRoomId
].
push
(
joinMessage
);
await
ChatRoom
.
updateOne
({
chatRoomId
},
{
$push
:
{
messages
:
joinMessage
,
participants
:
nickname
}
});
clients
.
forEach
(
client
=>
{
client
.
write
(
constructReply
(
JSON
.
stringify
(
joinMessage
)));
});
}
else
{
console
.
log
(
`
${
nickname
}
은 이미 채팅방에 참가 중입니다.`
);
}
try
{
const
previousMessages
=
await
getChatHistory
(
chatRoomId
);
if
(
previousMessages
.
length
>
0
)
{
socket
.
write
(
constructReply
(
JSON
.
stringify
({
type
:
'
previousMessages
'
,
messages
:
previousMessages
})));
console
.
log
(
`이전 메시지 전송:
${
previousMessages
.
length
}
개`
);
}
}
catch
(
err
)
{
console
.
error
(
'
이전 채팅 기록 불러오기 중 오류 발생:
'
,
err
);
}
}
else
if
(
type
===
'
message
'
)
{
const
chatMessage
=
{
message
:
text
,
timestamp
:
new
Date
(),
type
:
'
message
'
,
sender
:
nickname
};
chatRooms
[
chatRoomId
].
push
(
chatMessage
);
try
{
// 새로운 메시지를 messages 배열에 추가
const
updatedChatRoom
=
await
ChatRoom
.
findOneAndUpdate
(
{
chatRoomId
},
{
$push
:
{
messages
:
chatMessage
}
},
{
new
:
true
,
fields
:
{
"
messages
"
:
{
$slice
:
-
1
}
}
}
// 마지막 추가된 메시지만 가져옴
);
// 마지막에 추가된 메시지의 _id를 가져오기
const
savedMessage
=
updatedChatRoom
.
messages
[
updatedChatRoom
.
messages
.
length
-
1
];
// 새로운 메시지 전송: 클라이언트로 메시지 브로드캐스트
const
messageData
=
{
type
:
'
message
'
,
chatRoomId
,
sender
:
nickname
,
message
:
text
,
timestamp
:
chatMessage
.
timestamp
,
_id
:
savedMessage
.
_id
// 저장된 메시지의 _id 사용
};
clients
.
forEach
(
client
=>
{
client
.
write
(
constructReply
(
JSON
.
stringify
(
messageData
)));
console
.
log
(
'
채팅 메시지 전송:
'
,
messageData
);
});
}
catch
(
err
)
{
console
.
error
(
'
MongoDB 채팅 메시지 저장 오류:
'
,
err
);
}
}
else
if
(
type
===
'
leave
'
)
{
const
leaveMessage
=
{
message
:
`
${
nickname
}
님이 퇴장했습니다.`
,
timestamp
:
new
Date
()
};
chatRooms
[
chatRoomId
].
push
(
leaveMessage
);
await
ChatRoom
.
updateOne
(
{
chatRoomId
},
{
$set
:
{
[
`isOnline.
${
nickname
}
`
]:
false
}
}
);
await
ChatRoom
.
updateOne
({
chatRoomId
},
{
$push
:
{
messages
:
leaveMessage
},
$pull
:
{
participants
:
nickname
}
});
clients
.
forEach
(
client
=>
{
client
.
write
(
constructReply
(
JSON
.
stringify
(
leaveMessage
)));
});
clients
=
clients
.
filter
(
client
=>
client
!==
socket
);
}
}
catch
(
err
)
{
console
.
error
(
'
메시지 처리 중 오류 발생:
'
,
err
);
}
});
socket
.
on
(
'
close
'
,
async
()
=>
{
if
(
nickname
&&
chatRoomId
)
{
await
ChatRoom
.
updateOne
(
{
chatRoomId
},
{
$set
:
{
[
`isOnline.
${
nickname
}
`
]:
false
}
}
);
const
statusMessage
=
{
type
:
'
status
'
,
chatRoomId
,
nickname
,
isOnline
:
false
,
};
clients
.
forEach
(
client
=>
{
client
.
write
(
constructReply
(
JSON
.
stringify
(
statusMessage
)));
});
}
});
socket
.
on
(
'
error
'
,
(
err
)
=>
{
console
.
error
(
`WebSocket error:
${
err
}
`
);
clients
=
clients
.
filter
(
client
=>
client
!==
socket
);
});
});
wsServer
.
listen
(
8081
,
()
=>
{
console
.
log
(
'
WebSocket 채팅 서버가 8081 포트에서 실행 중입니다.
'
);
});
}
// Sec-WebSocket-Accept 헤더 값 생성 -> env처리
function
generateAcceptValue
(
key
)
{
return
crypto
.
createHash
(
'
sha1
'
).
update
(
key
+
'
258EAFA5-E914-47DA-95CA-C5AB0DC85B11
'
,
'
binary
'
).
digest
(
'
base64
'
);
}
// WebSocket 메시지 파싱 함수
function
parseMessage
(
buffer
)
{
const
byteArray
=
[...
buffer
];
const
secondByte
=
byteArray
[
1
];
let
length
=
secondByte
&
127
;
let
maskStart
=
2
;
if
(
length
===
126
)
{
length
=
(
byteArray
[
2
]
<<
8
)
+
byteArray
[
3
];
maskStart
=
4
;
}
else
if
(
length
===
127
)
{
length
=
0
;
for
(
let
i
=
0
;
i
<
8
;
i
++
)
{
length
=
(
length
<<
8
)
+
byteArray
[
2
+
i
];
}
maskStart
=
10
;
}
const
dataStart
=
maskStart
+
4
;
const
mask
=
byteArray
.
slice
(
maskStart
,
dataStart
);
const
data
=
byteArray
.
slice
(
dataStart
,
dataStart
+
length
).
map
((
byte
,
i
)
=>
byte
^
mask
[
i
%
4
]);
return
new
TextDecoder
(
'
utf-8
'
).
decode
(
Uint8Array
.
from
(
data
));
}
// 클라이언트 메시지 응답 생성 함수
function
constructReply
(
message
)
{
const
messageBuffer
=
Buffer
.
from
(
message
,
'
utf-8
'
);
const
length
=
messageBuffer
.
length
;
const
reply
=
[
0x81
];
if
(
length
<
126
)
{
reply
.
push
(
length
);
}
else
if
(
length
<
65536
)
{
reply
.
push
(
126
,
(
length
>>
8
)
&
255
,
length
&
255
);
}
else
{
reply
.
push
(
127
,
(
length
>>
56
)
&
255
,
(
length
>>
48
)
&
255
,
(
length
>>
40
)
&
255
,
(
length
>>
32
)
&
255
,
(
length
>>
24
)
&
255
,
(
length
>>
16
)
&
255
,
(
length
>>
8
)
&
255
,
length
&
255
);
}
return
Buffer
.
concat
([
Buffer
.
from
(
reply
),
messageBuffer
]);
}
// MongoDB 연결 후 WebSocket 서버 시작
connectMongoDB
();
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment