JAVAでのUDP通信(オンラインチャット実装)


目標

顧客があるサービスのオンラインヘルプに問い合わせすると想定し、チャット機能をjavaで実装する。
機能だけ実装し、UI部分は無し。

TODO整理

目標を実現するのに必要なのは以下が考えられます。

  • UDP通信

UDP通信特徴
①リアリティ性が求められない
②かつクライアントとサーバーか、クライアントとクライアントとの通信にこだわらない

今回の実装にUDP通信を使います。

  • スレッド

メッセージ受け取るにも送るのにも同時に行う必要があるのため、スレッドを実装すると考えらる。

実装

  • メッセージ送信処理

UDP通信はクラスDatagramSocketを使う

SendThread.java
package com.maekawa.onlineChat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class SendThread implements Runnable {
    //UDP通信を行うためのソケット
    DatagramSocket socket = null;
    //入力を一行ずつ読み込む
    BufferedReader reader = null;
    private int fromPort;
    private String toIp;
    private int toPort;

    //コネクションを一回接続のため、初期化してコンストラクタにした。
    //送信元のポートと送信先IPとポート初期化処理する
    public SendThread(int fromPort, String toIp, int toPort) {
        this.fromPort = fromPort;
        this.toIp = toIp;
        this.toPort = toPort;

        try {
            socket = new DatagramSocket(fromPort);
            //コンソールからの入力ストリームを受ける
            InputStreamReader streamReader = new InputStreamReader(System.in);
            reader = new BufferedReader(streamReader);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }
    //ひたすらにメッセージ送信処理をするメソッド
    @Override
    public void run() {
        //whileがないと、一回メッセージ送ったら、接続遮断されちゃう
        while (true) {

            try {
                String data = reader.readLine();
                byte[] datas = data.getBytes();
                //送信先のIPとポート指定
                InetSocketAddress socketAddress = new InetSocketAddress(this.toIp, this.toPort);
                //パケット送信準備
                DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, socketAddress);
                //送信する
                socket.send(packet);
                if(data.equals("bye")){
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //接続を遮断する
            socket.close();
        }
    }
}


  • メッセージ受け取る処理する
ReceiveThread.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

public class ReceiveThread implements Runnable {

    DatagramSocket socket = null;
    private int receivePort;
    private String whois;

    //コネクション設立ためのメソッド
    public ReceiveThread(int receivePort, String whois) {
        this.receivePort = receivePort;
        this.whois = whois;
        try {
            //顧客問い合わせ用にこのポートで待ち受けする。
            socket = new DatagramSocket(receivePort);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    //受け取ったメッセージをひたすらに処理するメソッド
    @Override
    public void run() {
        while (true) {
            try {
                //受け取ったパケットを格納する
                byte[] datas = new byte[1024];
                DatagramPacket packet = new DatagramPacket(datas, 0, datas.length);
                socket.receive(packet);

                byte[] data = packet.getData();
                String msg = new String(data, 0, data.length);
                //受け取ったメッセージを出力
                System.out.println(whois + msg);

                if (msg.equals("bye")) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //接続を遮断する
        socket.close();
    }
}

  • 利用者

利用者は送ると受け取る両方用意する

Server.java
public class Server {
    public static void main(String[] args) {
        //顧客への回答スレッド,2222ポートからlocalhostの8888に問い合わせする
        new Thread(new SendThread(2222,"localhost",8888)).start();
        //ヘルパーからのメッセージは9999ポートで受け取る
        new Thread(new ReceiveThread(9999,"顧客")).start();
    }
}
Custumer.java
public class Custumer {
    public static void main(String[] args) {
        //顧客問い合わせするスレッド,8888ポートからlocalhostの9999に問い合わせする
        new Thread(new SendThread(7777,"localhost",9999)).start();
        //ヘルパーからのメッセージは7777ポートで受け取る
        new Thread(new ReceiveThread(8888,"ヘルパー")).start();
    }
}