《网络程序设计-基于java8》复习笔记

2022-04-07  本文已影响0人  东也_

第一章概述

  1. ISO/OSI模型
  1. 网络协议
  1. TCP/IP协议(传输控制协议/网间协议)

java的输入和输出

  1. java中的输入和输出的类型有:常规的标准输入和输出、文件输入和输出、内存数据结构输入和输出、网络数据流的输入和输出;
  1. 流类概览
  1. IO异常
  1. InputStream常用方法
方法 定义
int available 返回下一次能够读取 不受阻塞或跳过 的字节数
void close 关闭输入流,并释放相关的所有系统资源
void mark(int) 标记输入流的当前位置,通常和reset结合使用,不是所有流都支持,可以通过markSupported方法判断是否支持mark
abstract int read 从输入流中读取一个字节
int read(byte[]) 从输入流中读取一定数量的字节存入字节数组,并返回读取的字节数
int read(byte[] b, int off, int len) 从输入流中读取最多len的字节存入字节数组b,读取的第一个字节存入b[off]中
void reset 将流重新置位为上次mark的位置
long skip(long n) 跳过输入流中的指定数量n字节
  1. OutputStream常用方法
方法 定义
abstract void write(int b) 将整型b的低字节写出入到输出流,低8位,其余24位忽略
void write(byte[] b) 将字节数组b的数据写入到输出流
void write(byte[] b, int off, int len) 把字节字节数组b中从off开始的len字节写入到输出流
flush 有时写到输出流的字节并没有被真正的发送出去,而是被缓存,达到一定的量时才会发出去,最后总会遗留一点数据,使用此方法可以将缓冲区的数据强行执行写操作
void close 关闭输出流,并释放相关的所有系统资源
  1. 文件流代码
static void fileReaderInput() {

        try {
            File file = new File("/Users/hansen/Desktop/项目练习/javaProjects/java考试/src/com/company/Person.java");
            FileReader reader  = new FileReader(file);
            FileWriter writer = new FileWriter("Person(1).java");

            char b[] = new char[100];
            int length;
            while ((length = reader.read(b)) != -1) {

                writer.write(b, 0, length);

            }
            reader.close();

            writer.flush();
            writer.close();

        } catch (IOException e) {

            e.printStackTrace();
        }

}
static void fileInputTest() {

        try {
            FileInputStream fileInputStream = new FileInputStream("/Users/hansen/Desktop/项目练习/javaProjects/java考试/src/com/company/Person.java");

            FileOutputStream outputStream = new FileOutputStream("/Users/hansen/Desktop/项目练习/javaProjects/Person.java");

            System.out.println("文件长度:" + fileInputStream.available());


            byte bytes[] = new byte[100];
            int count = 0;

            while ((count = fileInputStream.read(bytes)) != -1) {

                outputStream.write(bytes, 0, count);

            }

            fileInputStream.close();
            outputStream.flush();
            outputStream.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
}
  1. 数组流
  1. 缓冲流
  1. 对象流
  1. 管道流
static void pipedTest() throws IOException {



        PipedOutputStream outputStream = new PipedOutputStream();

        PipedInputStream inputStream = new PipedInputStream(outputStream);



        Thread thread1 = new Thread(new Runnable() {

            @Override
            public void run() {

                try {
                    outputStream.write("hello another".getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        });


        Thread thread2 = new Thread(new Runnable() {

            @Override
            public void run() {

                try {

                    int receives;
                    while ((receives = inputStream.read()) != -1) {

                        System.out.println("从管道中读取到数据:" + (char)receives);
                    }

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

        thread1.start();
        thread2.start();

 }
  1. 字节流和字符流之间的桥梁流
  1. 标准输入和输出
  1. 压缩流类

第三章 IP地址和URL

  1. 主机和端口
  1. IP地址

1、单播,一个网络接口指定一个单播地址,当两个网络节点通信的时候,通信双方根据IP地址所在的网段和路由关系建立数据通道,进行点对点通信;
2.、 广播,数据包会发送给给网段内所有的网络接口,无论网段内的其他主机是否希望接收,都会收到。但是广播只会在广播域内有效,因为路由器会阻隔广播;
3、组播,如果有多台主机希望同时获得相同的信息,可以加入同一个组,这个组的标识就是多播地址;送到该组播地址的数据包将会发送给组内的所有网络接口;

1、A类地址:网络地址占1个字节,且最高位时0,所以最大值是127,主机地址占3个字节。适用于大型网络,其中网络地址的10作为内部地址使用;
2、B类地址:网络地址占前两个字节,且最高位两位固定是10,那么B类网络地址的首字节取值范围是128~191。
3、C类地址:网络地址占前三个字节,最高三位固定是110,那么网络地址的首字节取值范围是192~223;C类地址适合中小型网络,其中192.168网段一般作为内部地址使用;
4、D类地址:是一类特殊的地址,用作组播地址。D地址的后28位不区分网络地址和主机地址,所以也没有掩码。发送到组播组的主机不一定需要加入到该组,组员也可以自由加入和退出。D类地址的首字节最高4位为1110,所有首字节的取值范围是224~239、所以D类地址的取址范围是224.0.0.0 ~ 239.255.255.255;
5、E类地址:也是一种特殊的地址,保留用于实验和未来使用。E类地址不标识网络地址,所以也不需要掩码;E类地址的首字节的最高5位固定是11110,首字节取值范围:240~247;

通过IP地址的首字节的值就可以判断,ip属于哪类地址。0 ~ 127 是A类;128 ~ 191是B类;192 ~ 223是C类; 224~239是D类;240~247是E类;

1、子网是将A、B、C类中主机地址,从高位开始的几位拿出来作为子网号,以进一步划分子网。如果拿前三位作为子网号,那么后5位就是主机号了,在子网号要去除000和111的情况;
2、那么如何判定IP中的主机地址的几位是用于划分子网的呢?这个就需要子网掩码来判断。
假如IP地址是192.168.1.33,子网掩码是255.255.255.224。主机地址变成二进制就是00100001,子网掩码的最后的224就是11100000。通过子网掩码的224就可以看出主机地址使用的前三位作为子网号。前三位可以划分为6种子网,分别是001,010,011,100,110,101。本来是8种因为去掉了全是0和全是1的情况。所以192.168.1.33的子网号是001,那么该子网的主机地址取址范围是192.168.1.33 ~ 192.168.1.62,主机地址二进制就是00100001 ~ 00111110;

1、全0对应当前主机,成为通配地址。可以接本机上收任一网卡的数据;
2、 全1意味着所有,它指的是网络中所有主机,也通常被称为广播地址;
3、 127.0.0.1是常用的环回地址,它指向主机内部的环回网络接口,这个接口允许主机给自己发送数据报,通常用来测试本地的TCP/IP协议是否安装完成,网络接口是配置成功;
4、 169.254.x.x,有时候网络中配置了DHCP(动态主机配置协议)服务器,为客户端分配IP地址。当DCHP分配失败后,客户端会采用169.254.x.x这样的默认地址,就是链路本地地址;

  1. 网络的连通性

在mac下使用ping

ping www.baidu.com
PING www.a.shifen.com (183.232.231.174): 56 data bytes
64 bytes from 183.232.231.174: icmp_seq=0 ttl=54 time=22.111 ms
64 bytes from 183.232.231.174: icmp_seq=1 ttl=54 time=23.262 ms
64 bytes from 183.232.231.174: icmp_seq=2 ttl=54 time=23.951 ms
64 bytes from 183.232.231.174: icmp_seq=3 ttl=54 time=23.570 ms
64 bytes from 183.232.231.174: icmp_seq=4 ttl=54 time=22.897 ms
64 bytes from 183.232.231.174: icmp_seq=5 ttl=54 time=23.951 ms
64 bytes from 183.232.231.174: icmp_seq=6 ttl=54 time=23.214 ms
64 bytes from 183.232.231.174: icmp_seq=7 ttl=54 time=22.972 ms
64 bytes from 183.232.231.174: icmp_seq=8 ttl=54 time=22.502 ms
64 bytes from 183.232.231.174: icmp_seq=9 ttl=54 time=23.083 ms
64 bytes from 183.232.231.174: icmp_seq=10 ttl=54 time=23.501 ms
64 bytes from 183.232.231.174: icmp_seq=11 ttl=54 time=22.926 ms
^C
--- www.a.shifen.com ping statistics ---
12 packets transmitted, 12 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 22.111/23.162/23.951/0.520 ms
  1. InetAddress类
  1. SocketAddress类
  1. URI
  1. URLConnection类
 static void getImageTest() {

         try {
             URL url = new URL("https://pics1.baidu.com/feed/4d086e061d950a7be45c7a2636d79cd1f3d3c9f2.jpeg?token=af070391e468a6f441b15805ef6c0ea0");
             BufferedInputStream inputStream = new BufferedInputStream(url.openStream());

             FileOutputStream outputStream = new FileOutputStream("logo_test.png");

             int i;
             while ((i = inputStream.read()) != -1) {

                 outputStream.write(i);
             }

             inputStream.close();
             outputStream.flush();
             outputStream.close();

         } catch (MalformedURLException e) {

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

     }
  1. URLStreamHandler

第4章 基于TCP的通信

  1. TCP传输层的数据格式称为segment报文段,UDP的数据结构称为Datagram数据报;
  1. socket
  1. ServerSocket
class DateServerSocket {

    public static void main(String[] args) {

        testServerSocket();

    }
    static void testServerSocket() {

        try {
            // 端口   队列长度   绑定网络接口ip地址  
            ServerSocket serverSocket = new ServerSocket(9999,100,null);
            

            System.out.println("Date 服务器开始运行..." + "port == " + serverSocket.getLocalPort() + "  ip == "+ serverSocket.getLocalSocketAddress());

            while (true) {

                Socket socket = serverSocket.accept();
                
                new ClientSocket(socket).start();

            }

        } catch (SocketException e) {

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

    }
    
    
    static class ClientSocket extends Thread {
        
        private  Socket socket;
        
        private  DataInputStream inputStream;
        
        ClientSocket(Socket socket) {
            
            this.socket = socket;
            System.out.println("收到一个请求:port = " + socket.getLocalPort()  + "ip = " + socket.getLocalAddress() + "net address = " + socket.getInetAddress() + "net port" + socket.getPort());


        }

        @Override
        public void run() {

            try {
                inputStream = new DataInputStream(socket.getInputStream());
                
                System.out.println("收到客户端消息:" + inputStream.readUTF());
                
                inputStream.close();
                
                socket.close();

            } catch (IOException e) {

                e.printStackTrace();
            }
        }
    }

}

第五章基于UDP的通信

1。 UDP协议

  1. DatagramSocket类

class UDPService {

    private  DatagramSocket datagramSocket;

    public static void main(String[] args) {

        UDPService service =  new UDPService();
        service.service();

    }

    UDPService() {

        try {
            datagramSocket = new DatagramSocket(9999);
            System.out.println("UDP 服务启动");

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

        }

    }

    void service() {


        new Thread(new Runnable() {
            @Override
            public void run() {

                while (true) {

                    byte[] buffer = new byte[100];
                    DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

                    try {
                        datagramSocket.receive(packet);

                        String message = new String(buffer,0,packet.getLength());

                        System.out.println("receive client message from " + packet.getAddress() + ":" + packet.getPort() + "is " + message);


                        if (message.equalsIgnoreCase("date")) {

                            SimpleDateFormat format = new SimpleDateFormat("yyyy 年 MM 月 dd 日");

                            packet.setData(("Now date is " + format.format(new Date())).getBytes());


                        } else if (message.equalsIgnoreCase("bye")) {

                            packet.setData("bye".getBytes());
                        }

                        datagramSocket.send(packet);


                    } catch (IOException e) {

                        e.printStackTrace();
                    }

                }

            }
        }).start();

    }
}

class UDPClient {


    public static void main(String[] args) {

        try {

            DatagramSocket socket = new DatagramSocket();

            InetAddress address = InetAddress.getByName("localhost");

            BufferedReader inputStream = new BufferedReader(new InputStreamReader(System.in));


            System.out.println("请输入发送的内容:");

            String msg = null;

            while ((msg = inputStream.readLine()) != null) {

                byte [] bytes = msg.getBytes();

                DatagramPacket sendPacket = new DatagramPacket(bytes, bytes.length,address,9999);

                socket.send(sendPacket);


                DatagramPacket receivePacket = new DatagramPacket(new byte[100], 100);

                socket.receive(receivePacket);

                String message = new String(receivePacket.getData(), 0 , receivePacket.getLength());

                if (message.equalsIgnoreCase("byte")) {
                    break;
                }

                System.out.println("receive service a message from " + receivePacket.getAddress() + "port " +receivePacket.getPort() + "is " + message );


            }


        } catch (SocketException e) {

            e.printStackTrace();
        } catch ( UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {

            e.printStackTrace();
        }
    }

}

第六章NIO和NIO2

1. NIO

2. 缓冲区Buffer

2. 选择器selector

aSelewctor.select();
Iterator it  = selector.selectedKeys().iterator();

虽然select方法是阻塞的,但可以通过wakeup方法可以让它立刻返回,如果当前没有线程阻塞在select方法中,而另一个线程调用了wakeup方法,那下一个调用select方法的线程会立即返回;

3. Channel接口

NIO服务器示例代码

class NIODateServer {

    private ServerSocketChannel serverSocketChannel;

    private Selector selector;

    public static void main(String[] args) {

        NIODateServer server = new NIODateServer();

        try {
            server.init();
            server.service();
        } catch (IOException e) {

            e.printStackTrace();
        }
    }

    void init()  {

        try {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            // 设置为非阻塞模式
            serverSocketChannel.configureBlocking(false);

            serverSocketChannel.bind(new InetSocketAddress("localhost",9999));

            System.out.println("服务器开始启动");

            // 将server通道注册到selector 分类为可接受连接的
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    void service() throws IOException {

        // 循环监听请求
        while (true) {

            selector.select();
            Iterator iterator = selector.selectedKeys().iterator();

            while (iterator.hasNext()) {

                SelectionKey key = (SelectionKey) iterator.next();

                if (key.isAcceptable()) {
                    // 处理连接请求
                    dealAccept(key);

                } else if (key.isReadable()) {
                    // 处理接收客户端数据

                    dealRead(key);
                }

                // 删除处理过的元素
                iterator.remove();
            }


        }


    }
    // 处理接受套接字的channel
    void dealAccept(SelectionKey key) throws IOException {


        ServerSocketChannel channel = (ServerSocketChannel) key.channel();

        SocketChannel socketChannel = channel.accept();


        System.out.println("收到连接请求:" + "\nip " + socketChannel.getRemoteAddress() + "\nport " + socketChannel.socket().getPort());

        socketChannel.configureBlocking(false);

        // 将SocketChannel注册到selector
        socketChannel.register(selector, SelectionKey.OP_READ);

    }

    // 处理通道读操作
    void dealRead(SelectionKey key) throws IOException {

        SocketChannel socketChannel = (SocketChannel)key.channel();

        // 创建字节缓冲区
        ByteBuffer buffer = ByteBuffer.allocateDirect(100);


        try {
            int count;
            count = socketChannel.read(buffer);
            if (count > 0) {
                System.out.println("收到消息:" + socketChannel.toString());

                dealMessageBuffer(buffer, socketChannel);
            } else if (count == -1) {

                // 当客户端断开 selector会一直收到该socket的可读操作
                System.out.println("客户端已断开,取消选择键关闭socket");
                key.cancel();
                socketChannel.close();
            }


        } catch (IOException e) {

            System.out.println("消息内容写入缓冲区出现错误--------");

            e.printStackTrace();
            // 如果读取数据出现异常就关闭socket
            socketChannel.close();
        }
        // 使用完的buffer要清空  不然这里会循环收到消息  不太理解
        buffer.clear();

    }
    // 处理收到的客户端消息
    void dealMessageBuffer(ByteBuffer buffer, SocketChannel socket) throws IOException {

        // 先将缓冲区进行反转  从写变成读
        buffer.flip();


        byte[] bytes = new byte[100];

        buffer.get(bytes,0, buffer.limit());
        String message = new String(bytes,0,buffer.limit());

        System.out.println("the message content is "  + message);

        // 清空缓冲区 准备重新写入数据
        buffer.clear();

        if (message.equalsIgnoreCase("date")) {
            SimpleDateFormat format = new SimpleDateFormat("yyyy - MM - dd");

            buffer.put(("date is  " +  format.format(new Date())).getBytes());

        } else if (message.equalsIgnoreCase("time")) {

            SimpleDateFormat format = new SimpleDateFormat("HH - mm - ss");

            buffer.put(("time is  " +  format.format(new Date())).getBytes());
        } else {

            buffer.put(message.getBytes());
        }

        // 再反转写为读 limit = position position = 0
        buffer.flip();

        socket.write(buffer);
    }
}

NIO客户端示例代码

class NIODateClient {

    private SocketChannel socketChannel;

    private Selector selector;

    public  static void main(String[] args) throws IOException {

        NIODateClient client = new NIODateClient();

        client.init();
        client.service();

    }

    void init () throws IOException {

        selector = Selector.open();

        socketChannel = SocketChannel.open(new InetSocketAddress("localhost",9999));


        socketChannel.configureBlocking(false);


        System.out.println(socketChannel.isConnected() ? "连接成功" : "连接失败" );


        socketChannel.register(selector, SelectionKey.OP_READ);


    }


    void service() throws IOException {

        doWirte();
        doRead();

    }

    // 从键盘输入消息内容
    private  void doWirte() {

        new NIOWriteThread(socketChannel).start();


    }

    private  void doRead() throws IOException {

        while (true) {

            selector.select();

            Iterator iterator = selector.selectedKeys().iterator();

            while (iterator.hasNext()) {

                SelectionKey key =  (SelectionKey) iterator.next();

                if (key.isReadable()) {

                    SocketChannel socket =  (SocketChannel) key.channel();

                    ByteBuffer buffer = ByteBuffer.allocateDirect(100);

                    socket.read(buffer);

                    buffer.flip();

                    byte[] bytes = new byte[100];
                    buffer.get(bytes, 0, buffer.limit());

                    String message = new String(bytes, 0,buffer.limit());

                    System.out.println("收到服务器消息:" + message);

                    buffer.clear();
                }
                // 删除处理过的元素
                iterator.remove();
            }
        }
    }

    // 开启一个子线程处理输入 并将内容发送出去
    class NIOWriteThread extends Thread {

        private SocketChannel socketChannel;

        NIOWriteThread(SocketChannel channel) {

            socketChannel = channel;
        }

        @Override
        public void run() {


            ByteBuffer buffer = ByteBuffer.allocateDirect(100);

            try {
                while (!Thread.currentThread().isInterrupted()) {

                    // 第二次输入 前 先清空缓冲区
                    buffer.clear();

                    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

                    String string = reader.readLine();

                    if (string != null) {

                        if (string.equalsIgnoreCase("quit")) {

                            System.exit(0);
                        }

                        buffer.put(string.getBytes());
                        buffer.flip();
                        socketChannel.write(buffer);
                    }

                }
            } catch (IOException e) {

                e.printStackTrace();

            } finally {
                buffer.clear();
            }

        }
    }
}

NIO.2

第七章 多线程和并发

进程是具有独立功能的程序针对一个数据集合的运行活动。它是操作系统分配资源的最小单位;
线程是比进程更小的概念,一个进程可以包含若干个线程,一个进程的多个线程可以共享进程资源;

1. 创建线程

2. 线程的状态

class ThreadTest implements Runnable {

    private String threadName;

    ThreadTest(String name) {

        threadName = name;

    }

    public  static  void main(String[] args) throws InterruptedException {


        Thread thread1 = new Thread(new ThreadTest("1"));
        Thread thread2 = new Thread(new ThreadTest("2"));

        thread2.start();

        thread2.join();

        thread1.start();


        System.out.println("Waiting");

        // 到这里的时候线程的任务已经执行完
        while (thread2.isAlive()) {

            System.out.println("Still waiting");

            thread2.join(1000);

        }

        System.out.println("End.");
    }

    @Override
    public void run() {

        try {

            for (int i = 0; i <5 ; i++) {

                Thread.sleep(1000);

                System.out.format("thread name: %s  -- %d \n", Thread.currentThread().getName(), i);
            }


        } catch (InterruptedException e) {

            e.printStackTrace();
        }


    }
}

3. 同步Synchronization


class SynchronizeObj {

    int count = 0;

    public synchronized void  increase() {

        for (int i = 0; i < 5; i++) {

            count++;

            System.out.println(Thread.currentThread().getName() + "加 1 == " + count);
        }
    }

    public synchronized  void decrease() {

        for (int i = 0; i < 5; i++) {

            count--;

            System.out.println(Thread.currentThread().getName() + "减 1 == " + count);
        }
    }
}

class SynchronizedTestThread extends  Thread {

    SynchronizeObj obj;
    SynchronizedTestThread(SynchronizeObj obj) {
        this.obj = obj;
    }


    @Override
    public void run() {
        synchronized (obj) {
            obj.increase();
            System.out.println("change to another operation");
            obj.decrease();
        }
    }

    public  static void main(String[] args) {

        SynchronizeObj obj = new SynchronizeObj();
        SynchronizedTestThread thread1 = new SynchronizedTestThread(obj);
        SynchronizedTestThread thread2 = new SynchronizedTestThread(obj);
        thread1.start();
        thread2.start();
    }
}

4. 线程间的协调

5. 死锁

6. 并发

7. CountDownLatch类

上一篇 下一篇

猜你喜欢

热点阅读