Java网络编程

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读取或者写入操作已经由内核完成了
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值