TCP和UDP的区别
- UDP
- 每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接
- UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。
- UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方
- UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。飞秋聊天、凌波桌面共享、网络视频会议
- TCP
- 面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中需要连接时间。
- TCP传输数据无大小限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大的数据。
- TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。
- TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。
网络编程中的关键类和接口
-
InteAddress类 --> 是一个代表IP地址的封装,没有构造方法,可以通过两个静态方法获得它的对象
InetAddress ip = InetAddress.getByName("www.baidu.com"); InetAddress local = InetAddress.getByAddress(new byte[]{127,0,0,1});
-
URL --> 是统一资源定位符的简称,它表示Internet上某一资源的地址
-
URLConnection --> 对象可以向所代表的URL连接发送请求和读取URL的资源
-
URLDecoder和URLEncoder -->用于编解码
String keyWord = URLDecoder.decode("%E6%9D%8E%E5%88%9A+j2ee", "UTF-8"); String urlStr = URLEncoder.encode( "ROR敏捷开发最佳指南" , "GBK");
-
Get方式请求资源
String urlName = url + "?" + param; URL realUrl = new URL(urlName); URLConnection conn = realUrl.openConnection();//打开和URL之间的连接 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"); conn.connect();//建立实际的连接
-
Post方式请求资源
URL realUrl = new URL(url); URLConnection conn = realUrl.openConnection();//打开和URL之间的连接 conn.setRequestProperty("accept", "*/*");//设置通用的请求属性 conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"); //发送POST请求必须设置如下两行 conn.setDoOutput(true);//可以发送数据 conn.setDoInput(true);//可以接收数据 out = new PrintWriter(conn.getOutputStream());//获取URLConnection对象对应的输出流 out.print(param);//发送请求参数
BIO编程
-
TCP
-
服务端
ServerSocket serverSocket =new ServerSocket(10086);//1024-65535的某个端口 Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream(); InputStreamReader isr =new InputStreamReader(is); BufferedReader br =new BufferedReader(isr); String info =null; while((info=br.readLine())!=null){ System.out.println("Hello,我是服务器,客户端说:"+info); } socket.shutdownInput();//关闭输入流 OutputStream os = socket.getOutputStream(); PrintWriter pw = new PrintWriter(os); pw.write("Hello World!"); pw.flush(); //5、关闭资源 XXX.close();
-
客户端
Socket socket =new Socket("127.0.0.1",10086); OutputStream os = socket.getOutputStream();//字节输出流 PrintWriter pw =new PrintWriter(os);//将输出流包装成打印流 pw.write("用户名:admin;密码:admin"); pw.flush(); socket.shutdownOutput(); InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String info = null; while((info=br.readLine())!=null){ System.out.println("Hello,我是客户端,服务器说:"+info); } //4、关闭资源 XXX.close();
-
-
UDP
-
服务端
DatagramSocket server = new DatagramSocket(5050); byte[] recvBuf = new byte[100]; DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length); server.receive(recvPacket); String recvStr = new String(recvPacket.getData(), 0, recvPacket.getLength()); System.out.println("Hello World!" + recvStr); int port = recvPacket.getPort(); InetAddress addr = recvPacket.getAddress(); String sendStr = "Hello ! I'm Server"; byte[] sendBuf; sendBuf = sendStr.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length, addr, port); server.send(sendPacket); server.close();
-
客户端
DatagramSocket client = new DatagramSocket(); String sendStr = "Hello! I'm Client"; byte[] sendBuf; sendBuf = sendStr.getBytes(); InetAddress addr = InetAddress.getByName("127.0.0.1"); int port = 5050; DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length, addr, port); client.send(sendPacket); byte[] recvBuf = new byte[100]; DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length); client.receive(recvPacket); String recvStr = new String(recvPacket.getData(), 0, recvPacket.getLength()); System.out.println("收到:" + recvStr); client.close();
-
NIO编程(应用反应器模式)
-
核心类和接口
- Channel(通道):NIO把它支持的I/O对象抽象为Channel。它模拟了通信连接,类似于最早I/O中的流(Stream),用户可以通过它读取和写入数据。目前常用的类有SocketChannel、ServerSocketChannel、DatagramChannel、FileChannel等。
- Buffer(缓冲区): Buffer是一块连续的内存区域,一般作为Channel收发数据的载体出现。所有数据都通过Buffer对象来处理。
- Selector(选择器):Selector类提供了监控一个和多个通道当前状态的机制。只要Channel向Selector注册了某种特定的事件,Selector就会监听这些事件是否会发生,一旦发生某个事件,便会通知对应的Channel。使用选择器,借助单一线程,就可对数量庞大的活动I/O通道实施监控和维护。
- 在通道上可以注册我们感兴趣的事件。总共有以下四种事件:
- 服务端接收客户端连接事件 SelectionKey.OP_ACCEPT(16)
- 客户端连接服务端事件 SelectionKey.OP_CONNECT(8)
- 读事件 SelectionKey.OP_READ(1)
- 写事件 SelectionKey.OP_WRITE(4)
-
使用NIO编程,大体上可以分为下面三个步骤
- 向Selector对象注册感兴趣的事件
- 从Selector中获取感兴趣的事件
- 根据不同的事件进行相应的处理
-
NIO实例代码
-
服务端
public class NIOServer { private Selector selector; // 通道管理器 public void initServer(int port) throws IOException { ServerSocketChannel serverChannel = ServerSocketChannel.open(); // 获得一个ServerSocket通道 serverChannel.configureBlocking(false); // 设置通道为非阻塞 serverChannel.socket().bind(new InetSocketAddress(port)); // 将该通道对应的ServerSocket绑定到port端口 this.selector = Selector.open(); // 获得一个通道管理器 // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后, // 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。 serverChannel.register(selector, SelectionKey.OP_ACCEPT); } public void listen() throws IOException { System.out.println("服务端启动成功!"); while (true) { // 轮询访问selector selector.select(); // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞 Iterator ite = this.selector.selectedKeys().iterator(); // 获得selector中选中的项的迭代器,选中的项为注册的事件 while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); ite.remove(); // 删除已选的key,以防重复处理 if (key.isAcceptable()) { // 客户端请求连接事件 ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel channel = server.accept(); // 获得和客户端连接的通道 channel.configureBlocking(false); // 设置成非阻塞 channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes( "UTF-8"))); channel.register(this.selector, SelectionKey.OP_READ); } else if (key.isReadable()) { read(key); } } } } public void read(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); // 服务器可读取消息:得到事件发生的Socket通道 ByteBuffer buffer = ByteBuffer.allocate(64); // 创建读取的缓冲区 channel.read(buffer); byte[] data = buffer.array(); String msg = new String(data, "UTF-8").trim(); System.out.println("服务端收到信息:" + msg); ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes("UTF-8")); channel.write(outBuffer); // 将消息回送给客户端 } public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(8000); server.listen(); } }
-
客户端
public class NIOClient { private Selector selector; // 通道管理器 public void initClient(String ip, int port) throws IOException { SocketChannel channel = SocketChannel.open(); // 获得一个Socket通道 channel.configureBlocking(false); // 设置通道为非阻塞 this.selector = Selector.open(); // 获得一个通道管理器 channel.connect(new InetSocketAddress(ip, port)); // 用channel.finishConnect();才能完成连接 channel.register(selector, SelectionKey.OP_CONNECT); } public void listen() throws IOException { while (true) { // 轮询访问selector selector.select(); Iterator ite = this.selector.selectedKeys().iterator(); // 获得selector中选中的项的迭代器 while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); ite.remove(); // 删除已选的key,以防重复处理 if (key.isConnectable()) { // 连接事件发生 SocketChannel channel = (SocketChannel) key.channel(); if (channel.isConnectionPending()) { // 如果正在连接,则完成连接 channel.finishConnect(); } channel.configureBlocking(false); // 设置成非阻塞 channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes( "UTF-8"))); channel.register(this.selector, SelectionKey.OP_READ); } else if (key.isReadable()) { read(key); } } } } public void read(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); // 服务器可读取消息:得到事件发生的Socket通道 ByteBuffer buffer = ByteBuffer.allocate(64); // 创建读取的缓冲区 channel.read(buffer); byte[] data = buffer.array(); String msg = new String(data, "UTF-8").trim(); System.out.println("客户端收到信息:" + msg); } public static void main(String[] args) throws IOException { NIOClient client = new NIOClient(); client.initClient("localhost", 8000); client.listen(); } }
-
AIO编程
-
核心类或接口
-
Callable: Callable与Runnable的功能大致相似,Callable中有一个call()函数,但是call()函数有返回值
-
Future: Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作。get方法会阻塞,直到任务返回结果
-
RunnableFuture: 它继承了Runnbale和Futrue这两个接口
-
FutureTask:
- 它实现了RunnableFuture,另外它还可以包装Runnable和Callable,由构造函数注入依赖
- 它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行
- 它还可以直接通过get()函数获取执行结果,该函数会阻塞,直到结果返回
-
使用实例:
Future<?> result = mExecutor.submit(new Runnable() { @Override public void run() { fibc(20); } }); Future<Integer> result2 = mExecutor.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { return fibc(20); } }); FutureTask<Integer> futureTask = new FutureTask<Integer>( new Callable<Integer>() { @Override public Integer call() throws Exception { return fibc(20); } }); mExecutor.submit(futureTask) ;//提交futureTask
-
-
异步Socket :
- AsynchronousChannel:所有AIO Channel的父类。
- AsynchronousByteChannel:支持Byte读写的Channel
- AsynchronousDatagramChannel:支持数据包(datagram)读写的Channel
- AsynchronousFileChannel:支持文件读写的Channel
- AsynchronousServerSocketChannel: 支持数据流读写的服务器端Channel
- AsynchronousSocketChannel:支持数据流读写的客户端Channel
- AsynchronousChannelGroup:支持资源共享的Channel分组
- CompletionHandler: 异步IO操作结果的回调接口,用于定义在IO操作完成后所作的回调工作。AIO的API允许两种方式来处理异步操作的结果:返回的Future模式或者注册CompletionHandler,我更推荐用CompletionHandler的方式
- 异步Socket方法总结:
- accept(): 接受一个连接,返回一个Future,可通过Future获取到Socket的状态,和数据
- accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler):接受连接,并为连接绑定一个CompletionHandler处理Socket连接
- bind(SocketAddress local): 把ServerSocket绑定到本地端口上,等待连接
- bind(SocketAddress local, int backlog): 功能和上面一个方法一样,添加了backlog参数指定队列中挂起的连接的最大个数
- open(): 开启一个异步Socket通道
- open(AsynchronousChannelGroup group): 开启一个异步Socket通道,并把通道加入到指定的组做资源管理
- provider(): 返回这个Channel的创建者
- setOption(SocketOption name, T value): 配置Socket参数的方法
-
-
AIO原理
所谓AIO,异步IO,其主要是针对进程在调用IO获取外部数据时,是否阻塞调用进程而言的。一个进程的IO调用步骤大致如下:- 进程向操作系统请求数据
- 操作系统把外部数据加载到内核的缓冲区中
- 操作系统把内核的缓冲区拷贝到进程的缓冲区
- 进程获得数据完成自己的功能
-
AIO代码实例
-
服务端
public class AIOServer { public static void main(String[] args) throws Exception { AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(Executors.newFixedThreadPool( 10)); AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(channelGroup); server.bind(new InetSocketAddress(5000)); server.accept(server, new CompletionHandler<AsynchronousSocketChannel, AsynchronousServerSocketChannel>() { @Override public void completed(AsynchronousSocketChannel result, AsynchronousServerSocketChannel server) { server.accept(server, this); ByteBuffer buffer = ByteBuffer.allocate(1024); Future<Integer> future = result.read(buffer); try { future.get(); buffer.flip(); byte[] dst = new byte[buffer.limit()]; buffer.get(dst, 0, buffer.limit()); System.out.println("server************* : " + new String(dst)); buffer.clear(); buffer.put("client received, haha".getBytes()); buffer.flip(); result.write(buffer); buffer.clear(); result.read(buffer).get(); buffer.flip(); byte[] dst2 = new byte[buffer.limit()]; buffer.get(dst2, 0, buffer.limit()); System.out.println("*********server received : " + new String(dst2)); } catch (Exception e) { e.printStackTrace(); } } @Override public void failed(Throwable exc, AsynchronousServerSocketChannel attachment) { } }); Thread.sleep(100000); } }
-
客户端
public class AIOClient { public static void main(String[] args) throws Exception { AsynchronousSocketChannel clientChannel = AsynchronousSocketChannel.open(); Future future = clientChannel.connect(new InetSocketAddress( "127.0.0.1", 5000)); Object o = future.get(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); clientChannel.write(byteBuffer.wrap("somchai ....".getBytes())); ByteBuffer readBuffer = ByteBuffer.allocate(1024); MyObj myObj = new MyObj(clientChannel, readBuffer); clientChannel.read(readBuffer, myObj, new CompletionHandler<Integer, MyObj>() { @Override public void completed(Integer result, MyObj attachment) { attachment.myBuffer.flip(); byte[] dst = new byte[attachment.myBuffer.limit()]; attachment.myBuffer.get(dst, 0, attachment.myBuffer.limit()); System.out.println("client***inner************** b : " + new String(dst)); ByteBuffer writeBuffer = ByteBuffer.allocate(1024); writeBuffer.put(new String(" I am client ...xixi").getBytes()); writeBuffer.flip(); attachment.clientChannel.write(writeBuffer); } @Override public void failed(Throwable exc, MyObj attachment) { } }); Thread.sleep(10000); } } class MyObj { AsynchronousSocketChannel clientChannel = null; ByteBuffer myBuffer = null; public MyObj(AsynchronousSocketChannel clientChannel, ByteBuffer myBuffer) { this.clientChannel = clientChannel; this.myBuffer = myBuffer; } }
-
BIO,NIO,AIO的区别
- 一个IO操作其实分成了两个步骤:发起IO请求和实际的IO操作
- 阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO
- 同步IO和异步IO的区别就在于第二个步骤是否阻塞,如果请求进程被IO读写阻塞,那么就是同步IO
- 什么是同步、异步
- 同步: 指用户进程发起了实际的IO操作之后,等待或轮询的查看IO操作是否完成
- 异步: 指用户进程发起了实际的IO操作之后,便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知
- 什么是阻塞和非阻塞
- 阻塞: 读取或者写入函数将一直等待
- 非阻塞: 读取或者写入函数会立即返回一个状态值
- 同步、异步和阻塞、非阻塞的组合
- 同步阻塞(BIO): 用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行
- 同步非阻塞(NIO): 用户进程发起一个IO操作以后,可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费
- 异步非阻塞(AIO): 用户进程发起一个IO操作后便立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了