韩顺平零基础30天学会Java【章17 网络】

P662~P710

网络基础

网络通信

概念:两台设备之间通过网络实现数据传输

网络通信:将数据通过网络从一台设备传输到另一台设备

java.net包下提供了一系列的类或接口,供程序员使用,完成网络通信

网络

  • 概念:两台或多台设备通过一定物理设备连接起来,构成了网络。
  • 根据网络的覆盖范围不同,对网络进行分类:
  • 局域网:覆盖范围最小,仅仅覆盖一个教室或机房
  • 城域网:覆盖范围较大,可以覆盖一个城市
  • 广域网:覆盖范围最大,可以覆盖全国甚至全球,万维网是广域网的代表

ip地址

  • 概念:用于唯一标识网络中的每台计算机
  • 查看ip地址:ipconfig
  • ip地址表示形式:点分十进制 xx.xx.xx.xx
  • 每一个十进制数范围0~255
  • ip地址的组成=网络地址+主机地址
  • ipv6是用于替代ipv4的下一代ip协议,地址数量号称可以为全世界的每一粒沙子编上一个地址
  • 由于ipv4的网络地址资源有限,严重制约了互联网的应用和发展,ipv6的使用,可以解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍

域名

  • 概念:将ip地址映射成域名
  • 好处:方便记忆

端口号

  • 概念:用于标识计算机上某个特定的网络程序
  • 表示形式:0~65535
  • 0~1024已被占用,比如 ssh 22,ftp 21,smtp 25,http 80
  • 常见网络程序端口号:
    • tomcat:8080
    • mysql:3306
    • oracle:1521
    • sqlserver:1433

网络通信协议

TCP/IP:传输控制协议,是Internet最基本的协议,是由网络层的IP协议和传输层的TCP协议组成

TCP、UDP

TCP协议:

  • 使用TCP协议前,需要先建立TCP连接,形成传输数据通道
  • 传输前采用三次握手方式,是可靠的
  • TCP协议进行通信的两个应用程序:客户端、服务端
  • 在链接中可进行大数据量的传输
  • 传输完毕,需释放已建立的连接,效率低

UDP协议:

  • 将数据、源、目的封装成数据包,不需要建立连接
  • 每个数据包的大小限制在64K内
  • 因无需连接,是不可靠的
  • 发送数据结束时无需释放资源,速度快

InetAddress类,InetAddress_api.java

相关方法:

  • 获取本机InetAddress对象 getLocalHost
  • 根据指定主机名/域名获取ip地址对象getByName
  • 获取InetAddress对象的主机名getHostName
  • 获取InetAddress对象的地址 getHostAddress
import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddress_api {
    public static void main(String[] args) throws UnknownHostException {
        //获取本机InetAddress对象
        InetAddress localHost= InetAddress.getLocalHost();
        System.out.println(localHost);
        //根据指定主机名获取InetAddress对象
//        InetAddress host1=InetAddress.getByName("");
        //根据域名获取InerAddress对象
        InetAddress host2 = InetAddress.getByName("www.baidu.com");
        System.out.println(host2);
        //通过InetAddress对象,获取对应地址
        String hostAddress= host2.getHostAddress();
        System.out.println("host2对应的ip"+hostAddress);
        //通过InetAddress对象,获取对应主机名/域名
        String hostName=host2.getHostName();
        System.out.println("host2对应主机名/域名"+hostName);
    }
}

Socket

  • 套接字(Socket)开发网络应用程序被广泛采用, 以至于成为事实上的标准。
  • 通信的两端都要有socket, 是两台机器间通信的端点。
  • 网络通信实际就是socket间的通信。
  • Socket允许程序把网络连接当成一个流。 数据在两个socket之间通过IO传输。
  • 一般主动发起通信的应用程序属于客户端, 等待通讯请求的为服务端。

Socket过程

当我们需要通讯时(读写数据)

socket使用方法getOutputStream()、getInputStream()

当客户端和服务端连接成功,如果客户端要读数据,socket使用getOutputStream,将数据传到数据通道中,服务器端socket使用getInputStream读取数据。服务器端给客户端数据同理。

TCP网络通信编程

基本介绍

基于客户端服务端的网络通信

底层使用的是TCP/IP协议

应用场景:客户端发送数据,服务端接受并显示

过程:

  1. 服务器端监听,等待连接
  2. 连接成功后,socket使用getOutputStream得到输出流,将数据发送给server,server的socket使用getInputStream
  3. 关闭socket

应用案例1,SocketTCP01Server.java、SocketTCP01Client.java(字节流)

  1. 编写一个服务器端和一个客户端。
  2. 服务器端在9999端口监听。
  3. 客户端连接到服务器端发送hello,然后退出。
  4. 服务器端接收到客户端发送信息,输出并退出。
//SocketTCP01Server

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

public class SocketTCP01Server {
    public static void main(String[] args) throws IOException {
        //1、在本机的9999端口监听,等待连接
        // 要求在本机没有其他服务在监听9999
        // 这个Serversocket可以通过accept()返回多个Socket,多个客户端连接服务器的并发
        ServerSocket serverSockrt = new ServerSocket(9999);
        System.out.println("服务器在9999端口监听,等待连接");
        //2、当没有客户端连接9999端口时,程序会阻塞,等待连接
        //如果有客户端连接,届时会返回Socket,程序继续
        Socket socket = serverSockrt.accept();
        System.out.println("服务器端 socket=" + socket.getClass());
        //3、通过socket.getInputStream读取客户端写入到数据通道数据的数据,并显示
        InputStream inputStream = socket.getInputStream();
        //4、IO读取
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            //根据读取到的实际长度显示内容
            System.out.println(new String(buf, 0, readLen));
        }
        //5、关闭流和socket
        inputStream.close();
        socket.close();
        serverSockrt.close();
    }
}



//SocketTCP01Client
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;


public class SocketTCP01Client {
    public static void main(String[] args) throws IOException {
        //1、连接服务端(ip,端口)
        //连接本机的9999端口,如果连接成功,返回Socket
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端 socket返回" + socket.getClass());
        //2、连上后,生成Socket,通过socket.getOutputStream
        //得到和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3、通过输出流,写入数据到数据通道
        outputStream.write("hello.server".getBytes(StandardCharsets.UTF_8));
        //4、关闭流对象和socket
        outputStream.close();
        socket.close();
        System.out.println("客户端退出");
    }
}


应用案例2,SocketTCP02Server.java、SocketTCP02Client.java(字节流)

  1. 编写一个服务器端,和一个客户端
  2. 服务器端在9999端口监听
  3. 客户端连接到服务器端,发送“hello,server”,并接收服务器端发回的“hello,client”,再退出
  4. 服务器端接收到客户端发送的信息,输出,并发送“hello,client”,再退出
//SocketTCP02Server
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class SocketTCP02Server {
    public static void main(String[] args) throws IOException {
        //1、在本机的9999端口监听,等待连接
        // 要求在本机没有其他服务在监听9999
        // 这个Serversocket可以通过accept()返回多个Socket,多个客户端连接服务器的并发
        ServerSocket serverSockrt = new ServerSocket(9999);
        System.out.println("服务器在9999端口监听,等待连接");
        //2、当没有客户端连接9999端口时,程序会阻塞,等待连接
        //如果有客户端连接,届时会返回Socket,程序继续
        Socket socket = serverSockrt.accept();
        System.out.println("服务器端 socket=" + socket.getClass());
        //3、通过socket.getInputStream读取客户端写入到数据通道数据的数据,并显示
        InputStream inputStream = socket.getInputStream();
        //4、IO读取
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            //根据读取到的实际长度显示内容
            System.out.println(new String(buf, 0, readLen));
        }
        //5、获取socket相关联的输出流
        OutputStream outputStream=socket.getOutputStream();
        outputStream.write("hello,client".getBytes(StandardCharsets.UTF_8));
        //设置结束标记
        socket.shutdownOutput();
        //6、关闭流和socket
        inputStream.close();
        outputStream.close();
        socket.close();
        serverSockrt.close();
    }
}

//SocketTCP02Client
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class SocketTCP02Client {
    public static void main(String[] args) throws IOException {
        //1、连接服务端(ip,端口)
        //连接本机的9999端口,如果连接成功,返回Socket
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端 socket返回" + socket.getClass());
        //2、连上后,生成Socket,通过socket.getOutputStream
        //得到和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3、通过输出流,写入数据到数据通道
        outputStream.write("hello.server".getBytes(StandardCharsets.UTF_8));
        //设置结束标记
        socket.shutdownOutput();
        //4、IO读取
        InputStream inputStream = socket.getInputStream();
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            //根据读取到的实际长度显示内容
            System.out.println(new String(buf, 0, readLen));
        }
        //5、关闭流对象和socket
        outputStream.close();
        inputStream.close();
        socket.close();
        System.out.println("客户端退出");
    }
}

应用案例3,SocketTCP03Server.java、SocketTCP03Client.java(字符流)

  1. 编写一个服务器端,和一个客户端
  2. 服务器端在9999端口监听
  3. 客户端连接到服务器端,发送“hello,server”,并接收服务器端发回的“hello,client”,再退出
  4. 服务器端接收到客户端发送的信息,输出,并发送“hello,client”,再退出
//SocketTCP03Server
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class SocketTCP03Server {
    public static void main(String[] args) throws IOException {
        //1、在本机的9999端口监听,等待连接
        // 要求在本机没有其他服务在监听9999
        // 这个Serversocket可以通过accept()返回多个Socket,多个客户端连接服务器的并发
        ServerSocket serverSockrt = new ServerSocket(9999);
        System.out.println("服务器在9999端口监听,等待连接");
        //2、当没有客户端连接9999端口时,程序会阻塞,等待连接
        //如果有客户端连接,届时会返回Socket,程序继续
        Socket socket = serverSockrt.accept();
        System.out.println("服务器端 socket=" + socket.getClass());
        //3、通过socket.getInputStream读取客户端写入到数据通道数据的数据,并显示
        // 将InputStream-》Reader,使用转换流InputStreamReader()
        InputStream inputStream = socket.getInputStream();
        //4、IO读取,使用readLine(),字符流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);
        //5、获取socket相关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        //使用字符流回复信息
        BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello,client,字符流");
        bufferedWriter.newLine();
        bufferedWriter.flush();//手动刷新
        //6、关闭流和socket
        bufferedWriter.close();
        bufferedReader.close();
        socket.close();
        serverSockrt.close();
    }
}
//SocketTCP03Client
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class SocketTCP03Client {
    public static void main(String[] args) throws IOException {
        //1、连接服务端(ip,端口)
        //连接本机的9999端口,如果连接成功,返回Socket
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端 socket返回" + socket.getClass());
        //2、连上后,生成Socket,通过socket.getOutputStream
        //得到和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3、通过输出流,写入数据到数据通道,使用字符流
        //将OutputStream-》Writer,需要使用转换流OutputSreamWriter(字节流)
        BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello.server");
        //设置写入结束标记,writer.newLine()
        bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束,需要对方使用readLine()
        bufferedWriter.flush();//如果使用字符流需要手动刷新,否则数据不会写入数据通道

        //4、IO读取
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
        String s=bufferedReader.readLine();
        System.out.println(s);
        //5、关闭流对象和socket
        bufferedReader.close();
        bufferedWriter.close();
        socket.close();
        System.out.println("客户端退出");
    }
}

应用案例4,TCPFileCopyServer.java、TCPFileCopyClient.java

  1. 编写一个服务器端,和一个客户端
  2. 服务器端在9999端口监听
  3. 客户端连接到服务器端,发送一张图片
  4. 服务器端接收客户端发送的图片,保存到src下,发送“收到图片”再退出
  5. 客户端收到服务器端的“收到图片”,再退出
  6. 要求使用StreamUtils.java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPFileCopyServer {
    public static void main(String[] args) throws IOException {
        //1、在本机的9999端口监听,等待连接
        // 要求在本机没有其他服务在监听9999
        // 这个Serversocket可以通过accept()返回多个Socket,多个客户端连接服务器的并发
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务器在9999端口监听,等待连接");
        //2、当没有客户端连接9999端口时,程序会阻塞,等待连接
        //如果有客户端连接,届时会返回Socket,程序继续
        Socket socket = serverSocket.accept();
        //3、读取客户端发送的数据
        //通过socket得到输入流
        BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtil.streamToByteArray(bufferedInputStream);
        //将得到bytes数组,写入到指定的路径,得到一个文件
        String FilePath = "./17网络/src/bg.jpg";
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(FilePath));
        bufferedOutputStream.write(bytes);
        bufferedOutputStream.close();
        //4、通过socket获取输出流
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bufferedWriter.write("收到");
        bufferedWriter.flush();
        socket.shutdownOutput();
        //5、关闭其他功能
        bufferedWriter.close();
        bufferedInputStream.close();
        socket.close();
        serverSocket.close();
    }
}

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

public class TCPFileCopyClient {
    public static void main(String[] args) throws Exception {
        //客户端连接服务器,得到Socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        //创建读取磁盘文件的输入流
        String filePath= "./17网络/bg.jpg";
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
        //bytes就是文件对应的字节数组
        byte[] bytes = StreamUtil.streamToByteArray(bufferedInputStream);
        //通过socket获取输出流,将bytes发送到服务端
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
        bufferedOutputStream.write(bytes);
        bufferedInputStream.close();
        socket.shutdownOutput();//写入结束
        //接收从服务端返回的消息
        InputStream inputStream = socket.getInputStream();
        //使用StreamUtils 的方法,直接将inputStream读到的内容专成字符串
        String s = StreamUtil.streamToString(inputStream);
        System.out.println(s);
        inputStream.close();
        //关闭其他资源
        bufferedOutputStream.close();
        socket.close();
    }
}


import java.io.*;
public class StreamUtil {
    //功能:将输入流转换成byte[],即可以把文件的内容读入到byte[]
    public static byte[] streamToByteArray(InputStream is) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
        byte[] b = new byte[1024];//字节数组
        int len;
        while ((len = is.read(b)) != -1) {
            System.out.println(new String(b, 0, len));
        }
        byte[] array = bos.toByteArray();//将字节输出流转换成为数组的形式
        bos.close();
        return array;
    }
    public static String streamToString(InputStream is) throws Exception{
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder builder = new StringBuilder();
        String line;
        while((line= reader.readLine())!=null){
            builder.append(line+"\r\n");
        }
        return  builder.toString();
    }
}


netstat指令

  • netstat -an 可以查看当前主机网络情况,可以看到端口监听情况和网络连接情况
  • netstat -an | more可以分页显示
  • 要求在dos控制台下执行

Listening表示某个端口在监听

如果有一个外部程序连接到该端口,就会显示一条连接信息

TCP网络通讯秘密

当客户端连接到服务器端,实际上客户端也是通过一个端口和服务端进行通讯的这个端口是TCP/IP来随机分配的

UDP编程【了解】

  1. 类DatagramSocket和DatagramPacket实现了基于UDP协议网络程序
  2. UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达
  3. DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号
  4. UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接

基本流程

  1. 核心的两个类/对象 DatagramSocket和DatagramPacket
  2. 建立发送端,接收端
  3. 建立数据报
  4. 调用类DatagramSocket的发送、接收方法
  5. 关闭DatagramSocket

UDP说明

  1. 没有明确的服务端和客户端,演变成数据的发送端和接收端
  2. 接收数据和发送数据是通过DatagramSocket对象完成
  3. 将数据封装到DatagramPacket对象,装包
  4. 当接收到DatagramPacket对象,进行拆包,取出数据
  5. DatagramSocket可以指定在哪个端口接收数据

应用案例,UDPReceiverA.java、UDPReceiverB.java

  1. 编写一个接收端A,和一个发送端B
  2. 接收端A在9999端口等待接收数据
  3. 发送端B向接收端A发送数据“hello”
  4. 接收端A接收到发送端B发送的数据,回复“hi”,再退出
  5. 发送端接收回复的数据再退出
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class UDPReceiverB {
    public static void main(String[] args) throws IOException {
        //创建DatagramSocket,准备发送和接收数据
        DatagramSocket socket = new DatagramSocket(9998);
        //将需要发送的数据封装到DatagramPacket
        byte[] data = "hello".getBytes(StandardCharsets.UTF_8);

//        DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("ip地址"), 9999);
        DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 9999);
        socket.send(packet);

        byte[] bytes = new byte[1024];
        packet = new DatagramPacket(bytes, bytes.length);
        //调用接收方法,将网络传输的DatagramPacket对象填充到packet对象
        //如果没有数据报发送到9999,就会阻塞
        System.out.println("B准备接收数据");
        socket.receive(packet);
        //可以把packet拆包,去输出句并显示
        int length = packet.getLength();//实际接收到的数据字节长度
        data = packet.getData();//接收到的数据
        String s = new String(data, 0, length);
        System.out.println(s);
        socket.close();
        System.out.println("B端退出");
    }
}
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class UDPReceiverA {
    public static void main(String[] args) throws IOException {
        //创建一个DatagramSocket对象,准备在9999接收数据
        DatagramSocket socket = new DatagramSocket(9999);
        //构建一个DataPacket对象,准备接收数据
        byte[] bytes = new byte[1024];
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
        //调用接收方法,将网络传输的DatagramPacket对象填充到packet对象
        //如果没有数据报发送到9999,就会阻塞
        System.out.println("A准备接收数据");
        socket.receive(packet);
        //可以把packet拆包,去输出句并显示
        int length = packet.getLength();//实际接收到的数据字节长度
        byte[] data = packet.getData();//接收到的数据
        String s = new String(data, 0, length);
        System.out.println(s);

        data = "hi".getBytes(StandardCharsets.UTF_8);
        //DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("ip地址"), 9999);
        packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 9998);
        socket.send(packet);

        socket.close();
        System.out.println("A端退出");
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_65656674/article/details/126566531