pom.xml文件如下
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>cn.hadrongroupId>
<artifactId>p2partifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>p2pname>
<url>http://maven.apache.orgurl>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<dependencies>
<dependency>
<groupId>org.java-websocketgroupId>
<artifactId>Java-WebSocketartifactId>
<version>1.3.4version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.9version>
<scope>testscope>
dependency>
dependencies>
project>
package cn.hadron.p2p;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
/**
* p2p服务端
*
*/
public class P2PServer {
//所有服务端的WebSocket
private List sockets = new ArrayList();
public List getSockets() {
return sockets;
}
/**
* 初始化P2P Server端
* @param port :Server端的端口号
*/
public void initServer(int port) {
/**
* 创建有一个WebSocket的服务端
* 定义了一个WebSocketServer的匿名子类对象socketServer
* new InetSocketAddress(port)是WebSocketServer构造器的参数
* InetSocketAddress是(IP地址+端口号)类型,也就是端口地址类型
*/
final WebSocketServer socketServer = new WebSocketServer(new InetSocketAddress(port)) {
/**
* 重写5个事件方法,事件发生时触发该方法
*/
@Override
public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake){//创建连接成功时触发
write(webSocket, "服务端开打");
//当成功创建一个WebSocket连接时,将该链接加入连接池
sockets.add(webSocket);
}
@Override
public void onClose(WebSocket webSocket, int i, String s, boolean b) {//断开连接时候触发
System.out.println(webSocket.getRemoteSocketAddress()+"客户端与服务器断开连接!");
//当客户端断开连接时,WebSocket连接池删除该链接
sockets.remove(webSocket);
}
@Override
public void onMessage(WebSocket webSocket, String msg) {//收到客户端发来消息的时候触发
System.out.println("接收到客户端消息:" + msg);
write(webSocket, "收到消息");
}
@Override
public void onError(WebSocket webSocket, Exception e) {//连接发生错误的时候调用,紧接着触发onClose方法
System.out.println(webSocket.getRemoteSocketAddress()+"客户端链接错误!");
sockets.remove(webSocket);
}
@Override
public void onStart() {
System.out.println("WebSocket Server端启动...");
}
};
socketServer.start();
System.out.println("监听socketServer端口" + port);
}
/**
* 向客户端发送消息
* @param ws
* @param message
*/
public void write(WebSocket ws, String message) {
System.out.println("发送给" + ws.getRemoteSocketAddress().getPort() + "的p2p消息:" + message);
ws.send(message);
}
/**
* 向所有客户端广播消息
* @param message
*/
public void broatcast(String message) {
if (sockets.size() == 0) {
return;
}
System.out.println("======广播消息开始:");
for (WebSocket socket : sockets) {
this.write(socket, message);
}
System.out.println("======广播消息结束");
}
}
package cn.hadron.p2p;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
/**
* p2p客户端
*
*/
public class P2PClient {
//所有客户端WebSocket的连接池
private List sockets = new ArrayList();
public List getSockets() {
return sockets;
}
/**
* 连接到peer
*/
public void connectPeer(String peer) {
try {
/**
* The WebSocketClient is an abstract class that expects a valid "ws://" URI to connect to.
* When connected, an instance recieves important events related to the life of the connection.
* A subclass must implement onOpen, onClose, and onMessage to be useful.
* An instance can send messages to it's connected server via the send method.
*
* 创建有一个WebSocket的客户端
*/
final WebSocketClient socketClient = new WebSocketClient(new URI(peer)) {
@Override
public void onOpen(ServerHandshake serverHandshake) {
write(this, "客户端打开");
sockets.add(this);
}
@Override
public void onMessage(String msg) {
System.out.println("收到服务端发送的消息:" + msg);
}
@Override
public void onClose(int i, String msg, boolean b) {
System.out.println("客户端关闭");
sockets.remove(this);
}
@Override
public void onError(Exception e) {
System.out.println("客户端报错");
sockets.remove(this);
}
};
//客户端 开始连接服务端
socketClient.connect();
} catch (URISyntaxException e) {
System.out.println("连接错误:" + e.getMessage());
}
}
/**
* 向服务端发送消息
* 当前WebSocket的远程Socket地址,就是服务器端
* @param ws:
* @param message
*/
public void write(WebSocket ws, String message) {
System.out.println("发送给" + ws.getRemoteSocketAddress().getPort() + "的p2p消息:" + message);
ws.send(message);
}
/**
* 向所有服务端广播消息
* @param message
*/
public void broatcast(String message) {
if (sockets.size() == 0) {
return;
}
System.out.println("======广播消息开始:");
for (WebSocket socket : sockets) {
this.write(socket, message);
}
System.out.println("======广播消息结束");
}
}
package cn.hadron.p2p;
/**
* P2P网络中每个节点即是服务端又是客户端
*/
public class Main {
public static void main(String[] args) {
if(args==null||args.length==0){
System.out.println("主方法没有参数!");
return;
}
P2PServer p2pServer = new P2PServer();
P2PClient p2pClient = new P2PClient();
int p2pPort = Integer.valueOf(args[0]);
// 启动p2p服务端
p2pServer.initServer(p2pPort);
if (args.length == 2 && args[1] != null) {
// 作为p2p客户端连接p2p服务端
p2pClient.connectPeer(args[1]);
}
}
}
基本思路,我们通过程序运行在不同端口号来模拟不同节点的P2P网络通信,也就是说一个进行看做一个节点
(1)peer1命名
(2)peer1参数配置
peer1作为Server端运行在7001端口
(3)运行peer1
peer1节点输出
(1)创建peer2
(2)peer2参数配置
P2P网络中的第2个节点peer2即作为Server又作为Client,作为Server运行在7002端口,同时作为Client通过ws://localhost:7001连接到peer1
(3)peer2节点输出
输出说明:
首尾两行是peer2作为Sever端输出,中间三行是作为Client端输出。下面针对Client端输出进行分析:
发送给7001的p2p消息:客户端打开
收到服务端发送的消息:服务端开打
收到服务端发送的消息:收到消息
当Client端执行connect()方法时,peer2成功连接到peer1触发连接两端的(Server端和Client端)的onOpen方法。
write(this, "客户端打开")
语句,发送消息到Server端,因此输出发送给7001的p2p消息:客户端打开
。客户端打开
,触发onMessage方法,并回复Client端收到消息
,因此Client端输出收到服务端发送的消息:收到消息
。write(webSocket, "服务端开打")
语句,发送消息到Client端,因此输出收到服务端发送的消息:服务端开打
。注意:Server端的onOpen方法先于Server端onMessage方法。
(4)查看peer1输出变化
Console切换到peer1
发现多出了3行信息:
发送给51290的p2p消息:服务端开打
接收到客户端消息:客户端打开
发送给51290的p2p消息:收到消息
当peer2成功连接到peer1时,连接成功打开,触发连接两端(Server端和Client端)的onOpen方法。
write(webSocket, "服务端开打")
语句,输出发送给51290的p2p消息:服务端开打
。write(this, "客户端打开")
,发送消息到Server端,Server端的onMessage方法被触发,输出1行接收到客户端消息:客户端打开
。发送给51290的p2p消息:收到消息
。(1)配置peer3
P2P网络中的第3个节点peer3即作为Server又作为Client,作为Server运行在7003端口,同时作为Client通过ws://localhost:7002连接到peer2