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

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();
- }
- }

评论
- int count = channel.read(clientBuffer);
- if (count > 0) {
- //命令解释
- }
channel.read(clientBuffer) 有可能接收到命令字节的一部分,命令解释会出问题
用nio写的server这段。
while (passlen>=0) {
clientBuffer.flip();
fileOut.write(clientBuffer.array(), 0, passlen);
passlen = channel.read(clientBuffer);
}
应嘎是这样的逻辑:
while (passlen>=0) {
if(passlen!=0) {
clientBuffer.flip();
}
fileOut.write(clientBuffer.array(), 0, passlen);
passlen = channel.read(clientBuffer);
}
这样传大文件也不都会错了。
利用NIO建立Socket服务器的更多相关文章
- 在Windows2008系统中利用IIS建立FTP服务器
一.服务器管理器 1.2008的系统使用服务器管理器,选择角色,因为我之前已经开启了IIS服务器角色,所以我现在只要添加角色服务即可,如果你没有开启过的话,直接添加角色即可. 2.选择WEB服 ...
- Java NIO 非阻塞Socket服务器构建
推荐阅读IBM developerWorks中NIO的入门教程,尤其是对块I/O和流I/O不太清楚的开发者. 说到socket服务器,第一反应是java.net.Socket这个类.事实上在并发和响应 ...
- Socket 基础解析使用ServerSocket建立聊天服务器
很简单的教程哦! 1.socket 简介 Socket 又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求.ServerSocket 用于 ...
- 利用ScktSrvr打造多功能Socket服务器
Socket服务端编程中最重要的也是最难处理的工作便是客户请求的处理和数据的接收和发送,如果每一个Socket服务器应用程序的开发都要从头到尾处理这些事情的话,人将会很累,也会浪费大量时间.试想,如果 ...
- 利用connect建立前端开发服务器
利用connect建立前端开发服务器 对于前后端完全分离的系统,开发时候我们需要给前端配置服务器,当然我们可以选择Nginx之类的服务器进行配置,但我们也能使用NodeJS构建高自由度的前端开发服务器 ...
- java的nio之:java的bio流下实现的socket服务器同步阻塞模型和socket的伪异步的socket服务器的通信模型
同步I/O模型的弊端===>每一个线程的创建都会消耗服务端内存,当大量请求进来,会耗尽内存,导致服务宕机 伪异步I/O的弊端分析===>当对Socket的输入流进行读取操作的时候,它会一直 ...
- 从Jetty、Tomcat和Mina中提炼NIO构架网络服务器的经典模式(一)
本文转载自 http://blog.csdn.net/cutesource/article/details/6192016 如何正确使用NIO来构架网络服务器一直是最近思考的一个问题,于是乎分析了一下 ...
- 利用Delphi编写Socket通信程序
一.Delphi与Socket 计算机网络是由一系列网络通信协议组成的,其中的核心协议是传输层的TCP/IP和UDP协议.TCP是面向连接的,通信双方保持一条通路,好比目前的电话线,使用telnet登 ...
- 品味性能之道<九>:利用Loadrunner编写socket性能测试脚本简述
一.概述 Loadrunner拥有极为丰富的工具箱,供予我们制造出各种奇妙魔法的能力.其中就有此次要讨论的socket套接字操作. 二.socket概述 ...
随机推荐
- 【随记】VS异常:HRESULT: 0x80070057 (E_INVALIDARG)) 解决方案
今天公司突然断电后,来电重启VS,调试WebService时报错: 未能加载文件或程序集 “XXX” 或它的某一个依赖项.系统找不到指定的文件. 说明: 执行当前 Web 请求期间,出现未处理的异常 ...
- js 表达式与运算符 详解(下)
比较运算符: > .>= .<. <=. ==. !=. ===. !==. 比较运算符的结果都为布尔值 ==只比较值是否相等 而 ===比较的是值和数据类型都要 ...
- 织梦dedecms后台发布文章不自动更新首页与栏目列表页
dedecms发文章不自动更新首页也列表页解决办法如下: 登陆dedecms后台,找到“系统”“系统基本参数”“性能选项”,把“arclist标签调用缓存”设置成0,然后把“发布文章后马上更新网站主页 ...
- iOS开发之——巧用反射机制
1.应用场景——自定义UITabBarController的TabBar视图 (1)隐藏TabBar视图 一般我们选择自定义TabBar视图有两种方式.1是将tabBar视图隐藏;2是将TabBar视 ...
- jQuery常用特效插件汇总
jquery的CDN引用及下载地址 http://www.bootcdn.cn/jquery/ 1:semantictabs.js可以简单地制作Tabs菜单2:tabBox.js可以非常简单方便地 ...
- [Forward]Use the SharePoint My Tasks Web Part outside of My Sites
from http://yalla.itgroove.net/2014/04/use-sharepoint-tasks-web-part-outside-sites/ Use the SharePoi ...
- Knight Tournament
Codeforces Round #207 (Div. 1) A:http://codeforces.com/problemset/problem/356/A 题意:给你n匹马,然后有m场比赛.每场比 ...
- Hibernate如何一个类映射两个表
一个User类有username,password属性,还有 otherInformation等其他属性,username和password映射到一个表,otherInformation等其他属性映射 ...
- 6.ListView
Repeater一般只用来展示数据,如果要增删改则用ListView更方便,使用向导(强类型数据)来使用ListView会自动生成很多模板,免去手写模板代码的麻烦,再进行手工调整即可. 首先设定数据源 ...
- 执行计划中常见index访问方式(转)
近期有朋友对于单个表上的index各种情况比较模糊,这里对于单个表上,单个index出现的大多数情况进行了总结性测试,给出了测试结果,至于为什么出现这样的试验结果未做过多解释,给读者留下思考的空间.本 ...