网络编程

一、概述

1. 引例

  • Package--包
    • 写信
      1. 对方的信息--姓名
      2. 对方的地址
      3. 自己的地址
  • TCP
    • 打电话 --连接-- --接了-- --通话
  • UDP
    • 发短信-- --发送了就完事了-- --接受

2. 什么是计算机网络

  • 计算机网络是指将地理位置不同的具有独立功能的多台计算机计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

3. 网络编程的目的

  • 传播交流信息

  • 数据交换——通信

4. 想要达到这个效果需要什么

  1. 如何准确地定位网络上的一台主机?

    • ip地址
    • 端口
    • 定位到这个计算机上的某个资源
  2. 找到了这个主机,如何传输数据/通信?

    • javaweb: 网页编程 BS架构
    • 网络编程: TCP/IP CS架构

二、网络通信的要素

1. 如何实现网络的通信?

  • 双方通信地址——IP

    192.168.16.1:5900

    • IP ——192.168.16.1

      image-20201121101335831
    • 端口号——5900

  • 规则:网络通信协议——UDP

    • http、ftp、smtp、tcp、udp

    • TCP/IP参考模型

      image-20201121101059750

三、IP地址

1. 作用

  • 唯一定位一台网络上的计算机
    • 127.0.0.1:本机localhost

2. 分类

  • ipv4 / ipv6

    • IPV4 127.0.0.1 四个字节组成 0-255 42亿个

      • 30亿都在美国,亚洲4亿,2011年用尽了
    • IPV6 128位,无符号整数

       2001:0bb2:aaaa:0000:0000:1aaa:1312
      
      • 可以通过ipconfig查询ip地址
    • 公网(互联网)-私网(局域网)

    • 域名:解决记忆IP问题

3. 代码实现

Inet4Adress类中没有构造器,无法new对象

image-20201121105204269

只能通过静态方法返回,来创建对象

InetAddress baidu = Inet4Address.getByName("www.baidu.com");
//测试IP
public class TestInetAdress {
    public static void main(String[] args) {
        try {
            //查询本机地址——三个方法
            InetAddress address1 = InetAddress.getByName("127.0.0.1");
            System.out.println(address1);// /127.0.0.1
            InetAddress address2 = InetAddress.getByName("localhost");
            System.out.println(address2);// localhost/127.0.0.1
            InetAddress localHost = InetAddress.getLocalHost();
            System.out.println(localHost);// DESKTOP-L7FN6HI/10.224.9.131

            //查询网站地址
            InetAddress baidu = InetAddress.getByName("www.baidu.com");
            System.out.println(baidu);// www.baidu.com/220.181.38.150

            //常用方法——get
            System.out.println(baidu.getAddress());// [B@1b6d3586
            System.out.println(baidu.getCanonicalHostName());// 220.181.38.150
            System.out.println(baidu.getHostAddress());// 220.181.38.150
            System.out.println(baidu.getHostName());// www.baidu.com

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

四、端口

1. 端口表示计算机上的一个程序的进程

  • 不同的进程有不同的端口号,用来区分软件

  • TCP端口、UDP端口

    • 被规定0~65535
    • 单个协议下端口号不能冲突

2. 分类

  • 公有端口 0~1023

    • http:80
    • http:443
    • FTP:21
    • Telent:23
  • 程序注册端口 1024~49151——分配给用户/程序的

    • Tomcat:8080
    • MySQL:3306
    • Oracle:1521
  • 动态、私有端口 49152~65535

    netstat -ano #查看所有端口
    netstat -ano|findstr "端口号" #查到具体pid
    tasklist|findstr "5584"
    

    image-20201121114123183

3. 端口Port与进程号PID

  • 一个程序对应一个端口

    • 一个端口可以有多个进程

      • 进程号PID——Process ID

        • 程序运行时系统自动分配,程序结束时回收
        • 因此同一个程序的PID是可变的
      • 一个进程可以有多个线程

        image-20201121113454820
        public class TestInetSocketAddress {
            public static void main(String[] args) {
        
                InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 8080);
                InetSocketAddress inetSocketAddress2 = new InetSocketAddress("localhost", 8080);
                System.out.println(inetSocketAddress);
                System.out.println(inetSocketAddress2);
        
                System.out.println(inetSocketAddress.getAddress());
                System.out.println(inetSocketAddress.getHostName());
                System.out.println(inetSocketAddress.getHostString());
                System.out.println(inetSocketAddress.getPort());
        
        
            }
        }
        
        image-20201121152343772

五、通信协议

协议:约定,让计算能互相交流的方法——普通话

网络通信协议:速率,传输码率,代码结构,传输结构

问题:非常复杂——大事化小——分层

image-20201121101059750

1.概念

TCP/IP协议——实际上是一组协议

  • TCP:用户传输协议——打电话

    • 连接,稳定
    • 三次握手、四次挥手
      • A与B都有各有一个发射器和接收器,每次断开一个
        • A ----断开请求
        • B ----确认断开?
        • B ----断开请求
        • A ----断开
    • 客户端、服务端
    • 传输完成,释放连接,效率低
  • UDP:用户数据报协议——发短信

    • 不连接,不稳定
    • 客户端、服务端,没有明确的界限
    • 不管准没准备好,都发给你
  • IP:网络互联协议

2. TCP协议

  • 客户端

    • 连接服务器Socket

    • 发送消息

      public class TcpClientDemo01 {
          public static void main(String[] args) {
      
              Socket socket = null;
              OutputStream os = null;
      
              try {
                  //1.要知道服务器的地址、端口号
                  InetAddress serverIP = InetAddress.getByName("127.0.0.1");
                  int port = 9999;
      
                  //2. 创建一个socket连接
                  socket = new Socket(serverIP,port);
      
                  //3. 创建流对象
                  os = socket.getOutputStream();
      
                  //4. 写入客户端消息
                  os.write("hello world".getBytes());
      
      
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  if (os!=null){
                      try {
                          os.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
                  if (socket!=null){
                      try {
                          socket.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
      
          }
      }
      
  • 服务器

    • 简历服务的端口 ServerSocket

    • 等待用户的连接 accept

    • 接受用的消息

      public class TcpServerDemo01 {
          public static void main(String[] args) {
      
              ServerSocket serverSocket = null;
              Socket socket = null;
              InputStream is = null;
              ByteArrayOutputStream bas = null;
      
              try {
                  //1.我有一个地址
                   serverSocket= new ServerSocket(9999);
      
                  //2.等待客户端连接过来
                   socket =  serverSocket.accept();
      
                  //3.创建流对象
                   is = socket.getInputStream();
                      //缓冲流包装
                  bas = new ByteArrayOutputStream();
      
                  //4. 读取客户端消息
                  byte[] buffer = new byte[1024];
                  int len;
                  while ((len = is.read(buffer)) != -1) {
                      bas.write(buffer,0,len);
                  }
                  System.out.println(bas.toString());
      
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  //关闭资源
                  if (bas!=null){
                      try {
                          bas.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
                  if (is!=null){
                      try {
                          is.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
                  if (socket!=null){
                      try {
                          socket.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
                  if (serverSocket!=null){
                      try {
                          serverSocket.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
      
              }
      
          }
      }
      
      image-20201123101625825

文件上传

  • 客户端

    文件发送到服务器——文件输入流+服务器输出流

    public class TcpClientDemo02 {
        public static void main(String[] args) throws IOException {
            //1. 客户端连接服务器
            InetAddress ip = InetAddress.getByName("localhost");
            int port = 9000;
            Socket socket = new Socket(ip,port);
    
            //2. 创建服务器输出流
            OutputStream out = socket.getOutputStream();
    
            //3.文件从本地发送到服务器
                //本地读取文件
            File target = new File("Src/github.jpg");
            InputStream fis = new FileInputStream(target);
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer))!=-1){
                //服务器输出流发送文件到服务器
                out.write(buffer,0,len);
            }
                //通知服务器我已经发送完毕
            socket.shutdownOutput();
            
    
            //4. 本地收到服务器文本
            InputStream in = socket.getInputStream();
            byte[] buffer2 = new byte[1024];
            while ((len = in.read(buffer2))!=-1) {
                System.out.println(new String(buffer2));
            }
            
            
            //5.关闭资源
            fis.close();
            out.close();
            socket.close();
    
        }
    }
    
  • 服务器端

    文件发送回本地——服务器输入流+文件输出流

    public class TcpServerDemo02 {
        public static void main(String[] args) throws Exception {
            //1. 服务器设置端口,等待接受
            ServerSocket serverSocket = new ServerSocket(9000);
            Socket socket = serverSocket.accept();
    
            //2. 创建服务器输入流
            InputStream in = socket.getInputStream();
    
            //3. 文件从服务器发送到本地
                //服务器输入流读取文件
            OutputStream fos = new FileOutputStream("received.jpg");
            byte[] buffer = new byte[1024];
            int len;
            while ((len = in.read(buffer))!= -1) {
                //通过输出流写入
                fos.write(buffer,0,len);
            }
    
            //4. 服务器发送文本,提示本地,已经发送
            OutputStream out = socket.getOutputStream();
            out.write("我已经收到了".getBytes());
    
            //5. 关闭资源
            fos.close();
            in.close();
            socket.close();
            serverSocket.close();
    
        }
    }
    
    image-20201123101332718

3. Tomcat

服务器

  • 自定义服务器 S
  • Tomcat服务器:Java后台开发

客户端

  • 自定义 C
  • 浏览器 B

Tomcat需要使用8080端口,如果被占用该如何解决

解决端口占用问题

  • 查找占用8080端口的进程,得到它的进程号

    netstat -ano|findstr "8080"
    
    image-20201202205609836
  • 关闭该进程

    taskkill /pid 进程号 /f
    

    或者使用任务管理器关闭进程

    image-20201202205833562

4. UPD协议

DatagramSocket服务端

DtatgramPacket数据包

  • 发送端

    public class UdpClientDemo01 {
        public static void main(String[] args) throws IOException {
            //服务器
            DatagramSocket socket = new DatagramSocket(8888);
    
            //数据装入包
            String msg = "hello";
            InetAddress ip = InetAddress.getByName("127.0.0.1");
            int port = 9999;
            DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,ip,port);
    
            //服务器发送数据包
            socket.send(packet);
    
            //关闭资源
            socket.close();
        }
    }
    
  • 接受端

    public class UdpServerDemo01 {
        public static void main(String[] args) throws IOException {
            DatagramSocket socket = new DatagramSocket(9999);
    
            //接受数据包
            byte[] buffer = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
            socket.receive(packet);//阻塞接受
    
            System.out.println(packet.getAddress());//打印数据包的发送地址
            System.out.println(new String(packet.getData()));//打印数据包的内容
    
            socket.close();
        }
    }
    

    image-20201124111636876

    发送端也可用用于接受内容,接收端同理,因此没有udp谁是服务端这一概念

利用多线程实现对话

  • 发送端

    public class Sender implements Runnable{
        DatagramSocket socket;
        int fromPort;
        String toIP;
        int toPort;
    
        public Sender(int myPort, String toIP, int toPort) {
            this.fromPort = myPort;//端口号
            this.toIP = toIP;
            this.toPort = toPort;
    
            //1. 创建服务器
            try {
                socket = new DatagramSocket(myPort);
            } catch (SocketException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void run() {
            //2. 数据装包
            //存放输入的数据
            while(true) {
                String data = null;//读取输入的数据
                try {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));//控制台输入数据
                    data = reader.readLine();
                    byte[] buffer = data.getBytes();//存放数据
                    DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length, new InetSocketAddress(toIP, toPort));
                    socket.send(packet);
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                if (data.trim().equals("bye")) {
                    break;
                }
            }
    
            socket.close();
        }
    }
    
  • 接收端

    public class Receiver implements Runnable{
        DatagramSocket socket;
        int fromPort;
    
        public Receiver(int fromPort) {
            this.fromPort = fromPort;
            //1. 创建服务器
            try {
                socket = new DatagramSocket(fromPort);
            } catch (SocketException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void run() {
            //2. 接受数据包
            while(true){
                byte[] buffer = new byte[0];
                try {
                    buffer = new byte[1024];
                    DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
                    socket.receive(packet);
    
                    System.out.println(Thread.currentThread().getName()+": "+new String(packet.getData()));
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                //new String()把byte[]转换成字符串
                //trim()去掉字符串中的所有空格
                if (new String(buffer).trim().equals("bye")) {
                    break;
                }
            }
            socket.close();
        }
    }
    
  • 教师

    public class TeacherTalk {
        public static void main(String[] args) {
            //发送
            new Thread(new Sender(8888,"127.0.0.1",7777)).start();
            //接受
            new Thread(new Receiver(6666),"学生").start();
        }
    }
    
  • 学生

    public class StudentTalk {
        public static void main(String[] args) {
            new Thread(new Sender(9999,"127.0.0.1",6666)).start();
            new Thread(new Receiver(7777),"老师").start();
        }
    }
    

    image-20201202201032899image-20201202201053138

5. URL

统一资源定位符:定位资源,定位互联网上的某个资源

DNS域名解析器

https://www.baidu.com/

将域名www.baidu.com解析成ip地址

协议://ip地址:端口/项目/项目名/资源
public class TestURL {
    public static void main(String[] args) throws MalformedURLException {
        URL url = new URL("https://www.baidu.com/");

        System.out.println(url.getProtocol());//协议
        System.out.println(url.getPort());//端口
        System.out.println(url.getAuthority());//域名
        System.out.println(url.getHost());//ip
        System.out.println(url.getPath());//全路径
        System.out.println(url.getQuery());//参数
        System.out.println(url.getFile());//文件
    }
}

URL下载器

在Tomcat中webapps目录下创建一个hello/hello.txt文件,利用URL下载

public class Downloader {
    public static void main(String[] args) throws IOException {
        URL url = new URL("http://localhost:8080/hello/hello.txt");

        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

        InputStream is = urlConnection.getInputStream();

        FileOutputStream fos = new FileOutputStream("hello.txt");

        byte[] buffer = new byte[10];
        int len;
        while ((len = is.read(buffer))!= -1) {
            fos.write(buffer,0,len);
        }

        fos.close();
        is.close();
        urlConnection.disconnect();
    }
}
image-20201202211108390
  • 同样也可以用URL解析下载网络上的资源

    URL url = new URL("https://p2.music.126.net/JoVareIX82eEzbrYtJLAPA==/109951165084884378.jpg?param=34y34");
    
posted @ 2020-12-02 21:31  球球z  阅读(109)  评论(0)    收藏  举报