初めに
今回はsocket.ioを使って簡単なチャットアプリを作ります
実装する機能
今回は以下の機能を実装します
- ルームの作成
- ルームに参加する
- メッセージを送信する
フォルダ構成
今回のフォルダ構成は以下の通りです。
---|
|---public|
| |--script.js
| |--index.html
| |--style.css
|
|---Servers|
| |--RoomServer.json
|
|--server.js
準備
次に、プロジェクトのフォルダーに移動して、以下のコマンドを入力してください(自分の場合は、C:/Users/qqn5192/Downloads/socketIOchatになります)
npm install socket.io express cors fs path
コードをコピー
以下のコードをコピペします
server.js:
server.js
const { Server } = require('socket.io');
const path = require('path');
const express = require('express');
const fs = require('fs');
const cors = require('cors');
// Server Initialize
const app = express();
app.use(cors());
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
const Sdirectory = path.join(__dirname, '/Servers/RoomServer.json');
// JSONファイルの初期化
if (!fs.existsSync(Sdirectory)) {
fs.writeFileSync(Sdirectory, JSON.stringify([]));
}
app.get('/search', (req, res) => {
const query = req.query.query;
if (!query) {
return res.status(400).json({ error: '検索クエリが指定されていません。' });
}
try {
const data = fs.readFileSync(Sdirectory, 'utf8');
const ParseData = JSON.parse(data);
if (typeof ParseData.rooms !== 'object' || ParseData.rooms === null) {
throw new TypeError('パースしたデータが無効です。');
}
const retDat = ParseData.rooms[query];
if (retDat) {
console.log(retDat);
return res.json({ roomname: query, messages: retDat });
} else {
return res.status(404).json({ message: '一致するデータが見つかりません。' });
}
} catch (err) {
console.error('JSONファイルの読み込みエラー:', err);
return res.status(500).json({ error: 'サーバーエラーが発生しました。' });
}
});
app.get('/home', (req, res) => {
res.sendFile(path.join(__dirname, '/public/index.html'));
});
app.get('/socket', (req, res) => {
res.sendFile(path.join(__dirname, 'node_modules', 'socket.io', 'client-dist', 'socket.io.js'))
})
const server = app.listen(3001, "<Your_Ipaddress>", () => { //Your_IPaddressには自分のIPv4アドレスを入力
console.log('サーバーが3001番ポートで起動しています');
});
// Socket.io Initialization with CORS settings
const io = new Server(server, {
cors: {
origin: '*',
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type'],
credentials: true
}
});
io.on('connection', (socket) => {
console.log('connection socket server on port 3001');
// クライアントに過去のメッセージを送信
try {
const existingData = JSON.parse(fs.readFileSync(Sdirectory, 'utf-8'));
} catch (err) {
console.error('JSONファイルの読み込みエラー:', err);
}
// クライアントからの 'sdmessage' イベントをリスニング
socket.on('sdmessage', (Mdata) => {
if (!Mdata.content) {
console.error('データが破損しているかクライアント側のコードが改造されています。');
} else {
console.log(Mdata);
const MessageData = {
name: Mdata.name,
content: Mdata.content,
timestamp: new Date().toISOString()
};
try {
const existingData = JSON.parse(fs.readFileSync(Sdirectory, 'utf-8'));
// ルームが存在しない場合、新たに作成
if (!existingData.rooms) {
existingData.rooms = {};
}
if (!existingData.rooms[Mdata.roomname]) {
existingData.rooms[Mdata.roomname] = []; // 修正: 新しいルームに空配列を代入
}
// ルームに新しいメッセージを追加
existingData.rooms[Mdata.roomname].push(MessageData);
// JSONファイルに書き込む
fs.writeFileSync(Sdirectory, JSON.stringify(existingData, null, 2));
} catch (err) {
console.error('JSONファイルの書き込みエラー:', err);
}
io.emit('message', MessageData);
}
});
// 検索イベント 'test'
socket.on('test', (Quer) => {
try {
const data = fs.readFileSync(Sdirectory, 'utf8');
const ParseData = JSON.parse(data);
const retDat = ParseData.find(SeaD => SeaD.roomname === Quer);
if (retDat) {
socket.emit('searchResult', retDat); // 結果をクライアントに個別送信
} else {
socket.emit('searchResult', { message: '一致するデータが見つかりません。' });
}
} catch (err) {
console.error('JSONファイルの読み込みエラー:', err);
socket.emit('searchResult', { error: 'サーバーエラーが発生しました。' });
}
});
socket.on('CreateRoom', (CreateRoom) => {
if(CreateRoom) {
} else {
}
})
});
script.js:
script.js
const socket = io('http://<Your_IPaddress>:3001'); //Your_IPaddressには自分のpcのIPv4アドレスを入力
let RoomID
// ルームIDの入力と検索
window.onload = async function() {
AppendData('System', 'Connected server')
RoomID = prompt('ルームIDを入力');
if (RoomID) {
console.log(`Room ID is ${RoomID}`);
AppendData('Home/connection', `${RoomID}を検索しています。`);
fetch(`/search?query=${encodeURIComponent(RoomID)}`, {
method: 'GET'
})
.then(response => response.json())
.then(data => {
if (data && !data.error) {
AppendData('SYSTEM', `ルームが見つかりました: ${data.roomname}`);
document.title = data.roomname;
document.getElementById('name').textContent = data.roomname;
LoadMessages(data.messages)
} else {
AppendData('SYSTEM', '一致するデータが見つかりません。');
}
})
.catch(err => {
console.error('検索リクエスト中にエラーが発生しました:', err);
AppendData('SYSTEM', '検索中にエラーが発生しました。');
});
}
}
function AppendData(chatname, chatcontent) {
const chatBody = document.getElementById('chatBody');
const messageElement = document.createElement('div');
messageElement.classList.add('message');
messageElement.textContent = `${chatname}: ${chatcontent}`;
chatBody.appendChild(messageElement);
}
// サーバーからの過去メッセージを受信し、表示
function LoadMessages(messages) {
if (!Array.isArray(messages)) {
console.error('受信したメッセージが無効です:', messages);
return; // エラーログを出力して処理を終了
}
const chatBody = document.getElementById('chatBody');
messages.forEach(message => {
const messageElement = document.createElement('div');
messageElement.classList.add('message');
messageElement.textContent = `${message.name}: ${message.content}`;
chatBody.appendChild(messageElement);
});
};
// 新しいメッセージをサーバーから受信して表示
socket.on('message', (MessageData) => {
AppendData(MessageData.name, MessageData.content);
});
// 送信ボタンのクリックイベント
document.getElementById('sendButton').addEventListener('click', () => {
const messageInput = document.getElementById('messageInput');
const messageContent = messageInput.value.trim();
if (messageContent) {
const name = localStorage.getItem('name');
const userName = name ? name : (localStorage.setItem('name', prompt('ユーザー名を入力')), localStorage.getItem('name'));
const messageData = {
roomname: RoomID,
name: userName,
content: messageContent
};
socket.emit('sdmessage', messageData);
messageInput.value = ''; // 入力欄をクリア
}
});
index.html:
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SocketIO_chat</title>
<script src="/socket"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="chat-container">
<div class="chat-header">
<h1 id="name">チャットアプリ</h1>
</div>
<div class="chat-body" id="chatBody">
<!-- メッセージはここに追加される -->
</div>
<div class="chat-footer">
<input type="text" class="message-input" id="messageInput" placeholder="メッセージを入力...">
<button class="send-button" id="sendButton">送信</button>
</div>
</div>
<script src="script.js" type="module"></script>
</body>
</html>
style.css:
style.css
body {
font-family: Arial, sans-serif;
background-color: #121212; /* ダークモード背景色 */
margin: 0;
padding: 0;
color: #e0e0e0; /* テキスト色 */
}
.chat-container {
width: 900px; /* PC版に対応した幅 */
margin: 50px auto;
border: 1px solid #333;
border-radius: 8px;
background-color: #1e1e1e; /* ダークな背景色 */
display: flex;
flex-direction: column;
height: 700px; /* PC版の高さ */
}
.chat-header {
background-color: #333;
color: #ffffff;
padding: 15px;
text-align: center;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}
.chat-body {
flex: 1;
padding: 15px;
overflow-y: auto;
border-bottom: 1px solid #333;
}
.message {
margin-bottom: 10px;
padding: 10px;
border-radius: 8px;
background-color: #2c2c2c; /* メッセージの背景色 */
}
.chat-footer {
display: flex;
border-top: 1px solid #333;
padding: 10px;
background-color: #1e1e1e;
}
.message-input {
flex: 1;
padding: 10px;
border: 1px solid #333;
border-radius: 8px;
background-color: #2c2c2c; /* 入力フィールドの背景色 */
color: #e0e0e0; /* 入力フィールドのテキスト色 */
}
.send-button {
padding: 10px 15px;
border: none;
border-radius: 8px;
background-color: #444;
color: #e0e0e0;
cursor: pointer;
}
.send-button:hover {
background-color: #555;
}
RoomServer.json:
RoomServer.json
{
"rooms": {
}
}
説明
今回はjson形式でデータを保存するようにしました。
最後に
今回はsocket.ioを使って簡単なチャットアプリを作りました。今回も、コードの修正をchatGPTにやってもらいました。
それではまたお会いしましょう!