【韩老师零基础30天学会Java 20】网络编程,TCP和UDP,Socket,使用字节流和字符流,TCP上传下载图片,netstat

网络通信

1.概念:两台设备之间通过网络实现数据传输
2.网络通信:将数据通过网络从一台设备传输到另一台设备3.java.net包下提供了一系列的类或接口,
供程序员使用,完成网络通信

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

IP

网络的相关概念
ip地址

  1. 概念:用于唯一标识网络中的每台计算机

  2. 查看ip地址: ipconfig

  3. ip地址的表示形式:点分十进制 XX.XX.XX.XX

  4. 每一个十进制数的范围:0~255

  5. ip地址的组成=网络地址+主机地址,比如:192.168.16.69

  6. ilPv6是互联网工程任务组设计的用于替代IPv4的下一代IP协议,其地址数量号称可以
    为全世界的每一粒沙子编上一个地址[1]

  7. 由于IPv4最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展。IPv6
    的使用,不仅能解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍

IP 表示:对于IPV44个字节(32位)表示
1个字节的范围是0~255

IPV6使用128位表示地址16个字节是IPV4的四倍

2408:8207:1851:8670: d52d:7a14:362c:4e8d

每个是2个字节

A
0.0.0.0到127.255.255.255
B
128.0.0.0到191.255.255.255
c
192.0.0.0到223.255.255.255
D 多播组号
224.0.0.0到239.255.255.255
E 备用
240.0.0.0到247.255.255.255

特殊的
127.0.0.1表示本机地址

域名

●域名
1.www.baidu.com
2.好处:为了方便记忆,解决记ip的困难3.概念:将ip地址映射成域名
I
●端口号

  1. 概念:用于标识计算机上某个特定的网络程序

  2. 表示形式:以整数形式,范围0~6553 [2个字节表示端口0~2的16次方 -1 ]

  3. 0~1024已经被占用,比如ssh 22, ftp 21, smtp 25 http 80

  4. 常见的网络程序端口号:

    1. tomcat :8080
    2. mysql:3306
    3. oracle:1521
    4. sqlserver:1433

网络协议

网络通信协议协议(tcp/ip)
TCP/IP (Transmission Control
Protocol /Internet Protocol)的简写,中文译名为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是lnternet最基本的协议、Internet国际互联网络的基础,简单地说,就是由网络层的IP协议和传输层的TCP协议组成的。[示意图]

在网络编程中数据的组织形式就是协议

用户数据:下面这四层,对应 了TCP/IP模型的四层

  • Appl首部 应用数据。 经过应用层 加一个app头,代表哪个应用程序发出来的。

  • TCP首部 应用数据

  • IP首部 TCP首部 应用数据

  • 以太网首部 IP首部 TCP首部 应用数据 以太网尾部

OSI模型:应用层表示层会话层传输层网络层数据链路层物理层

TCP/IP模型
应用层:HTTP、ftp、telnet、DNS…
传输层:TCP、UDP、

网络层:IP、ICMP、ARP
物理+数据链路层:Link

ICMP(Internet Control Message Protocol)Internet控制报文协议

TCP 和 UDP

TCP 和UDP√TCP协议:
1.使用TCP协议前,须先建立TCP连接,形成传输数据通道2.传输前,采用"三次握手"方式,
是可靠的
3.TCP协议进行通信的两个应用进程:客户端、服务端4.在连接中可进行大数据量的传输
5.传输完毕,需释放已建立的连接,
效率低
√UDP协议:
1.将数据、源、目的封装成数据包,
不需要建立连接
2.每个数据报的大小限制在64K内
3.因无需连接,故是不可靠的
4.发送数据结束时无需释放资源(因为不是面向连接的),速度快5.举例:厕所通知:发短信

tom 1:你在听吗?

kim 2:我在听

tom 3:下面我给你说一个事情,认真听.

常用逻辑

2.1 IntAddress

相关方法
1.获取本机InetAddress对象 getLocalHost

2.根据指定主机名/域名获取ip地址对象 getByName

3.获取InetAddress对象的主机名getHostName

4.获取InetAddress对象的地址getHostAddress

1.获取本机的信息[主机名/IP]
2.通过域名获取远程服务器的IP

        //1. 获取本机的InetAddress 对象
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost);//DESKTOP-S4MP84S/192.168.12.1

        //2. 根据指定主机名 获取 InetAddress对象
        InetAddress host1 = InetAddress.getByName("DESKTOP-S4MP84S");
        System.out.println("host1=" + host1);//DESKTOP-S4MP84S/192.168.12.1

        //3. 根据域名返回 InetAddress对象, 比如 www.baidu.com 对应
        InetAddress host2 = InetAddress.getByName("www.baidu.com");
        System.out.println("host2=" + host2);//www.baidu.com / 110.242.68.4

        //4. 通过 InetAddress 对象,获取对应的地址
        String hostAddress = host2.getHostAddress();//IP 110.242.68.4
        System.out.println("host2 对应的ip = " + hostAddress);//110.242.68.4

        //5. 通过 InetAddress 对象,获取对应的主机名/或者的域名
        String hostName = host2.getHostName();
        System.out.println("host2对应的主机名/域名=" + hostName); // www.baidu.com

2.2 Socket

基本介绍

  1. 套接字(Socket)开发网络应用程序被广泛采用,以至于成为事实上的标准。

  2. 通信的两端都要有Socket,是两台机器间通信的端点

  3. 网络通信其实就是Socket间的通信。

  4. Socket允许程序把网络连接当成一个流,
    数据在两个Socket间通过IO传输。

  5. 一般主动发起通信的应用程序属客户端
    等待通信请求的为服务端

主机/客户端发起连接
socket

主机/服务端
接收请求连接(监听)socket

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

  1. socket.getOutputStream()

  2. socket.getlnputStream()

Socket的理解

1.TCP编程,可靠
2.UDP编程,不可靠

基本介绍
1.基于客户端-服务端的网络通信

2.底层使用的是TCP/IP协议

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

4.基于Socket的TCP编程

Server
ServerSocket(int port)

Socket accept()

OutputStream
Socker.getOutputStream()

InputStream
Socker.gerInputSream()

Socket.close()

ClientSocket(InetAddressaddress,int port)

OutputStream
Socket.getOutputStream() 先调用输出流,去写出数据。server端调用 输出流 去读取。

InputStream
Socket.getInputStream()

Socker.close

1.编写一个服务器端,和一个客户端

2.服务器端在9999端口监听

3.客户端连接到服务器端,发送"hello, server",然后退出

4.服务器端接收到客户端发送的信息,输出,并退出

服务端思路
1.在本机的9999端口监听,等待连接2.当没有客户端连接9999端口时,程序会阻塞,等待连接
3.通过socket.getlnputStream()读取客户端写入到数据通道的数据,显示

客户端思路
1.连接服务端(ip,端口)
2.连接上后,生成Socket,通过socket.getOutputStream()
3.通过输出流,写入数据到数据通道

client端
        //思路
        //1. 连接服务端 (ip , 端口)
        //解读: 连接本机的 9999端口, 如果连接成功,返回Socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端 socket返回=" + socket.getClass());//class java.net.Socket
        //2. 连接上后,生成Socket, 通过socket.getOutputStream()
        //   得到 和 socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3. 通过输出流,写入数据到 数据通道
        outputStream.write("hello, server".getBytes());
        //4. 关闭流对象和socket, 必须关闭
        outputStream.close();
        socket.close();
        System.out.println("客户端退出.....");
Server端
        //思路
        //1. 在本机 的9999端口监听, 等待连接
        //   细节: 要求在本机没有其它服务在监听9999
        //   细节:这个 ServerSocket 可以通过 accept() 返回多个Socket[多个客户端连接服务器的并发]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端,在9999端口监听,等待连接..");
        //2. 当没有客户端连接9999端口时,程序会 阻塞, 等待连接
        //   如果有客户端连接,则会返回Socket对象,程序继续

        Socket socket = serverSocket.accept();

        System.out.println("服务端 socket =" + socket.getClass());//class java.net.Socket
        //
        //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();
        serverSocket.close();//关闭
字节流服务器回复

3.客户端连接到服务端,发送"hello, server",并接收服务器端回发的
“hello,client”,再退出
4.服务器端接收到客户端发送的信息,输出,并发送"hello, client",再退出

  • server端读取后,再写入。(注意 写入后 要有结束标记)
        //5. 获取socket相关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello, client".getBytes());
        //   设置结束标记
        socket.shutdownOutput();

        //6.关闭流和socket
        outputStream.close();
  • client写入后,再读取(注意 写入后 要有结束标记)
 		//3. 通过输出流,写入数据到 数据通道
        outputStream.write("hello, server".getBytes());
        //设置结束标记
        socket.shutdownOutput();

        //4. 获取和socket关联的输入流. 读取数据(字节),并显示
        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, 必须关闭
        inputStream.close();
使用字符流

设置写入结束标记
writer.newLine()//换行符,注意需要使用readLine()

  • Client端,发送的时候 转为 字符流。最终接收服务端的 也用 字符流。
	   //3. 通过输出流,写入数据到 数据通道, 使用字符流
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello, server 字符流");
        bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束, 注意,要求对方使用readLine()!!!!
        bufferedWriter.flush();// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道


        //4. 获取和socket关联的输入流. 读取数据(字符),并显示
        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();
  • 服务端 同理
 		//4. IO读取, 使用字符流, 老师使用 InputStreamReader 将 inputStream 转成字符流
        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();//注意需要手动的flush


        //6.关闭流和socket
        bufferedWriter.close();
        bufferedReader.close();

        socket.close();
        serverSocket.close();//关闭
再次复制完整的代码
客户端
  • 只能聊一次的聊天室

  • 就是问题让字节输入

  • 客户端判断,问题,进行回复

  • 其他 和 客户端 发消息,接收消息。服务端 接收消息,回复消息一样。

//思路
        //1. 连接服务端 (ip , 端口)
        //解读: 连接本机的 9999端口, 如果连接成功,返回Socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);

        //===============================问一个问题========================================
        //2. 连接上后,生成Socket, 通过socket.getOutputStream()
        //   得到 和 socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3. 通过输出流,写入数据到 数据通道, 使用字符流
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));

        //从键盘读取用户的问题
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你的问题");
        String question = scanner.next();

        bufferedWriter.write(question);
        bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束, 注意,要求对方使用readLine()!!!!
        bufferedWriter.flush();// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道


        //===============================收到问题的答案========================================
        //4. 获取和socket关联的输入流. 读取数据(字符),并显示
        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("客户端退出.....");
服务端
        //思路
        //1. 在本机 的9999端口监听, 等待连接
        //   细节: 要求在本机没有其它服务在监听9999
        //   细节:这个 ServerSocket 可以通过 accept() 返回多个Socket[多个客户端连接服务器的并发]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端,在9999端口监听,等待连接..");
        //2. 当没有客户端连接9999端口时,程序会 阻塞, 等待连接
        //   如果有客户端连接,则会返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        //===============================读取一个问题========================================
        //3. 通过socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
        InputStream inputStream = socket.getInputStream();
        //4. IO读取, 使用字符流, 老师使用 InputStreamReader 将 inputStream 转成字符流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        String answer = "";
        if ("name".equals(s)) {
            answer = "我是韩顺平";
        } else if ("hobby".equals(s)) {
            answer = "编写java程序";
        } else {
            answer = "你说的啥子";
        }


        //===============================准备好问题的答案,回写========================================
        //5. 获取socket相关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        //    使用字符输出流的方式回复信息
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write(answer);
        bufferedWriter.newLine();// 插入一个换行符,表示回复内容的结束
        bufferedWriter.flush();//注意需要手动的flush


        //===============================关流========================================
        //6.关闭流和socket
        bufferedWriter.close();
        bufferedReader.close();
        socket.close();
        serverSocket.close();//关闭

2.3 上传图片

工具类
/**
 * 此类用于演示关于流的读写方法
 */
public class StreamUtils {
    /**
     * 功能:将输入流转换成byte[]
     *
     * @param is
     * @return
     * @throws Exception
     */
    public static byte[] streamToByteArray(InputStream is) throws Exception {
        //创建输出流对象。因为要转为 字节数组,所以用 ByteArray 输出流。
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        int len;
        while ((len = is.read(b)) != -1) {
            bos.write(b, 0, len);
        }
        byte[] array = bos.toByteArray();
        bos.close();
        return array;
    }

    /**
     * 功能:将InputStream转换成String
     *
     * @param is
     * @return
     * @throws Exception
     */

    public static String streamToString(InputStream is) throws Exception {
        //转为 字符流
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        //使用 StringBuilder接收
        StringBuilder builder = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) { //当读取到 null时,就表示结束
            builder.append(line + "\r\n");
        }
        return builder.toString();
        
        //不使用 缓冲字符输入流 也行。
        /*byte[] b = new byte[1024];
        int len;
        while ((len = inputStream.read(b)) != -1) {
            sb.append(new String(b, 0, len));
        }*/
        
    }

}
客户端
        //客户端连接服务端 8888,得到Socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
        //创建读取磁盘文件的输入流
        //String filePath = "e:\\qie.png";
        String filePath = "e:\\abc.mp4";

        //直接用 FileInputStream,也可。
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));

        //bytes 就是filePath对应的字节数组
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        bis.close();

        //老师使用了 字节缓冲输出流
        //通过socket获取到输出流, 将bytes数据发送给服务端
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        //直接用  OutputStream.write(bytes); 也行。
        bos.write(bytes);//将文件对应的字节数组的内容,写入到数据通道
        //设置写入数据的结束标记
        socket.shutdownOutput();
        //bos.close();//注意了,这个流,不能这里关。要放在后面,因为下面还要用 socket.getInputStream();

        //=====接收从服务端回复的消息=====

        InputStream inputStream = socket.getInputStream();
        //使用StreamUtils 的方法,直接将 inputStream 读取到的内容 转成字符串
        String s = StreamUtils.streamToString(inputStream);
        System.out.println(s);
        //关闭相关的流
        inputStream.close();//先关上面的: socket.getInputStream()
        bos.close();//在关:socket.getOutputStream()

        socket.close();
服务端
        //1. 服务端在本机监听8888端口
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端在8888端口监听....");
        //2. 等待连接
        Socket socket = serverSocket.accept();


        //3. 读取客户端发送的数据
        //   通过Socket得到输入流。直接把:InputStream 传递给工具类也行。
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        //bis.close();//这个流 要放在下面关。因为下面还要用 socket.getOutputStream()

        //4. 将得到 bytes 数组,写入到指定的路径,就得到一个文件了
        String destFilePath = "src\\abc.mp4";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        //不用缓冲输出流。直接用 fileOutputStream.write(bytes); 也行。
        bos.write(bytes);
        bos.close();

        // 向客户端回复 "收到图片"
        // 通过socket 获取到输出流(字符)。这里使用了 字符输入流
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        writer.write("收到图片");
        writer.flush();//把内容刷新到数据通道
        //使用字节 输出流,也行。
        //outputStream.write("收到图片了".getBytes());
        socket.shutdownOutput();//设置写入结束标记
        //关闭其他资源
        writer.close(); //先关最新的 socket.getOutputStream()
        bis.close();//在关:socket.getInputStream()

        socket.close();
        serverSocket.close();
关任一 一个socket.getInputStream() 或 output 就不能在用 socket获得流了,会报错。所以:流要代码最后关。
java.net.SocketException: Socket is closed

2.4 下载文件

极简代码
  • 就是 不用工具类(数组流)中转,也不用 缓冲流。
		ServerSocket serverSocket = new ServerSocket(8888);
        //获取 客户端
        Socket socket = serverSocket.accept();

        /*//获取输入流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //获取客户端的输入
        String downLoad = bufferedReader.readLine();*/

		//**** 服务端:就是使用 输出流,循环输出 字节数组。字节数组是 文件输入流 读取的。
        OutputStream outputStream = socket.getOutputStream();
        FileInputStream fis = new FileInputStream("e:\\高山流水.mp3");

        //进行读取
        byte[] buf = new byte[1024];
        int len;
        //写入到 客户端
        while ((len = fis.read(buf)) != -1) {
            outputStream.write(buf, 0, len);
        }
        //结束标志
        socket.shutdownOutput();

        fis.close();
        outputStream.close();

        //bufferedReader.close();
        socket.close();
        serverSocket.close();
	    Socket socket = new Socket(InetAddress.getLocalHost(), 8888);

        /*//写入数组
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("高山流水".getBytes());
        //写入结束符
        socket.shutdownOutput();*/

	    //*** 客户端依然很简单:就是 获取到输入流,循环读取。然后使用 文件输出流,循环写入。
        //读取
        InputStream inputStream = socket.getInputStream();

        //读取 服务端的歌曲
        FileOutputStream fos = new FileOutputStream("e:\\下载.mp3");

        //写入到 输出流
        byte[] buf = new byte[1024];
        int len;
        while ((len = inputStream.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }

        //关流
        fos.close();
        inputStream.close();

        //outputStream.close()
        socket.close();
服务端
        //1 监听 9999端口
        ServerSocket serverSocket = new ServerSocket(9999);
        //2.等待客户端连接
        System.out.println("服务端,在9999端口监听,等待下载文件");
        Socket socket = serverSocket.accept();

        //=====================1. 读取出 客户端要下载的字符=====================
        //3.读取 客户端发送要下载的文件名
        //  这里老师使用了while读取文件名,时考虑将来客户端发送的数据较大的情况
        InputStream inputStream = socket.getInputStream();
        byte[] b = new byte[1024];
        int len = 0;
        String downLoadFileName = "";
        while ((len = inputStream.read(b)) != -1) {
            downLoadFileName += new String(b, 0, len);
        }
        System.out.println("客户端希望下载文件名=" + downLoadFileName);

        //老师在服务器上有两个文件, 无名.mp3 高山流水.mp3
        //如果客户下载的是 高山流水 我们就返回该文件,否则一律返回 无名.mp3

        String resFileName = "";
        if ("高山流水".equals(downLoadFileName)) {
            resFileName = "src\\高山流水.mp3";
        } else {
            resFileName = "src\\无名.mp3";
        }

        //=====================2. 缓冲字节输入流读取文件,转为 字节数组。缓冲字节输出流写入=====================
        //4. 创建一个输入流,读取文件
        BufferedInputStream bis =
                new BufferedInputStream(new FileInputStream(resFileName));

        //5. 使用工具类StreamUtils ,读取文件到一个字节数组

        byte[] bytes = StreamUtils.streamToByteArray(bis);
        //6. 得到Socket关联的输出流
        BufferedOutputStream bos =
                new BufferedOutputStream(socket.getOutputStream());
        //7. 写入到数据通道,返回给客户端
        bos.write(bytes);
        socket.shutdownOutput();//很关键.



        //8 关闭相关的资源
        bis.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端退出...");
客户端
        //1. 接收用户输入,指定下载文件名
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入下载文件名");
        String downloadFileName = scanner.next();

        //2. 客户端连接服务端,准备发送
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);

        //========================1.使用输出流 写入要下载的 文件名===========================
        //3. 获取和Socket关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write(downloadFileName.getBytes());
        //设置写入结束的标志
        socket.shutdownOutput();

        //=============2.缓冲字节输入流 读取文件 转为 字节数组。使用 缓冲字节输出流 保存为文件===========
        //4. 读取服务端返回的文件(字节数据)
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        //5. 得到一个输出流,准备将 bytes 写入到磁盘文件
        //比如你下载的是 高山流水 => 下载的就是 高山流水.mp3
        //    你下载的是 韩顺平 => 下载的就是 无名.mp3  文件名 韩顺平.mp3
        String filePath = "e:\\" + downloadFileName + ".mp3";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
        bos.write(bytes);

        //6. 关闭相关的资源
        bos.close();
        bis.close();
        outputStream.close();
        socket.close();

        System.out.println("客户端下载完毕,正确退出..");

网络通信编程

netstat

netstat 指令

  1. netstat -an可以查看当前主机网络情况,包括端口监听情况和网络连接情况

  2. netstat -an | more 可以分页显示

    1. -anb,b参数 可以查看是哪个程序正在监听这个端口。需要管理员权限。
  3. 要求在dos控制台下执行

说明:
(1) Listening表示某个端口在监听
(2)如果有一个外部程序(客户端)连接到该端口,就会显示一条连接信息.

命令输入后出现:协议——本地地址——外部地址——状态

外部地址:就是 客户端连接的地址和端口。当然 客户端,也可能是 当前本机。

客户端随机端口

1.当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,

个端口是TCP/IP 来分配的

如:输入netstat -an 下面的

TCP
192.168.12.1:8888 服务端
192.168.12.1:60285 客户端的随机端口
ESTABLISHED

如果客户端和服务端在同一个电脑,还会有:192.168.12.1:60285 到 192.168.12.1:8888 的通道联系起来了

establish ed
adj.
确立已久的,早已投入使用的;资深的,知名的;(植物)已生根的,长势良好的;<英>已被认可的,已被接受的(尤指英国国教)

v.
建立,设立;证实,确定;发现,找出(establish 的过去式和过去分词形式)

UDP

基本介绍

  1. 类 DatagramSocket和 DatagramPacket实现了基于UDP 协议网络程序。

  2. UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP
    数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。

  3. DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和
    端口号以及接收端的IP地址和端口号。

  4. UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方
    的连接

1.核心的两个类/对象 DatagramSocket与DatagramPacket

2.建立发送端,接收端

3.发送数据前,建立数据包/报DatagramPacket

4.调用DatagramSocket的发送、接收方法

5.关闭DatagramSocket

没有 服务端

程序/发送端B
程序/接收端A

UDP说明:
1.没有明确的服务端和客户端,演变成数据的发送端和接收端

2.接收数据和发送数据是通过DatagramSocket 对象/装包 完成

3.将数据封装到DatagramPacket对象,发送

  • 包含数握, ip,端口

4.当接收到DatagramPacket 对象,需要进行拆包
取出数据

5.DatagramSocket 可以指定在哪个端口接收数据

Datagram(数据报)

gram
n.
克(公制重量单位,1000克等于1公斤);(用作食物的)鹰嘴豆(或其他豆类植物);<美,非正式>奶奶,外婆(同 gramma)

代码示例

DatagramPacket(byte[], int, int) //接收
DatagramPacket(byte[], int数组的开始, int结束, InetAddress, int端口号) //发送

1.编写一个接收端A,和一个发送端B
2.接收端A在9999端口等待接收数据(receive)
3.发送端B向接收端A发送数据"hello,明天吃火锅~"
4.接收端A接收到发送端B发送的数据,回复
“好的,明天见",再退出
5.发送端接收回复的数据,再退出

发送端
//1.创建 DatagramSocket 对象,准备在9998端口 接收数据
        DatagramSocket socket = new DatagramSocket(9998);

        //2. 将需要发送的数据,封装到 DatagramPacket对象
        byte[] data = "hello 明天吃火锅~".getBytes(); //

        //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
        DatagramPacket packet =
                new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9999);

        socket.send(packet);

        //3.=== 接收从A端回复的信息
        //(1)   构建一个 DatagramPacket 对象,准备接收数据
        //   在前面讲解UDP 协议时,老师说过一个数据包最大 64k
        byte[] buf = new byte[1024];
        packet = new DatagramPacket(buf, buf.length);
        //(2)    调用 接收方法, 将通过网络传输的 DatagramPacket 对象
        //   填充到 packet对象
        //老师提示: 当有数据包发送到 本机的9998端口时,就会接收到数据
        //   如果没有数据包发送到 本机的9998端口, 就会阻塞等待.
        socket.receive(packet);

        //(3)  可以把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端退出");
接收端
  • 当然 ,也是会给发送端 发送消息的
        //1. 创建一个 DatagramSocket 对象,准备在9999接收数据
        DatagramSocket socket = new DatagramSocket(9999);
        //2. 构建一个 DatagramPacket 对象,准备接收数据
        //   在前面讲解UDP 协议时,老师说过一个数据包最大 64k
        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        //3. 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
        //   填充到 packet对象
        //老师提示: 当有数据包发送到 本机的9999端口时,就会接收到数据
        //   如果没有数据包发送到 本机的9999端口, 就会阻塞等待.
        System.out.println("接收端A 等待接收数据..");
        socket.receive(packet);

        //4. 可以把packet 进行拆包,取出数据,并显示.
        int length = packet.getLength();//实际接收到的数据字节长度
        byte[] data = packet.getData();//接收到数据
        String s = new String(data, 0, length);
        System.out.println(s);


        //===回复信息给B端
        //将需要发送的数据,封装到 DatagramPacket对象
        data = "好的, 明天见".getBytes();
        //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
        packet =
                new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9998);

        socket.send(packet);//发送

        //5. 关闭资源
        socket.close();
        System.out.println("A端退出...");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值