这几天,研究网络和分布式——我想弄一个可自由扩展的高效率的围棋系统。
有几个问题是要必须得解决的:
1,协议的设计,也就是通信
2,服务器端的架构,比如是阻塞还是事件io之类的支持,包括集群
3,基本网络架构的选择,java nio的框架,实在是有点复杂。
我参考了几个东西:
1,jboss netty一个很不错的基础框架,很干净,我很喜欢
2,apache mina和jboss netty有亲戚,不过我觉得有点乱
3,jcsp这个东西很好,让我对于通讯和计算有了更加深入一点的了解,很漂亮的模型(CSP)
4,java jini,以前我就看过jini实在是那时候,水平太低,没明白,现在大概清楚它是啥东西了——比较高层,sun真的比较超前
5,erlang,我一直犹豫,是不是该用erlang一个搞定所有的问题得了,但是,erlang把你限定的太死了,你没了选择,当然,它很好,直到现在,我还是一头雾水。
我用jboss netty做了一个聊天的程序(主要是测试一下netty的功能),我发现很简单,可是,对于协议的设计,我始终不够自信,我觉得肯定还有什么问题。我的现在的方法只是单向消息对象,只是发消息,收消息,处理消息,其他的一概不理。我觉得不够严谨,到底应该怎么做,我不知道。
现在把代码贴一下,做个纪念吧!
这个程序主要参照了jboss netty里的一个securechat的例子,我去掉了secure的功能,同时加上了swing界面。
package orange;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
/**
* swing 的client!
*/
public class OrangeClient {
private JTextField input=new JTextField(20);
private JTextField output=new JTextField(20);
OrangeClientHandler handler;
public OrangeClient(){
String host="localhost";
int port=8081;
ChannelFactory factory=
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ClientBootstrap bootstrap=new ClientBootstrap(factory);
handler=new OrangeClientHandler(output);
bootstrap.setPipelineFactory(new OrangePipelineFactory(handler));
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);
bootstrap.connect(new InetSocketAddress(host, port));
}
public void init(){
JFrame frame=new JFrame("Orange Client!");
Container container=frame.getContentPane();
frame.addWindowListener(new WindowAdapter(){
@Override
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
JPanel pInput=new JPanel();
pInput.setLayout(new FlowLayout());
JLabel inputLabel=new JLabel("input:");
JButton inputButton=new JButton("To Server!");
inputButton.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
Channel mych=handler.getMyChannel();
if(mych.isOpen()){
ChannelFuture f=mych.write(input.getText()+'\n');
f.addListener(new ChannelFutureListener(){
@Override
public void operationComplete(ChannelFuture future)
throws Exception {
System.out.println("ok!");
}
});
}
}
});
pInput.add(inputLabel);
pInput.add(input);
pInput.add(inputButton);
JPanel pOutput=new JPanel();
pOutput.setLayout(new FlowLayout());
JLabel outputLabel=new JLabel("output:");
pOutput.add(outputLabel);
pOutput.add(output);
container.setLayout(new BorderLayout());
container.add(BorderLayout.NORTH,pInput);
container.add(BorderLayout.SOUTH,pOutput);
frame.setLocation(400, 400);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
OrangeClient client=new OrangeClient();
client.init();
}
}
package orange;
import javax.swing.JTextField;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipelineCoverage;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
* client handler
*/
@ChannelPipelineCoverage("one")
public class OrangeClientHandler extends SimpleChannelHandler {
private Channel myChannel;
private JTextField myout;
public OrangeClientHandler(JTextField out){
this.myout=out;
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
setMyChannel(e.getChannel());
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
String data=(String) e.getMessage();
myout.setText(data);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getChannel().close();
}
public Channel getMyChannel() {
return myChannel;
}
public void setMyChannel(Channel myChannel) {
this.myChannel = myChannel;
}
}
package orange;
import static org.jboss.netty.channel.Channels.pipeline;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.handler.codec.serialization.ObjectDecoder;
import org.jboss.netty.handler.codec.serialization.ObjectEncoder;
/**
*这个PipelineFactory很重要,它定义了用serialize object通讯
*定义了编码和解码,以及client 和 server的handler的指定
*/
public class OrangePipelineFactory implements ChannelPipelineFactory {
private ChannelHandler handler;
public OrangePipelineFactory(ChannelHandler handler){
this.handler=handler;
}
@Override
public ChannelPipeline getPipeline(){
ChannelPipeline pipeline = pipeline();
pipeline.addLast("decoder", new ObjectDecoder());//up
pipeline.addLast("encoder", new ObjectEncoder());//down
pipeline.addLast("myhandler", handler);//my handler include up and down
return pipeline;
}
}
package orange;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
/**
* server
*/
public class OrangeServer {
public static void main(String[] args) throws Exception{
ChannelFactory factory=
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ServerBootstrap bootstrap=new ServerBootstrap(factory);
OrangeServerHandler handler=new OrangeServerHandler();
bootstrap.setPipelineFactory(new OrangePipelineFactory(handler));
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive",true);
bootstrap.bind(new InetSocketAddress(8081));
}
}
package orange;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipelineCoverage;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
*server side handler,它可以说是最主要的处理事情的地方,我现在只是把所有的channel
*放在集合里,有对象来的时候,就告诉大家
*/
@ChannelPipelineCoverage("all")
public class OrangeServerHandler extends SimpleChannelHandler {
static final Set<Channel> channels =Collections.synchronizedSet(new HashSet<Channel>());
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
Channel ch=e.getChannel();
channels.remove(ch);
ch.close();
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
// Convert to a String first.
String request = (String) e.getMessage();
// Send the received message to all channels but the current one.
for (Channel c: channels) {
if (c != e.getChannel()) {
c.write("[" + e.getChannel().getRemoteAddress() + "] " +
request + '\n');
}
}
// Close the connection if the client has sent 'bye'.
if (request.toLowerCase().equals("bye")) {
e.getChannel().close();
}
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
channels.add(e.getChannel());
}
}
我知道我又回到了老地方,我在不断的绕着圈子,不过这个圈子是螺旋上升的。
每一次,我都会在稍微高一点的地方看原来的问题,这也许就是生命智力的有限和无奈啊!
如果我一开始就可以看穿这一切,那么,我不就可以省了很多的功夫了吗?!