您的位置:首页 > 其它

利用NIO建立Socket服务器

2013-11-19 10:43 295 查看


利用NIO建立Socket服务器

2011-12-07 17:05 tonlion2046 tonlion2046的博客 我要评论(0) 字号:T | T



Socket的Channel在Selector上注册某一种动作,Selector通过select操作,监视所有在该Selector注册过的Channel的对应的动作,如果监测到某一对应的动作,则返回selectedKeys,自己手动取到各个SelectionKey进行相应的处理。当然NIO不仅可以接受Socket的Channel,还有文件操作等其他IO操作。

AD: 2013云计算架构师峰会课程资料下载

传统的Java 的IO,利用Socket建立服务器,接收客户端连接,一般都是为每一个连接建立一个线程,如果连接数巨大,那么服务器开销也将巨大。。NIO的原理,可以参照图:





Socket的Channel在Selector上注册某一种动作,Selector通过select操作,监视所有在该Selector注册过的Channel的对应的动作,如果监测到某一对应的动作,则返回selectedKeys,自己手动取到各个SelectionKey进行相应的处理。当然NIO不仅可以接受Socket的Channel,还有文件操作等其他IO操作。

作业的要求:

使用socket编程实现一个简单的文件服务器。客户端程序实现put功能(将一个文件从本地传到文件服务器)和get功能(从文件服务器取一远程文件存为本地文件)。客户端和文件服务器不在同一台机器上。
put [-h hostname] [-p portname] local_filename remote_filename
get [-h hostname] [-p portname] remote_filename local_filename


服务器端不使用nio,直接使用io的socket代码如下:
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();
}
}
}


使用NIO建立的Socket服务器,代码如下:
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();
}

}
}


客户端代码如下:
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class ClientMain {

private   int ServerPort = 9527;
private   String ServerAddress = "192.168.1.154";
private   String GetOrPut = "get";
private   String local_filename = "";
private   String remote_filename  = "";
private   byte[] buf;
private   int len;
class SocketThread extends Thread{

@Override
public void run() {
try {

File file = new File("C:\\",local_filename); //假设文件放在C盘
if(!file.exists()&&GetOrPut.equals("put")){
System.out.println("本地没有这个文件,无法上传!");
return;
}

InetAddress loalhost = InetAddress.getLocalHost();
Socket socket = new Socket(ServerAddress,ServerPort,loalhost,44);
//服务器IP地址  端口号   本机IP 本机端口号
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());

//dos.writeUTF(GetOrPut+" "+remote_filename);//服务器端如果是io的socket,writeUTF和writeUTF对接
dos.write((GetOrPut+" "+remote_filename).getBytes());
dos.flush();

//String tempString = dis.writeUTF();
buf = new byte[1024];
len = dis.read(buf);
String tempString = new String(buf,0,len);//服务器反馈的信息

//System.out.println(tempString);
if(tempString.equals("notexists")){
System.out.println("服务器没有这个文件,无法下载!");
dos.close();
dis.close();
socket.close();
return;
}

if(tempString.startsWith("准备下载")){
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();
dos.close();
dis.close();
socket.close();
}
else if(tempString.equals("UploadReady")){
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();
System.out.println("上传完毕!");
fis.close();
dis.close();
dos.close();
socket.close();
}

} catch (Exception e) {
e.printStackTrace();
}
}

}

public boolean checkCommand(String command)
{
if(!command.startsWith("put")&&!command.startsWith("get")){
System.out.println("输入命令错误");
return false;
}

int index = -1;
String temp = "";
String[] tempStrings = null;

if((index=command.indexOf("-h"))>0){
temp = command.substring(index+3);
temp = temp.substring(0, temp.indexOf(' '));
ServerAddress = temp;
}
if((index=command.indexOf("-p"))>0){
temp = command.substring(index+3);
temp = temp.substring(0, temp.indexOf(' '));
ServerPort = Integer.valueOf(temp);
}

tempStrings = command.split(" ");
if(command.startsWith("put")){
GetOrPut = "put";
local_filename = tempStrings[tempStrings.length-2];
remote_filename = tempStrings[tempStrings.length-1];
}
else if(command.startsWith("get")){
GetOrPut = "get";
local_filename = tempStrings[tempStrings.length-1];
remote_filename = tempStrings[tempStrings.length-2];
}

return true;
}

public static void main(String[] args) {
ClientMain thisC= new ClientMain();
Scanner sc = new Scanner(System.in);
String commandString = "";
do {
System.out.println("请输入命令:");
commandString = sc.nextLine();
} while (!thisC.checkCommand(commandString));

ClientMain.SocketThread a = thisC.new SocketThread();
a.start();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: