1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Socket.ioで簡単なチャットアプリを作る

Posted at

初めに

今回は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にやってもらいました。
それではまたお会いしましょう!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?