udpの実験用サーバー Java編

以下のプログラムのudpreply.jarは、ここからダウンロードできます。

01
02
03
04
05
06
07
08
09
10 11
12
13
14
15
16
17
18
19
20 21
22
23
24
25
26
27
28
29
30 31
32
33
34
35
36
37
38
39
40 41
42
43
44
45
46
47
48
49
50 51
52
53
54
55
56
57
58
59
60 61
62
63
64
65
66
67
68
69
70 71
72
73
74
75
76
77
78
79
80 81
82
83
84
85
86
87
88
89
90 91
92
93
94
95
96
97
98
99
100 101
102
103
104
105
106
107
108
109
110 111
112
113
114
115
116
117
118
119
120 121
122
123
124
125
126
127
128
129
130 131
132
133
134
135
136
137
138
139
130 141
142
143
144
145
146
147
148
149
150 151
152
153
154
155
156
157
158
159
160 161
162
163
164
165
166
167
168
169
170 171
172
173
174
175
176
177
178
179
180 181
182
183
184
185
186
187
188
189
190 191
192
193
194
195
196
197
198
199
200 201
202
203
204
205
206
207
208
209
import java.io.*;//BufferedReadered用
import java.net.*;//DatagramSocket用
import java.util.*; //Calendarクラス利用のため
public class UdpReply implements Runnable {
	static String newLine = System.getProperty("line.separator");
	static String IPアドレス;
	static String ホスト名;

	int portNumber = 49152;//ポート番号
	DatagramSocket recSocket;
	
	byte[] buf = new byte[256];
	DatagramPacket packet = new DatagramPacket(buf, buf.length);
	
	boolean loop = true;
	FileOutputStream logOs;
	
	HashMap<String, User>hashMap = new HashMap<String, User>();
	int countUser = 0;// ユーザーカウント
	
	//ユーザー管理するクラス
	class User {
		String s学籍番号;//key保存する時のキー
		String hostName;//key保存する時のキー この2つ
		String msg;//最後に記憶されたのメッセージ
		String ip;
		int port;
		String s受信時間 = "";
		int i状態 = 0;//未接続 ,1初期文字列, 2次文字列 
		int userNumner; //このユーザーのカウント番号
		
		public void do振る舞い()throws Exception{
			//自身をhashMapに登録する。キーが登録済みの場合は、新しい方で登録し直す。
			//但し、userNumnerは、最初のcountUserの値です。
			//そして、相手にメッセージを送り返します。
			
			//メッセージから学籍番号の部分を記憶し、適合しなければそのメッセージを送信
			if(this.msg.length() < 7){
				sendMessage("メッセージを取得しましたが、受信文字数が足りません。学籍番号がメッセージ先頭にありますか?\n");
				return;
			}
			this.s学籍番号 = this.msg.substring(0, 7);
			if(! this.msg.matches("s[0-9]{6,6}.*")){
				sendMessage("メッセージを取得しましたが、sから始まる学籍番号がメッセージ先頭にありません?\n");
				return;
			}
			//未登録なら、 UdpReply.countUserを増やし、その番号で 登録します。
			String key = this.hostName;
			User user = UdpReply.this.hashMap.get(key);
			if(user == null){
				//未登録で、ユーザーのカウント番号を増やして、それを付ける。
				UdpReply.this.countUser++;
				this.userNumner = UdpReply.this.countUser;
				UdpReply.this.hashMap.put(key, this);
			} else {
				if(! this.s学籍番号.equals(user.s学籍番号)){
					String warning = this.s学籍番号 +"さんへ、このマシンは、" + user.s学籍番号 + "の方が使っています。";
					warning += "異なるマシンで送信ください" + newLine + newLine;
					sendMessage(warning);
					UdpReply.this.logOs.write(warning.getBytes());
					UdpReply.this.logOs.flush();
					return;
				}
				this.userNumner = user.userNumner;//初期のユーザー番号()
				this.i状態 = user.i状態; //以前の状態
				UdpReply.this.hashMap.put(key, this);
			}
			this.sendMessageBySate();//相手にメッセージを送り返す。
		}
	
		// 状態に応じたメッセージを表示
		void sendMessageBySate()throws Exception{
			String msg = "";
			if(this.i状態 == 0){
				this.i状態 = 1; 
				msg = "はい、最初受信を確認しました。あなたは、『" + this.userNumner + "』番目のユーザーです。\n";
				sendMessage(msg);
				msg = "『" + this.userNumner + "』の番号を先頭にしたメッセージを送信ください。\n";
				sendMessage(msg);
				String log = this.s学籍番号+" 1の状態" + newLine + newLine;
				UdpReply.this.logOs.write(log.getBytes());
				UdpReply.this.logOs.flush();
			} else if(this.i状態 == 1){
				if(this.msg.length() >= 7){
					String s = this.msg.substring(7);
					if(s.matches("" + this.userNumner + ".*")){
						this.i状態 = 2;
						sendMessage("はい、正しく受け取りました。\n");
						String log = this.s学籍番号+" 2の状態" + newLine + newLine;
						UdpReply.this.logOs.write(log.getBytes());
						UdpReply.this.logOs.flush();
						return;
					}
				}
				msg = "『" + this.userNumner + "』の番号を先頭にしたメッセージを送信ください。\n";
				sendMessage(msg);
			}else if(this.i状態 == 2){
				sendMessage("あなたからのメッセージ受信は終了しています。(登録済みです)\n");
			}
		}
		//引数の文字列を、相手に送り返します。
		void sendMessage(String msg)throws Exception{
			InetAddress inet = InetAddress.getByName(this.ip);//送信先(変更ください)
			int portNumber = 49152;//ポート番号
			DatagramSocket sendSocket = new DatagramSocket();//UDP送信用ソケット
			byte[] buf = msg.getBytes();//バイト列に変換
			DatagramPacket packet;
			packet= new DatagramPacket(buf, buf.length, inet, portNumber);
								//IPアドレス、ポート番号も指定
			if(IPアドレス.equals(inet.getHostAddress())|| inet.getHostAddress().startsWith("127.")){
				System.out.println(IPアドレス + "の自身マシンからのパケットに対しては、パケットを送り返しません。");
				//このプログラムは受けっと情報で相手に送り返すように作られています。
				//しかし、その送り先が自身のホストであると、その受信で再び自身に送り返すループができてしまう。
				//それを防ぐために このifがあります。
			} else {
				sendSocket.send(packet);//送信
			}
			sendSocket.close();
		}
	}
	
	//コンストラクタ 受信スレッドのスレッドを起動
	public UdpReply(){
		try {
			this.recSocket = new DatagramSocket(portNumber);//UDP受信用ソケット
			this.recSocket.setSoTimeout(0);//0 は無限のタイムアウトとして解釈
			System.out.println("<" + portNumber + "のポート番号で受信待機状態>");
			Thread thread = new Thread(this);
			thread.start(); // 上記のrunスレッド起動
			String logFileName = "log" + getDateTimeString(true) + ".log";
			this.logOs = new FileOutputStream(logFileName);
		}
		catch (Exception e){
			e.printStackTrace();
		}
	}

	public void run(){
		while(this.loop){
			try {
				String logStr = "";
				recSocket.receive(packet);//受信& wait

				//受信情報取得
				InetAddress recInet = packet.getAddress();
				User user = new User();
				user.s受信時間 = getDateTimeString(false);//現在の日時を取得します。
				user.hostName = recInet.getCanonicalHostName();
				logStr += user.hostName; //相手接続先ホスト名
				
				user.ip =recInet.getHostAddress(); 
				logStr += ":" + user.ip;//IPアドレス
				
				user.port = packet.getPort();
				logStr += ":" + user.port;//ポート番号
				
				int len = packet.getLength();//受信バイト数取得
				String msg = new String(buf, 0, len, "MS932");			

				logStr += "『" + msg + "』:" + len + "byte 受信しました。";
				user.msg = msg;//受信メッセージ
				logStr += user.s受信時間 + System.getProperty("line.separator");
				
				System.out.print(logStr);//ログ用文字列
				
				this.logOs.write(logStr.getBytes());
				this.logOs.flush();
				user.do振る舞い();
			}
			catch (Exception e){
				e.printStackTrace();
				loop = false;
			}
		}
		recSocket.close();
	}
	
	//現在の日時と時間を取得
	// 引数が、falseではれば「2009/10/24 10:52:58」の文字列で、
	// 引数が trueであれば、「2009_10_24_10_52_58」の文字列になる。
	static String getDateTimeString(boolean file){
		Calendar calendar = Calendar.getInstance();
		Date date = calendar.getTime();
		java.text.DateFormat dataFormat = 
			java.text.DateFormat.getDateTimeInstance();
		String time = dataFormat.format(date);
		if(file){
			time = time.replaceAll("[/ :]","_");
		}
		return time;
	}
	
	public static void main(String[] args) throws Exception {
		//ローカルのInetAddress(IPアドレス)を取得
		InetAddress inet  = InetAddress.getLocalHost();

		System.out.println("IPアドレス:" + (IPアドレス = inet.getHostAddress()));
		ホスト名 = inet.getHostName();
		System.out.println("ホスト名:" +ホスト名);

		System.out.println("[end]で終了");
		UdpReply reply = new UdpReply();
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		for(;;){
			String s = br.readLine();
			if(s.equals("end")) {
				reply.loop = false;
				System.exit(0);
			}
		}
	}
}

古いバージョンのLinuxでも動作するソースも以下に紹介します。

// Linux 用
import java.io.*;//BufferedReadered用
import java.net.*;//DatagramSocket用
import java.util.*; //Calendarクラス利用のため
public class UdpReply0 implements Runnable {
        static String newLine = System.getProperty("line.separator");
        static String IPアドレス;
        static String ホスト名;

        int portNumber = 49152;//ポート番号
        DatagramSocket recSocket;

        byte[] buf = new byte[256];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

        boolean loop = true;
        FileOutputStream logOs;

        HashMap hashMap = new HashMap();
        int countUser = 0;// ユーザーカウント

        //ユーザー管理するクラス
        class User {
                String s学籍番号;//key保存する時のキー
                String hostName;//key保存する時のキー この2つ
                String msg;//最後に記憶されたのメッセージ
                String ip;
                int port;
                String s受信時間 = "";
                int i状態 = 0;//未接続 ,1初期文字列, 2次文字列
                int userNumner; //このユーザーのカウント番号

                public void do振る舞い()throws Exception{
                        //自身をhashMapに登録する。キーが登録済みの場合は、新し い方で登録し直す。
                        //但し、userNumnerは、最初のcountUserの値です。
                        //そして、相手にメッセージを送り返します。

                        //メッセージから学籍番号の部分を記憶し、適合しなければそのメッセージを送信
                        if(this.msg.length() < 7){
                                sendMessage("メッセージを取得しましたが、受信文 字数が足りません。学籍番号がメッセージ先頭にありますか?\n");
                                return;
                        }
                        this.s学籍番号 = this.msg.substring(0, 7);
                        if(! this.msg.matches("s[0-9]{6,6}.*")){
                                sendMessage("メッセージを取得しましたが、sから始まる学籍番号がメッセージ先頭にありません?\n");
                                return;
                        }
                        //未登録なら、 UdpReply0.countUserを増やし、その番号で 登録します。
                        String key = this.hostName;
                        User user = (User) UdpReply0.this.hashMap.get((Object)key);
                        if(user == null){
                                //未登録で、ユーザーのカウント番号を増やして、それを付ける。
                                UdpReply0.this.countUser++;
                                this.userNumner = UdpReply0.this.countUser;
                                UdpReply0.this.hashMap.put(key, this);
                        } else {
                                if(! this.s学籍番号.equals(user.s学籍番号)){
                                        String warning = this.s学籍番号 +"さんへ、このマシンは、" + user.s学籍番号 + "の方が使っています。";
                                        warning += "異なるマシンで送信ください" + newLine + newLine;
                                        sendMessage(warning);
                                        UdpReply0.this.logOs.write(warning.getBytes());
                                        UdpReply0.this.logOs.flush();
                                        return;
                                }
                                this.userNumner = user.userNumner;//初期のユーザー番号()
                                this.i状態 = user.i状態; //以前の状態
                                UdpReply0.this.hashMap.put(key, this);
                        }
                        this.sendMessageBySate();//相手にメッセージを送り返す。
                }

                // 状態に応じたメッセージを表示
                void sendMessageBySate()throws Exception{
                        String msg = "";
                        if(this.i状態 == 0){
                                this.i状態 = 1;
                                msg = "はい、最初受信を確認しました。あなたは、 『" + this.userNumner + "』番目のユーザーです。\n";
                                sendMessage(msg);
                                msg = "『" + this.userNumner + "』の番号を先頭にしたメッセージを送信ください。\n";
                                sendMessage(msg);
                                String log = this.s学籍番号+" 1の状態" + newLine + newLine;
                                UdpReply0.this.logOs.write(log.getBytes());
                                UdpReply0.this.logOs.flush();
                        } else if(this.i状態 == 1){
                                if(this.msg.length() >= 7){
                                        String s = this.msg.substring(7);
                                        if(s.matches("" + this.userNumner + ".*")){
                                                this.i状態 = 2;
                                                sendMessage("はい、正しく受け取 りました。\n");
                                                String log = this.s学籍番号+" 2の状態" + newLine + newLine;
                                                UdpReply0.this.logOs.write(log.getBytes());
                                                UdpReply0.this.logOs.flush();
                                                return;
                                        }
                                }
                                msg = "『" + this.userNumner + "』の番号を先頭にしたメッセージを送信ください。\n";
                                sendMessage(msg);
                        }else if(this.i状態 == 2){
                                sendMessage("あなたからのメッセージ受信は終了し ています。(登録済みです)\n");
                        }
                }
                //引数の文字列を、相手に送り返します。
                void sendMessage(String msg)throws Exception{
                        InetAddress inet = InetAddress.getByName(this.ip);//送信先(変更ください)
                        int portNumber = 49152;//ポート番号
                        DatagramSocket sendSocket = new DatagramSocket();//UDP送信用ソケット
                        byte[] buf = msg.getBytes("MS932");//バイト列に変換
                        DatagramPacket packet;
                        packet= new DatagramPacket(buf, buf.length, inet, portNumber);
                                                                //IPアドレス、ポート番号も指定
                        if(IPアドレス.equals(inet.getHostAddress())){
                                System.out.println(IPアドレス + "の自身マシンか らのパケットに対しては、パケットを送り返しません。");
                        } else {
                                sendSocket.send(packet);//送信
                        }
                        sendSocket.close();
                }
        }

        //コンストラクタ 受信スレッドのスレッドを起動
        public UdpReply0(){
                try {
                        this.recSocket = new DatagramSocket(portNumber);//UDP受 信用ソケット
                        this.recSocket.setSoTimeout(0);//0 は無限のタイムアウト として解釈
                        System.out.println("<" + portNumber + "のポート番号で受 信待機状態>");
                        Thread thread = new Thread(this);
                        thread.start(); // 上記のrunスレッド起動
                        String logFileName = "log" + getDateTimeString(true) + ".log";
                        this.logOs = new FileOutputStream(logFileName);
                }
                catch (Exception e){
                        e.printStackTrace();
                }
        }

        public void run(){
                while(this.loop){
                        try {
                                String logStr = "";
                                recSocket.receive(packet);//受信& wait

                                //受信情報取得
                                InetAddress recInet = packet.getAddress();
                                User user = new User();
                                user.s受信時間 = getDateTimeString(false);//現在の日時を取得します。
                                user.hostName = recInet.getCanonicalHostName();
                                logStr += user.hostName; //相手接続先ホスト名

                                user.ip =recInet.getHostAddress();
                                logStr += ":" + user.ip;//IPアドレス

                                user.port = packet.getPort();
                                logStr += ":" + user.port;//ポート番号

                                int len = packet.getLength();//受信バイト数取得
                                String msg = new String(buf, 0, len, "MS932");  

                                logStr += "『" + msg + "』:" + len + "byte 受信 しました。";
                                user.msg = msg;//受信メッセージ
                                logStr += user.s受信時間 + System.getProperty("line.separator");

                                System.out.print(logStr);//ログ用文字列

                                this.logOs.write(logStr.getBytes());
                                this.logOs.flush();
                                user.do振る舞い();
                        }
                        catch (Exception e){
                                e.printStackTrace();
                                loop = false;
                        }
                }
                recSocket.close();
        }

        //現在の日時と時間を取得
        // 引数が、falseではれば「2009/10/24 10:52:58」の文字列で、
        // 引数が trueであれば、「2009_10_24_10_52_58」の文字列になる。
        static String getDateTimeString(boolean file){
                Calendar calendar = Calendar.getInstance();
                Date date = calendar.getTime();
                //java.text.DateFormat dataFormat =
                //      java.text.DateFormat.getDateTimeInstance();
                //String time = dataFormat.format(date);
                String time = date.toString();
                if(file){
                        time = time.replaceAll("[?/ :]","_");
                }
                return time;
        }

        public static void main(String[] args) throws Exception {
                //ローカルのInetAddress(IPアドレス)を取得
                InetAddress inet  = InetAddress.getLocalHost();

                System.out.println("IPアドレス:" + (IPアドレス = inet.getHostAddress()));
                ホスト名 = inet.getHostName();
                System.out.println("ホスト名:" +ホスト名);

                System.out.println("[end]で終了");
                UdpReply0 reply = new UdpReply0();
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                for(;;){
                        String s = br.readLine();
                        if(s.equals("end")) {
                                reply.loop = false;
                                System.exit(0);
                        }
                }
        }

}