一.socket编程
socket是最基础的网络通信服务,其他的可网络通信服务基本都是封装了socket通信,比如http通信,nio通信.作为最基本的通信协议,有两个要素,Socket,ServerSocket.比如:
服务器端
//打开ServerSocket,port自定义,int类型,一般大于1024
ServerSocket server = new ServerSocket(port);
//server尝试接收其他Socket的连接请求,这里的accept方法是阻塞式的,即没有连接会在这等待直至连接到来
Socket socket1 = server.accept();
客户端
//打开socket连接,其中server ip为服务器<em>ip,若是本机,则可以填入127.0.0.1,port为对应端口</em>
Socket socket2 = new Socket("server ip",port)
这样当服务器打开服务,客户端连接时会建立连接,而当服务器接收到连接后,可以使用如下方法获取输入流输出流
socket1.getInputStream();
socket1.getOutputStream();
同样客户端也可以通过以上方式获得输入输出流
socket2.getInputStream();
socket2.getOutputStream();
使用如下方法发送与接收
Writer writer = new OutputStreamWriter(socketi.getOutputStream());
writer.write("hello , I am hero"); //发送信息
Reader reader = new InputStreamReader(socketi.getInputStream());
char data[] = new char[1024];
reader.read(data); //接收信息
下面给一个完整的socket服务器与客户端程序
//服务器程序
public class Server {
static Socket socket = null;
static DataInputStream input = null;
static DataOutputStream output = null;
public static void main(String args[]) throws IOException {
ServerSocket server = new ServerSocket(8888);
while(true){
socket = server.accept();
System.out.print("wait for data\n");
input = new DataInputStream(socket.getInputStream());
String ret = input.readUTF();
// 处理客户端数据
System.out.println("receive content:" + ret);
if(ret.equals("exit"))
break;
//返回信息
System.out.print("write data back\n");
output=new DataOutputStream(socket.getOutputStream());
output.writeUTF("hello world");
}
input.close();
output.close();
socket.close();
server.close();
}
}
//客户端程序
public class client{
static Socket socket = null;
public static void main(String args[]) throws IOException{
try {
socket = new Socket("127.0.0.1",8888);
} catch (IOException e) {
e.printStackTrace();
}
//向服务器端发送数据
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
System.out.print("请输入: ");
String str = new BufferedReader(new InputStreamReader(System.in)).readLine();
out.writeUTF(str);
//接收服务器消息
DataInputStream input = new DataInputStream(socket.getInputStream());
String ret = input.readUTF();
System.out.println("接收消息为:"+ret);
}
}
二.IO、NIO、AIO区别
NIO即New IO的简称,是相对于IO而言的,并不是Non blocking IO的简称。但NIO确实是非阻塞方式的IO,AIO也是非阻塞方式的IO。
应用程序在处理IO的时候分为一般分为等待数据准备、数据从内核拷贝到应用程序。也就是这两个阶段才产生了 不同的IO方式。
首先用一个场景来描述IO、NIO、AIO的区别。以快递员为例,此时快递员相当于一个线程。IO方式好比一个快递员一次只传送一件货物,送完后返回继续;NIO则是一个分发机制,一个收发站聚集所有此时到来的货物,由负责人进行统一管理,同样也是一个快递员负责一件货物,不同的是负责人会对货物进行筛选,确定一个用户的所有货物到齐后将货物交由快递员一起配送,在货物未到齐前,快递员只需处理当前已完全到达的货物。AIO采用的是回调机制,快递员不用担心货物是否到齐,收发站会确认货物到齐,并通知快递员揽件。
NIO在调用系统IO命令后便返回,但是需要不停的询问系统IO是否完成;而AIO在调用系统IO命令后大可去喝杯茶,等待系统告知说IO已完成,然后便去处理。
NIO除了是非阻塞IO,也使用了IO多路复用(IO multiplexing)。NIO的select主线程会轮询请求的所有request,将request分为两个队列存储,一个是IO已准备好的队列,一个是IO未准备好的队列;一旦发现IO已经准备好的队列不为空,则接受该队列的所有请求(此时也是一个线程一个请求)。一个线程控制着所有的IO请求便是IO多路复用了。
从以上可以看出,NIO并不一定会比IO好很多,NIO的适用场景一般是连接数较多但是连接时间较短的场景(比如聊天工具)。
更多关于IO的内容请查看 http://blog.csdn.net/zhangzeyuaaa/article/details/42609723
三.NIO通信相关内容
NIO是将socket封装的一类非阻塞方式的通信模式.何为非阻塞通信,非阻塞即线程在处理一个IO时,若IO未准备好,则线程不会停留在这等待IO.先谈谈NIO的四个要素ServerSocketChannel,SocketChannel,Selector,SelectionKey.这四个东西是如何协调工作呢?
服务器端
//服务器初始化
private ServerSocketChannel serverSocketChannel = null;
private Selector selector = null;
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false); //设置成非阻塞式
serverSocketChannel.socket().bind(new InetSocketAddress(8888));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //通道设置权限
System.out.println("服务器启动正常!");
//非阻塞式轮询每个selector获得的连接,对每个连接进行处理
while(true){
selector.select();
Iterator it = this.selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key = (SelectionKey)it.next();
it.remove();
if(key.isAcceptable()){ <span>
ServerSocketChannel server = (ServerSocketChannel)key.channel();</span>
SocketChannel channel = server.accept();
channel.configureBlocking(false);
channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
channel.register(this>.selector, SelectionKey.OP_READ);
}else if(key.isReadable()){
doRead();
}
}
}
我还是先上传一份可以直接运行的代码
//服务器端程序
public class SimpleNIOServer {
private static ServerSocketChannel serverSocketChannel = null;
private static Selector selector = null;
public static void main(String args[]) throws IOException{
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(8888));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器启动正常!");
while(true){
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key = (SelectionKey)it.next();
it.remove();
if(key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel)key.channel();
SocketChannel channel = server.accept();
channel.configureBlocking(false);
channel.write(ByteBuffer.wrap(new String("Hello I am server").getBytes()));
channel.register(selector, SelectionKey.OP_READ);
}else if(key.isReadable()){
//获取消息
SocketChannel channel1 = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel1.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("server get :"+msg);
//发送消息
ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
channel1.write(outBuffer);
}
}
}
}
}
//客户端程序
public class SimpleNIOClient {
private static Selector selector;
public static void main(String args[]) throws IOException{
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
selector = Selector.open();
channel.connect(new InetSocketAddress("127.0.0.1",8888));
channel.register(selector, SelectionKey.OP_CONNECT);
while (true) {
selector.select();
Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
ite.remove();
if (key.isConnectable()) {
SocketChannel channel1 = (SocketChannel) key.channel();
// 如果正在连接,则完成连接
if(channel1.isConnectionPending()){
channel1.finishConnect();
}
//设置成非阻塞
channel1.configureBlocking(false);
//给服务端发送信息哦
channel1.write(ByteBuffer.wrap(new String("hello I am client").getBytes()));
//给通道设置读的权限。
channel1.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel channel1 = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel1.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("client get :"+msg);
ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
channel1.write(outBuffer);
}
}
}
}
}
注:该代码参考了部分http://weixiaolu.iteye.com/blog/1479656中代码
从代码中可以看出,NIO对事件依赖度很大,在绑定通道后需要注册事件类型,而NIO有比较常用的四种事件
事件名 | 对应值 |
服务端接收客户端连接事件 | SelectionKey.OP_ACCEPT(16) |
客户端连接服务端事件 | SelectionKey.OP_CONNECT(8) |
读事件 | SelectionKey.OP_READ(1) |
写事件 | SelectionKey.OP_WRITE(4) |