<後述するような中継サーバー(P2PTracker)を試してみましょう>
ルータの制限
なぜだ? UDPならばできそうなものだが?
「アドレスとポートから送り先をたどれない」ならば、そもそもUDPでの通信ができないではないか?そもそも、UDPはTCPと違いコネクションを持ちません。UDPは通信相手もUDPパケットに含まれるアドレスとポート番号を頼りにして相手と通信をします。
ですから、「UDPパケットを受け取ったサーバー」と「そのサーバーからパケット情報をもらったクライアント」では、差が無いように思えます。
残念ながら、ルータによって制限がかけられています。
残念ながらルータには制限がかけられている場合があります。UDPの通信の仕組みはどうあれ、「UDPで送ったパケットを返信するのは、送った先の端末から」なのです。ルータ制作者の立場にたってみれば、それ以外を想定する必要はないでしょう。
例えば、通信した「相手のアドレス」意外は不正な通信の可能性が高いとみなして、パケットを破棄する。(制限付きNAT)
例えば、「通信した相手のポート番号」意外は不正な通信の可能性が高いと見なして、パケットを破棄する。
※ 制限している訳では無くて、効率の問題でそうなっているだけかも知れません。
そもそも、UDPの本来の目的を満たすだけならば、送信相手によって、アドレスとポートを変えても良いでしょう。(シンメトリックNAT)
まとめ/次回予告
UDPに制限がある事を理解して頂けたことでしょう。 次回は、「制限を突破する方法」または、「制限の種類を特定する方法」について解説して行きたいと思います。
サンプルコード
○中継サーバー
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.LinkedList;
public class P2PTracker {
public static void main(String[] args) {
P2PTracker tracker;
try {
tracker = new P2PTracker();
tracker.run();
} catch (IOException e) {
e.printStackTrace();
}
}
private LinkedList mPeerList = new LinkedList<>();
private DatagramSocket mSocket= null;
public P2PTracker() throws IOException {
mSocket = new DatagramSocket(8080);
}
public void run() {
try {
do {
DatagramPacket packet = receive();
requestPacket(packet.getAddress().getHostAddress(), packet.getPort());
} while(true);
} catch (IOException e) {
e.printStackTrace();
}
}
public DatagramPacket receive() throws IOException {
byte[] buf = new byte[1024];
DatagramPacket packet= new DatagramPacket(buf,buf.length);
mSocket.receive(packet);
PeerInfo info = new PeerInfo();
info.mHost = packet.getAddress().getHostName();
info.mPort = packet.getPort();
if(!mPeerList.contains(info)) {
mPeerList.addFirst(info);
if(mPeerList.size() > 50) {
mPeerList.removeLast();
}
}
return packet;
}
public void requestPacket(String host, int port) throws UnknownHostException, IOException {
byte[] buf = getPeerInfoList().toString().getBytes();
DatagramPacket packet= new DatagramPacket(
buf, buf.length,
Inet4Address.getByName(host), port);
mSocket.send(packet);
}
public String getPeerInfoList() {
StringBuilder buider = new StringBuilder();
for(PeerInfo info : mPeerList) {
buider.append(""+info.mHost+":"+info.mPort+",");
}
System.out.println("#s#"+buider.toString());
return buider.toString();
}
public static class PeerInfo {
public String mHost = "";
public int mPort = 0;
@Override
public boolean equals(Object obj) {
if(!(obj instanceof PeerInfo)) {
return false;
}
PeerInfo target = (PeerInfo)obj;
if(target.mHost.equals(mHost) && target.mPort == mPort) {
return true;
} else {
return false;
}
}
}
}
○クライアントの中継サーバと通信する部分
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
public class P2PClient {
public static void main(String[] args) {
P2PClient client = new P2PClient("xx.xx.xx.xx");
client.run(8081);
}
private String mHost = "127.0.0.1";
public P2PClient(String host) {
mHost = host;
}
public void run(int port) {
try {
clientAction(port);
} catch (IOException e) {
e.printStackTrace();
}
}
public void clientAction(int port) throws IOException {
DatagramSocket socket = new DatagramSocket(port);
byte[] buf = new byte[1024];
DatagramPacket sendPacket= new DatagramPacket(
buf,buf.length,
Inet4Address.getByName(mHost),
8080);
socket.send(sendPacket);
DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);
socket.receive(receivePacket);
System.out.println("#cl#"+new String(receivePacket.getData()));
socket.close();
}
}
....
...
..
..
0 件のコメント:
コメントを投稿