java-网络编程——黑马程序员学习笔记

网络编程概念

可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信)

Java网络编程解决方案

java.net.*包下提供了网络编程的解决方案

基本通信架构

基本的通信架构有两种形式:CS架构,BS架构

注意:无论是BS架构还是CS架构都必须依赖网络编程

网络通信的三要素

ip地址:

设备在网络中的地址,是唯一的标识

IPV6:共128位,IPV6分成8段表示,每段每四位编码成一个16进制位表示,数之间用冒号(:)分开

inetAdress
  • 代表ip地址

    package com.cy;

    import java.net.InetAddress;
    import java.net.UnknownHostException;

    /**

    • 目标:掌握InetAdress类的使用
      */
      public class InetAdressTest {
      public static void main(String[] args) throws Exception{
      //1、获取本机IP地址对象
      InetAddress ip1 = InetAddress.getLocalHost();//获取本地ip地址对象
      System.out.println(ip1.getHostName());//获取主机名字
      System.out.println(ip1.getHostAddress());//获取主机具体ip地址信息

      复制代码
       //2、获取指定ip地址或者域名的IP地址对象
       InetAddress ip2 = InetAddress.getByName("www.baidu.com");
       System.out.println(ip2.getHostName());
       System.out.println(ip2.getHostAddress());
       
       //ping www.baidu.com
       System.out.println(ip2.isReachable(6000));

      }
      }

端口号:

  • 应用程序在设备中唯一的标识
  • 标记正在计算机设备上运行的应用程序的,被规定为一个16位的二进制,范围是0~65535
分类
  • 周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用80,FTP占用21 )
  • 注册端口:1024~49151,分配给用户进程或某些应用程序
  • 动态端口:49152~65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配

**注意:**我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。

协议:

  • 连接和数据在网络中传输的规则
  • 网络上的通信设备事先规定的连接规则,以及传输数据的规则被称为网络通信协议
开放式网络互联标准:
  • OSI网络参考模型,全球网络互联标准
  • TCP/IP网络模型:事实上的国际标准
两种通信协议
  • UDP:用户数据报协议

    特点:无连接,不可靠通信,但是通信效率高

  • TCP: 传输控制协议

    特点:面向连接,可靠通信

    三次握手建立可靠连接,数据传输进行确认,四次挥手断开通信

UDP 通信

UDP单次收发

Server.java

复制代码
package com.cy.d2_udp1;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * 目标:完成UDP通信快速入门-服务端开发
 */
public class Server {
    public static void main(String[] args)throws Exception{
        System.out.println("======服务端启动======");
        //1、创建服务端对象(创建一个接韭菜的人)
        DatagramSocket socket = new DatagramSocket(6666);

        //2、创建一个数据包对象,用于接收数据(创建一个韭菜盘子)
        byte[] buffer = new byte[1024*64]; //64kb
        DatagramPacket packet = new DatagramPacket(buffer,buffer.length);

        //3、开始正式使用数据包来接收客户端发来的数据
        socket.receive(packet);

        //4、从字节数组中,把接收到的数据打印出来
        //接收多少就倒出多少
        //接收本次数据包接收了多少数据
        int len = packet.getLength();
        String rs = new String(buffer,0,len);
        System.out.println(rs);

        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(packet.getPort());

        socket.close();//释放资源

    }
}

Client.java

复制代码
package com.cy.d2_udp1;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
* 目标:完成UDP通信快速入门------客户端开发:实现一发一收
* */
public class Client {
   public static void main(String[] args)throws Exception{
       //1、创建客户端对象(发韭菜出去的人)
       DatagramSocket socket = new DatagramSocket(7777);

       //2、创建数据包对象,封装发出去的数据(创建一个韭菜盒子)
       /*public DatagramPacket(byte buf[], int length,
         InetAddress address, int port)
       参数一:封装要发出去的数据
       参数二:发出去的数据大小
       参数三:服务端的IP地址(找服务端的主机)
       参数四:服务端程序的端口
       */
       byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();
       DatagramPacket packet = new DatagramPacket(bytes,bytes.length
               ,InetAddress.getLocalHost(),6666);
       //3、开始正式发送这个数据包的数据出去
       socket.send(packet);

       System.out.println("客户端数据发送完毕");
       socket.close();//释放资源
   }
}

UDP通信------多发多收

client.java

复制代码
package com.cy.d2_udp1;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

/**
 * 目标:完成UDP通信快速入门------客户端开发:实现多发多收
 * */
public class Client {
    public static void main(String[] args)throws Exception{
        //1、创建客户端对象(发韭菜出去的人)
        DatagramSocket socket = new DatagramSocket();

        //2、创建数据包对象,封装发出去的数据(创建一个韭菜盒子)
        /*public DatagramPacket(byte buf[], int length,
          InetAddress address, int port)
        参数一:封装要发出去的数据
        参数二:发出去的数据大小
        参数三:服务端的IP地址(找服务端的主机)
        参数四:服务端程序的端口
        */
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            //一旦发现用户输入的exit命令,就退出客户端
            if("exit".equals(msg)){
                System.out.println("欢迎下次光临,退出成功");
                socket.close();
                break;//跳出死循环
            }

            byte[] bytes = msg.getBytes();
//            byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();
            DatagramPacket packet = new DatagramPacket(bytes,bytes.length
                    ,InetAddress.getLocalHost(),6666);
            //3、开始正式发送这个数据包的数据出去
            socket.send(packet);
        }

//        System.out.println("客户端数据发送完毕");
//        socket.close();//释放资源
    }
}

server.java

复制代码
package com.cy.d2_udp1;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * 目标:完成UDP通信快速入门-服务端开发
 */
public class Server {
    public static void main(String[] args)throws Exception{
        System.out.println("======服务端启动======");
        //1、创建服务端对象(创建一个接韭菜的人)
        DatagramSocket socket = new DatagramSocket(6666);

        //2、创建一个数据包对象,用于接收数据(创建一个韭菜盘子)
        byte[] buffer = new byte[1024*64]; //64kb
        DatagramPacket packet = new DatagramPacket(buffer,buffer.length);

        while (true) {
            //3、开始正式使用数据包来接收客户端发来的数据
            socket.receive(packet);

            //4、从字节数组中,把接收到的数据打印出来
            //接收多少就倒出多少
            //接收本次数据包接收了多少数据
            int len = packet.getLength();
            String rs = new String(buffer,0,len);
            System.out.println(rs);

            System.out.println(packet.getAddress().getHostAddress());
            System.out.println(packet.getPort());
            System.out.println("------------------------------------------");

//            socket.close();//释放资源
        }

    }
}

TCP 通信

Client.java

复制代码
package com.cy.d4_tcp1;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;

public class Client {
    public static void main(String[] args)throws Exception{
        //1、创建socket对象,并同时请求与服务端程序进行连接
        Socket socket = new Socket("127.0.0.1",8888);

        //2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();

        //3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        //4、开始写数据出去
        dos.writeUTF("在一起好吗?");
        dos.close();

        socket.close();//释放连接资源
    }
}

Server.java

复制代码
package com.cy.d4_tcp1;

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args)throws Exception{
        System.out.println("-----------------服务端启动成功-------------");
        //1、创建ServerSocket对象,同时为服务端注册端口
        ServerSocket serversocket = new ServerSocket(8888);

        //2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
        Socket socket = serversocket.accept();

        //3、从socket通信管道中得到一个字节输入流
        InputStream is = socket.getInputStream();

        //4、把原始的数据输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);

        //5、使用数据输入流读取客户端发送过来的消息
        String rs = dis.readUTF();
        System.out.println(rs);
        //其实也可以获取客户端的IP地址
        System.out.println(socket.getRemoteSocketAddress());

        dis.close();
        socket.close();
    }
}

多发多收

Client.java

复制代码
package com.cy.d5_tcp2;

import javax.print.Doc;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args)throws Exception{
        //1、创建socket对象,并同时请求与服务端程序进行连接
        Socket socket = new Socket("127.0.0.1",8888);

        //2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();

        //3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说");
            String msg = sc.nextLine();
            //4、开始写数据出去
            //一旦用户输入exit就退出输入端
            if("exit".equals(msg)){
                System.out.println("欢迎下次光临,退出成功");
                dos.close();
                socket.close();
                break;
            }
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

Server.java

复制代码
package com.cy.d5_tcp2;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args)throws Exception{
        System.out.println("-----------------服务端启动成功-------------");
        //1、创建ServerSocket对象,同时为服务端注册端口
        ServerSocket serversocket = new ServerSocket(8888);

        //2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
        Socket socket = serversocket.accept();

        //3、从socket通信管道中得到一个字节输入流
        InputStream is = socket.getInputStream();

        //4、把原始的数据输入流包装成数据输入流
        DataInputStream dis = new DataInputStream(is);

        while (true) {
            //5、使用数据输入流读取客户端发送过来的消息
            try {
                String rs = dis.readUTF();
                System.out.println(rs);
            } catch (IOException e) {
                System.out.println(socket.getRemoteSocketAddress() + "离线");
                dis.close();
                socket.close();
                break;
            }
        }
    }
}

支持多个客户端同时通信

Client.java

复制代码
package com.cy.d6_tcp3;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args)throws Exception{
        //1、创建socket对象,并同时请求与服务端程序进行连接
        Socket socket = new Socket("127.0.0.1",8888);

        //2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();

        //3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说");
            String msg = sc.nextLine();
            //4、开始写数据出去
            //一旦用户输入exit就退出输入端
            if("exit".equals(msg)){
                System.out.println("欢迎下次光临,退出成功");
                dos.close();
                socket.close();
                break;
            }
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

Server.java

复制代码
package com.cy.d6_tcp3;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args)throws Exception{
        System.out.println("-----------------服务端启动成功-------------");
        //1、创建ServerSocket对象,同时为服务端注册端口
        ServerSocket serversocket = new ServerSocket(8888);

        while (true) {
            //2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serversocket.accept();

            System.out.println("有人上线了" + socket.getRemoteSocketAddress());

            //3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();

        }
    }
}

ServerReaderThread.java

复制代码
package com.cy.d6_tcp3;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class ServerReaderThread extends Thread{
    private Socket socket;
    public  ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run(){
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                } catch (IOException e) {
                    System.out.println("有人下线了" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

TCP通信综合案例------群聊

Client.java

复制代码
package com.cy.d6_tcp3;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args)throws Exception{
        //1、创建socket对象,并同时请求与服务端程序进行连接
        Socket socket = new Socket("127.0.0.1",8888);

        //创建一个独立的线程,负责随机从socket中接收服务端发来的消息
        new ClienReaderThread(socket).start();

        //2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
        OutputStream os = socket.getOutputStream();

        //3、把低级的字节输出流包装成数据输出流
        DataOutputStream dos = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说");
            String msg = sc.nextLine();
            //4、开始写数据出去
            //一旦用户输入exit就退出输入端
            if("exit".equals(msg)){
                System.out.println("欢迎下次光临,退出成功");
                dos.close();
                socket.close();
                break;
            }
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

Server.java

复制代码
package com.cy.d6_tcp3;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class Server {
    public static List<Socket> onlineSockets = new ArrayList<>();
    public static void main(String[] args)throws Exception{
        System.out.println("-----------------服务端启动成功-------------");
        //1、创建ServerSocket对象,同时为服务端注册端口
        ServerSocket serversocket = new ServerSocket(8888);

        while (true) {
            //2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serversocket.accept();
            onlineSockets.add(socket);
            System.out.println("有人上线了" + socket.getRemoteSocketAddress());

            //3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();

        }
    }
}

ServerReaderThread.java

复制代码
package com.cy.d6_tcp3;

import java.io.*;
import java.net.Socket;

public class ServerReaderThread extends Thread{
    private Socket socket;
    public  ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run(){
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                    //把这个消息分发给全部客户端进行接收
                    sendMsgToAll(msg);
                } catch (IOException e) {
                    System.out.println("有人下线了" + socket.getRemoteSocketAddress());
                    Server.onlineSockets.remove(socket);
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void sendMsgToAll(String msg) throws IOException {
        //发送给全部在线的socket管道接收
        for (Socket onlineSocket : Server.onlineSockets) {
            OutputStream os = onlineSocket.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);
            dos.writeUTF(msg);
            dos.flush();
        }
    }
}

ClienReaderThread.java

复制代码
package com.cy.d6_tcp3;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class ClienReaderThread extends Thread{
    private Socket socket;
    public ClienReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run(){
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true){
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                    //把这个消息分发给全部客户端进行接收
                } catch (IOException e) {
                    System.out.println("自己下线了" + socket.getRemoteSocketAddress());
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

实现BS架构

Server.java

复制代码
package com.cy.d8_tcp5;

import com.cy.d8_tcp5.ServerReaderThread;

import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args)throws Exception{
        System.out.println("-----------------服务端启动成功-------------");
        //1、创建ServerSocket对象,同时为服务端注册端口
        ServerSocket serversocket = new ServerSocket(8081);


        while (true) {
            //2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serversocket.accept();
            System.out.println("有人上线了" + socket.getRemoteSocketAddress());

            //3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            new ServerReaderThread(socket).start();

        }
    }
}

ServerReaderThread.java

复制代码
package com.cy.d8_tcp5;

import javax.xml.crypto.Data;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

public class ServerReaderThread extends Thread{
    private final Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run(){
        //立即响应网页内容,"黑马程序员"给浏览器展示
        try {
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println();
            ps.println("<div style='color:red;font-size:120px;text-align:center'>黑马程序员666</div>");
            ps.close();
            socket.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

线程池优化BS架构

Server.java

复制代码
package com.cy.d9_tcp6.d8_tcp5;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;

public class Server {
    public static void main(String[] args)throws Exception{
        System.out.println("-----------------服务端启动成功-------------");
        //1、创建ServerSocket对象,同时为服务端注册端口
        ServerSocket serversocket = new ServerSocket(8081);

        //创建出一个线程池,负责处理管道的任务
        //参数1:核心线程数量(io密集型的话,当前计算机线程数量*2)
        //参数2:最大线程数量
        //参数3:临时线程存活时间
        //参数4:s为单位
        //任务5:缓存几个客户端的请求
        //任务6:运用线程工厂
        //任务7:拒绝任务策略
        ThreadPoolExecutor pool = new ThreadPoolExecutor(16*2,16*2,0, TimeUnit.SECONDS
        ,new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        while (true) {
            //2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
            Socket socket = serversocket.accept();
            System.out.println("有人上线了" + socket.getRemoteSocketAddress());

            //3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
            pool.execute(new ServerReaderRunnable(socket));
        }
    }
}

ServerReaderRunnable.java

复制代码
package com.cy.d9_tcp6.d8_tcp5;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

public class ServerReaderRunnable implements Runnable{
    private final Socket socket;
    public ServerReaderRunnable(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run(){
        //立即响应网页内容,"黑马程序员"给浏览器展示
        try {
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println();
            ps.println("<div style='color:red;font-size:120px;text-align:center'>黑马程序员666</div>");
            ps.close();
            socket.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
相关推荐
零基础的修炼5 小时前
Linux网络---网络层
运维·服务器·网络
五VV5 小时前
【ESP32】SP3手柄与ESP32连接不上问题解决
经验分享·学习
开开心心就好5 小时前
键盘改键工具免安装,自定义键位屏蔽误触
java·网络·windows·随机森林·计算机外设·电脑·excel
IManiy5 小时前
总结之Temporal全局速率控制(二)第三方速率控制服务设计
java
OpenMiniServer5 小时前
电气化能源革命下的社会
java·人工智能·能源
独自破碎E6 小时前
LCR_019_验证回文串II
java·开发语言
坚持就完事了6 小时前
Java中的一些关键字
java·开发语言
墨黎芜6 小时前
SQL Server从入门到精通——C#与数据库
数据库·学习·信息可视化
wdfk_prog6 小时前
[Linux]学习笔记系列 -- [drivers][dma]stm32-dma
linux·笔记·学习