Java Nio的Socket服务端编写

攻城尸 2015-04-01 02:47:02
最近项目中需要用到Nio的socket技术,在网上看了很多nio的示例,但是都是懵懵懂懂,可能是个人理解能力太差,但是抱着一颗好学的心,请教各位大神。话不多说,直接贴代码
Nio socket 服务端代码:


public class ServerSocket {
// 缓冲区大小
private static final int BufferSize=1024;

// 超时时间,单位毫秒
private static final int TimeOut=3000;

// 本地监听端口
private static final int ListenPort=1978;

public static void main(String[] args) throws IOException{
// 创建选择器
Selector selector=Selector.open();

// 打开监听信道
ServerSocketChannel listenerChannel=ServerSocketChannel.open();

// 与本地端口绑定
listenerChannel.socket().bind(new InetSocketAddress(ListenPort));

// 设置为非阻塞模式
listenerChannel.configureBlocking(false);

// 将选择器绑定到监听信道,只有非阻塞信道才可以注册选择器.并在注册过程中指出该信道可以进行Accept操作
listenerChannel.register(selector, SelectionKey.OP_ACCEPT);

// 创建一个处理协议的实现类,由它来具体操作
TCPProtocol protocol=new TCPProtocolImpl(BufferSize);

// 反复循环,等待IO
while(true){
// 等待某信道就绪(或超时)
if(selector.select(TimeOut)==0){
System.out.print("独自等待.");
continue;
}

// 取得迭代器.selectedKeys()中包含了每个准备好某一I/O操作的信道的SelectionKey
Iterator<SelectionKey> keyIter=selector.selectedKeys().iterator();

while(keyIter.hasNext()){
SelectionKey key=keyIter.next();

try{
if(key.isAcceptable()){
// 有客户端连接请求时
protocol.handleAccept(key);
}

if(key.isReadable()){
// 从客户端读取数据
protocol.handleRead(key);
}

if(key.isValid() && key.isWritable()){
// 客户端可写时
protocol.handleWrite(key);
}
}
catch(IOException ex){
// 出现IO异常(如客户端断开连接)时移除处理过的键
keyIter.remove();
continue;
}

// 移除处理过的键
keyIter.remove();
}
}
}

}


实现类的代码:

public class TCPProtocolImpl implements TCPProtocol {
private int bufferSize;

public TCPProtocolImpl(int bufferSize){
this.bufferSize=bufferSize;
}

public void handleAccept(SelectionKey key) throws IOException {
SocketChannel clientChannel=((ServerSocketChannel)key.channel()).accept();
clientChannel.configureBlocking(false);
clientChannel.register(key.selector(), SelectionKey.OP_READ,ByteBuffer.allocate(bufferSize));
}

public void handleRead(SelectionKey key) throws IOException {
// 获得与客户端通信的信道
SocketChannel clientChannel=(SocketChannel)key.channel();

// 得到并清空缓冲区
ByteBuffer buffer=(ByteBuffer)key.attachment();
buffer.clear();

// 读取信息获得读取的字节数
long bytesRead=clientChannel.read(buffer);

if(bytesRead==-1){
// 没有读取到内容的情况
clientChannel.close();
}
else{
// 将缓冲区准备为数据传出状态
buffer.flip();

// 将字节转化为为UTF-16的字符串
String receivedString=Charset.forName("UTF-16").newDecoder().decode(buffer).toString();

// 控制台打印出来
System.out.println("接收到来自"+clientChannel.socket().getRemoteSocketAddress()+"的信息:"+receivedString);

// 准备发送的文本
String sendString="你好,客户端. @"+new Date().toString()+",已经收到你的信息"+receivedString;
buffer=ByteBuffer.wrap(sendString.getBytes("UTF-16"));
clientChannel.write(buffer);

// 设置为下一次读取或是写入做准备
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
}

public void handleWrite(SelectionKey key) throws IOException {
// do nothing
}
}



我的问题是,为什么当监听程序检查到客户端请求时,会走handleAccept这个函数,这个函数中的三句话不是在服务端的开始就写过了嘛,请各位大神不要计较我的小白,指点一下,或者帮讲解一下nio socket的原理
...全文
301 4 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
oh_Maxy 2015-04-01
  • 打赏
  • 举报
回复
handleAccept 在TCPProtocol中定义、TCPProtocolImpl中实现,在ServerSocket中使用,大部分方法不都是这样的么,先定义,再使用?
谁伴我闯荡ING 2015-04-01
  • 打赏
  • 举报
回复
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.Iterator; public class NewSocketServer { private static final int port = 9527; private Selector selector; private ByteBuffer clientBuffer = ByteBuffer.allocate(1024); private CharsetDecoder decoder = Charset.forName("GB2312").newDecoder(); private CharsetEncoder encoder = Charset.forName("GB2312").newEncoder(); //编码解码格式设置成GBK也行.UTF-8不行,中文乱码 (前提都是客户端没有设置任何编码解码格式) public void setListener() throws Exception{ selector = Selector.open(); //打开选择器 ServerSocketChannel server = ServerSocketChannel.open(); //定义一个 ServerSocketChannel通道 server.socket().bind(new InetSocketAddress(port)); //ServerSocketChannel绑定端口 server.configureBlocking(false); //配置通道使用非阻塞模式 server.register(selector, SelectionKey.OP_ACCEPT); //该通道在selector上注册 接受连接的动作 while(true) { selector.select(); //select() 会阻塞,直到在该selector上注册的channel有对应的消息读入 Iterator iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey key = (SelectionKey) iter.next(); iter.remove(); // 删除此消息 process(key); // 当前线程内处理。(为了高效,一般会在另一个线程中处理此消息) } } } private void process(SelectionKey key) throws IOException { if (key.isAcceptable()) { // 接收请求 ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel channel = server.accept();//类似于io的socket,ServerSocketChannel的accept函数返回 SocketChannel channel.configureBlocking(false); //设置非阻塞模式 SelectionKey sKey = channel.register(selector, SelectionKey.OP_READ); sKey.attach("read_command"); //这儿接收到连接请求之后可以为每个连接设置一个ID } else if (key.isReadable()) { // 读信息 SocketChannel channel = (SocketChannel) key.channel(); String name = (String) key.attachment(); if(name.equals("read_command")){ int count = channel.read(clientBuffer); if (count > 0) { clientBuffer.flip(); CharBuffer charBuffer = decoder.decode(clientBuffer); String command = charBuffer.toString(); //command形如:get abc.png 或者 put aaa.png System.out.println("command===="+command); //得到客户端传来的命令 String[] temp =command.split(" "); command = temp[0]; //命令 是put还是get String filename = temp[1]; //文件名 SelectionKey sKey = channel.register(selector,SelectionKey.OP_WRITE); if(command.equals("put"))sKey.attach("UploadReady#"+filename); //要保护该通道的文件名 else if(command.equals("get")){ if(!new File("C:\\",filename).exists()){ //假设文件都是在C盘根目录 System.out.println("没有这个文件,无法提供下载!"); sKey.attach("notexists"); } else sKey.attach("DownloadReady#"+filename); //要保护该通道的文件名 } } else { channel.close(); } } else if(name.startsWith("read_file")){//这儿可以新开一个线程 文件操作也可以用NIO DataOutputStream fileOut = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( new File("C:\\",name.split("#")[1])))); int passlen = channel.read(clientBuffer); while (passlen>=0) { clientBuffer.flip(); fileOut.write(clientBuffer.array(), 0, passlen); passlen = channel.read(clientBuffer); } System.out.println("上传完毕!"); fileOut.close(); channel.close(); } clientBuffer.clear(); } else if (key.isWritable()) { // 写事件 SocketChannel channel = (SocketChannel) key.channel(); String flag = (String) key.attachment(); if(flag.startsWith("downloading")){//这儿可以新开一个线程 文件操作也可以用NIO DataInputStream fis = new DataInputStream( new BufferedInputStream( new FileInputStream( new File("C:\\",flag.split("#")[1])))); byte[] buf = new byte[1024]; int len =0; while ((len = fis.read(buf))!= -1) { channel.write(ByteBuffer.wrap(buf, 0, len)); } fis.close(); System.out.println("文件传输完成"); channel.close(); } else if(flag.equals("notexists")){ //channel.write(encoder.encode(CharBuffer.wrap(flag))); channel.write(ByteBuffer.wrap(flag.getBytes())); //不用编码也行 客户端直接接收 中文也不是乱码 channel.close(); } else if(flag.startsWith("UploadReady")){ channel.write(encoder.encode(CharBuffer.wrap("UploadReady"))); //这儿如果不重新注册该通道的读操作 selector选择到该通道的将继续永远是写操作,也就无法跳转到上面的接受上传的处理 SelectionKey sKey =channel.register(selector, SelectionKey.OP_READ);//register是覆盖的????!!! sKey.attach("read_file#"+flag.split("#")[1]); //key.attach("read_file#"+flag.split("#")[1]); //select不到读操作 } else if(flag.startsWith("DownloadReady")){ channel.write(ByteBuffer.wrap("准备下载".getBytes())); //channel.write(encoder.encode(CharBuffer.wrap("准备下载"))); key.attach("downloading#"+flag.split("#")[1]); } } } public static void main(String[] args) { try { System.out.println("等待来至" + port + "端口的客户端连接....."); new NewSocketServer().setListener(); } catch (Exception e) { e.printStackTrace(); } } }
谁伴我闯荡ING 2015-04-01
  • 打赏
  • 举报
回复
如果非要贴代码,我只能帮你找个给你借鉴(我只会C#和nodejs写socket,java不会写): import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class ServerMain { public static void main(String[] args) { class SocketThread extends Thread{ private Socket socket; private byte[] buf; private int len = 0; public SocketThread(Socket socket) { this.socket = socket; buf = new byte[1024]; } @Override public void run() { try { DataInputStream dis = new DataInputStream(socket.getInputStream()); DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); //String command = dis.readUTF(); len = dis.read(buf); String command = new String(buf,0,len); System.out.println("command=="+command); String[] temp =command.split(" "); command = temp[0]; //命令 是put还是get String filename = temp[1]; //文件名 File file = new File("C:\\",filename);//假设放在C盘 if(command.equals("get")){ if(!file.exists()){ //dos.writeUTF("notexists"); dos.write("notexists".getBytes()); dos.flush(); System.out.println("没有这个文件,无法提供下载!"); dis.close(); dos.close(); socket.close(); return; } //dos.writeUTF("DownloadReady "+file.length()); dos.write("准备下载".getBytes()); dos.flush(); System.out.println("正在接受文件下载..."); DataInputStream fis = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); while ((len = fis.read(buf))!= -1) { dos.write(buf, 0, len); } dos.flush(); fis.close(); System.out.println("文件传输完成"); } else { //dos.writeUTF("UploadReady"); dos.write("UploadReady".getBytes()); dos.flush(); System.out.println("正在接受文件上传..."); DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); while ((len = dis.read(buf))!=-1) { fileOut.write(buf, 0, len); } System.out.println("上传完毕!"); fileOut.close(); } dis.close(); dos.close(); socket.close(); } catch (Exception e) { e.printStackTrace(); } } } System.out.println("等待客户端连接...."); int index = 0; try { ServerSocket server = new ServerSocket(9527,300); //端口号9527 允许最大连接数300 while (true) { Socket socket = server.accept(); System.out.println("收到第"+(++index)+"个连接"); new SocketThread(socket).start(); //对每个连接创建一个线程 } } catch (Exception e) { e.printStackTrace(); } } }
谁伴我闯荡ING 2015-04-01
  • 打赏
  • 举报
回复
我的java还停留在n年前教科书阶段; 不过我知道nio和socket是两个东西,换而言之io也能实现socket服务端。 socket思想大抵都是通的,注册》订阅》触发》广播,期间维护一个消息队列,针对不同客户端的订阅实现不同的消息分发机制。

5,657

社区成员

发帖
与我相关
我的任务
社区描述
Web开发应用服务器相关讨论专区
社区管理员
  • 应用服务器社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧