이런 모양의 채팅방을 만들어보자.
지난 시간에 기본적인 php 웹소켓을 이용해서 채팅 서버를 만들어봤다. 이번엔 그 채팅서버를 이용해서 좀더 그럴듯한 채팅방을 만들어보려고 한다.
MyApp/Chat.php
<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Chat implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
$this->subscriptions = [];
$this->users = [];
$this->usernames = [];
$this->channelcnt = [];
}
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
$this->users[$conn->resourceId] = $conn;
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $conn, $msg)
{
$numRecv = count($this->clients) - 1;
echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
, $conn->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
$data = json_decode($msg);
$nowtime = date("Y-m-d H:i:s");
switch ($data->command) {
case "subscribe":
$this->subscriptions[$conn->resourceId] = $data->channel;
$this->usernames[$conn->resourceId] = $data->uname;
$this->channelcnt = array_count_values($this->subscriptions);
$target = $this->subscriptions[$conn->resourceId];
foreach ($this->subscriptions as $id=>$channel) {
if ($channel == $target && $id != $conn->resourceId) {
$sendmessage='{"nowtime" : "'.$nowtime.'","uname" : "SERVER","msg" : "'.$data->uname.'님이 대화방에 들어오셨습니다. 현재 인원은 '.$this->channelcnt[$data->channel].'명 입니다."}';
$this->users[$id]->send($sendmessage);
}
if ($channel == $target && $id == $conn->resourceId) {
$sendmessage='{"nowtime" : "'.$nowtime.'","uname" : "SERVER","msg" : "'.$channel.' 대화방에 입장하셨습니다. 현재 인원은 '.$this->channelcnt[$data->channel].'명 입니다."}';
$this->users[$id]->send($sendmessage);
}
}
break;
case "message":
$sendmessage='{"nowtime" : "'.$nowtime.'","uname" : "'.$data->uname.'","msg" : "'.$data->message.'"}';
if (isset($this->subscriptions[$conn->resourceId])) {
$target = $this->subscriptions[$conn->resourceId];
foreach ($this->subscriptions as $id=>$channel) {
if ($channel == $target) {//같은 채널에 모두에게 보냄
$this->users[$id]->send($sendmessage);
}
}
}
}
}
public function onClose(ConnectionInterface $conn)
{
$nowtime = date("Y-m-d H:i:s");
if (isset($this->subscriptions[$conn->resourceId])) {
$target = $this->subscriptions[$conn->resourceId];
foreach ($this->subscriptions as $id=>$channel) {
if ($channel == $target && $id != $conn->resourceId) { //나 말고 다른 상대에게만 보냄
$sendmessage='{"nowtime" : "'.$nowtime.'","uname" : "SERVER","msg" : "'.$this->usernames[$conn->resourceId].'님이 대화방에서 나가셨습니다. 현재 인원은 '.($this->channelcnt[$target]-1).'명 입니다."}';
$this->users[$id]->send($sendmessage);
}
}
}
$this->clients->detach($conn);
unset($this->users[$conn->resourceId]);
unset($this->subscriptions[$conn->resourceId]);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
?>
날짜와 시간을 넣었다.
사용자가 접근하는 페이지를 chatroom.php로 다시 만들었다.
<?php session_start();
$uname=$_GET["uname"]??"익명";
?>
<!doctype html>
<html lang="kr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
</head>
<style>
body{margin-top:20px;}
.chat-online {
color: #34ce57
}
.chat-offline {
color: #e4606d
}
.chat-messages {
display: flex;
flex-direction: column;
max-height: 800px;
overflow-y: scroll
}
.chat-message-left,
.chat-message-right {
display: flex;
flex-shrink: 0
}
.chat-message-left {
margin-right: auto
}
.chat-message-right {
flex-direction: row-reverse;
margin-left: auto
}
.py-3 {
padding-top: 1rem!important;
padding-bottom: 1rem!important;
}
.px-4 {
padding-right: 1.5rem!important;
padding-left: 1.5rem!important;
}
.flex-grow-0 {
flex-grow: 0!important;
}
.border-top {
border-top: 1px solid #dee2e6!important;
}
</style>
<main class="content">
<div class="container p-0">
<h1 class="h3 mb-3">Messages</h1>
<div class="col-12 col-lg-7 col-xl-9">
<div class="position-relative">
<div class="chat-messages p-4" id="chat">
</div>
</div>
<div class="flex-grow-0 py-3 px-4 border-top">
<div class="input-group">
<input type="hidden" id="uname" value="<?php echo $uname;?>">
<input type="text" class="form-control" placeholder="Type your message" id="message">
<button class="btn btn-primary" id="send">Send</button>
</div>
</div>
</div>
</div>
</main>
<script>
function getParameterByName(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
const chat = document.getElementById('chat');
const uname = document.getElementById('uname');
const messageInput = document.getElementById('message');
const sendButton = document.getElementById('send');
const socket = new WebSocket('ws://localhost:8080');//각자에 맞는 주소와 포트를 넣으면 된다.
function subscribe(channel) {
socket.send(JSON.stringify({command: "subscribe", channel: channel, uname:'<?php echo $uname;?>'}));
}
function sendMessage(uname,msg) {
socket.send(JSON.stringify({command: "message", uname:uname, message: msg}));
}
socket.onopen = function(e) {
console.log("Connection established!");
var channel = getParameterByName('channel');
subscribe(channel);
};
socket.onmessage = (event) => {//채팅내용을 받아서 뿌려줌
const myname = document.getElementById('uname').value
var rmsg=JSON.parse(event.data);
//var li = document.createElement('li');
//li.innerHTML = rmsg.uname + ' : ' + rmsg.msg;
const addhtml = document.createElement("div");
if(myname!=rmsg.uname){
addhtml.innerHTML='<div class="chat-message-left pb-4"><div><img src="https://bootdey.com/img/Content/avatar/avatar3.png" class="rounded-circle mr-1" alt="'+rmsg.uname+'" width="40" height="40"><div class="text-muted small text-nowrap mt-2">'+rmsg.nowtime+'</div></div><div class="flex-shrink-1 bg-light rounded py-2 px-3 ml-3"><div class="font-weight-bold mb-1">'+rmsg.uname+'</div>'+rmsg.msg+'</div></div>';
}else{
addhtml.innerHTML='<div class="chat-message-right pb-4"><div><img src="https://bootdey.com/img/Content/avatar/avatar1.png" class="rounded-circle mr-1" alt="'+rmsg.uname+'" width="40" height="40"><div class="text-muted small text-nowrap mt-2">'+rmsg.nowtime+'</div></div><div class="flex-shrink-1 bg-light rounded py-2 px-3 ml-3">'+rmsg.msg+'</div></div>';
}
chat.append(addhtml);
};
sendButton.addEventListener('click', () => {//채팅을 서버에 보냄
const uname = document.getElementById('uname').value?document.getElementById('uname').value:'익명';
const message = messageInput.value;
console.log({
uname : uname,
message : message
});
sendMessage(uname,message);
messageInput.value = '';
});
</script>
무료로 공개된 부트스트랩 템플릿을 가져왔다. 내가 올린 글은 오른쪽에 상대방이 보낸 메세지는 왼쪽에 표시되도록 했다.
http://localhost/chatroom.php?channel=test1&uname=손흥민
http://localhost/chatroom.php?channel=test1&uname=이강인
브라우저를 2개를 열어서 각각 위 주소를 하나씩 붙여 넣어 보자.
같은 채널에 이름만 다르게 접속했다. 대화를 해보면 이 글 맨위에 처럼 나타날 것이다. 아 물론 웹소켓 서버는 작동을 해놓고 해야한다. 웹소켓 서버 작동은 지난 시간 강좌를 확인해보자.
안되면 될때까지...
다음 시간엔 디비에 대화 내용을 저장하는 걸 해보자.
'PHP' 카테고리의 다른 글
[PHP]파일 용량 표시하는 함수 (0) | 2024.03.25 |
---|---|
PHP 웹소켓을 이용한 채팅 feat.Ratchet #3 - WSS(SSL) 사용하기 (0) | 2023.12.04 |
PHP 웹소켓을 이용한 채팅 feat.Ratchet #1 - 설치(설정)하기 (1) | 2023.11.20 |
PHP unix timestamp 변환 사이트 (0) | 2023.07.04 |
[PHP]첨부파일 속성 알아내기 (0) | 2023.06.26 |