网络编程

1. 简介

  • 网络:两台或多台设备通过一定物理设备连接起来构成了网络
  • 网络通信:两台设备之间通过网络实现数据传输
  • ip地址:用于唯一标识网络中的每台计算机
    • 查看ip地址:ipconfig
    • ip地址组成:网络地址+主机地址
    • IPv4和IPv6
    • IPv4表示:4个字节(32位)0~255
    • IPv6表示:16个字节(128位)

a. 域名:将ip地址映射成域名

b. 端口号:用于标识计算机上某个特定的网络程序065535,01024已被占用,smtp 25,http 80

TCP协议:

  • 三次握手
  • 可靠,速度慢

UDP协议(用户数据协议):

  • 大小限制64k
  • 速度快

2. InetAddress类

常用方法:

  • getLocalHost() 返回本地主机。
  • getByName() 在给定主机名的情况下确定主机的 IP 地址。
  • getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
  • getHostName() 获取此 IP 地址的主机名。
package com.xxx;

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

public class API_ {
    public static void main(String[] args) throws UnknownHostException {
        //返回本机的主机名/ip地址
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost);//DESKTOP-T5RI7XC/192.168.22.127

        //根据指定主机名,获取InetAddress对象
        InetAddress inetAddress = InetAddress.getByName("DESKTOP-T5RI7TC");
        System.out.println(inetAddress);//DESKTOP-T5RI7XC/192.168.22.127

        //根据域名返回InetAdddress对象
        InetAddress inetAddress1 = InetAddress.getByName("www.baidu.com");
        System.out.println(inetAddress1);//www.baidu.com/182.61.200.6

        //根据InetAddress对象,获取对应的地址
        String hostAddress = inetAddress.getHostAddress();
        System.out.println(hostAddress);//192.168.22.127

        //根据InetAddress对象,获取对应的主机名/域名
        String hostName = inetAddress1.getHostName();
        System.out.println(hostName);//www.baidu.com
    }
}

3. Socket套接字

简介:

Socket网络应用程序被广泛使用,成为事实上的标准。

通信的两端都要有Socket,网络通信其实就是Socket通信。

客户端:发起通信的应用程序

服务端:等待通信请求

package com.xxx;

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

public class SocketTcpServer {
    public static void main(String[] args) throws IOException {
        //思路
        //1.在本机的9999端口监听, 等待连接
        //如果本机有其他服务已经在监听9999,那么会失败
        ServerSocket serverSocket = new ServerSocket (9999);
        System.out.println("服务端在9999端口监听");
        //2. 当没有客户端连接9999端口时,程序会阻塞,等待连接
        //如果有客户端连接,则会返回Socket对象,程序继续往下执行
        Socket socket = serverSocket.accept();
        System.out.println("服务端socket=" + socket.getClass());
        //3.通过socket.getInputStream() 读取客户端写入到数据通道的数据,显示
        InputStream inputStream = socket.getInputStream();
//        //4.通过IO流读取,字节流
//        byte[] buffer = new byte[1024];
//        int readLen = -1;
//        while ((readLen = inputStream.read(buffer)) != -1) {
//            System.out.println(new String(buffer,0,readLen));
//        }
        //通过IO读取,字符流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);

        //5.获取socket相关的输出流
        OutputStream outputStream = socket.getOutputStream();
//        outputStream.write("Hello Client".getBytes());
        //使用字符输出流回复信息
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("Hello Client 字符流");
        bufferedWriter.newLine();//插入一个换行符,表示回复内容结束
        bufferedWriter.flush();
        System.out.println();

        //6.关闭流和socket
//        outputStream.close();
        bufferedWriter.close();//关闭外层流就行
//        inputStream.close();
        bufferedReader.close();//关闭外层流就行
        socket.close();
        serverSocket.close();//服务socket也要关闭
    }
}

package com.xxx;

import sun.reflect.generics.scope.Scope;

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

class SocketTcpClient {
    public static void main(String[] args) throws IOException {
        //思路
        //1.连接服务器(ip,端口号)
        //连接本机的9999端口,连接成功时返回socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端:" + socket.getClass());
        //2.连接上后,生成Socket,通过getOutputStream得到和socket对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3.通过输出流,写入数据到数据通道
//        outputStream.write("Hello".getBytes());
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("Hello Server 字符流");
        bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束。作用相当于设置结束标记,但是要求对方也要用readLine
        bufferedWriter.flush();//使用字符流,需要手动刷新,否则数据不会写入到数据通道
//        //设置结束标记
//        socket.shutdownOutput();
        //4.关闭流对象和socket
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);

        //5.关闭流和socket
//        inputStream.close();
        bufferedReader.close();//关闭外层流就行
//        outputStream.close();
        bufferedWriter.close();//关闭外层流就行
        socket.close();
        System.out.println("客户端退出。。。");
    }
}

3.1 案例:

上传文件

package com.xxx;

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

/**
 * @author 王则钰 Wyzel
 * @date 2022/10/30 12:39
 * @Description
 */
public class TcpFileUploadServer {
    public static void main(String[] args) throws Exception {
        //1.服务端在本机监听7777端口
        ServerSocket serverSocket = new ServerSocket(7777);
        System.out.println("服务器在7777端口监听。。。");
        //2.等待客户端连接
        Socket socket = serverSocket.accept();

        //8.读取客户端发送的数据
        //通过Socket得到输入流
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToByteArray(bis);

        //9.将得到的bytes数组,写入到指定路径,得到一个文件
        String destFilePath = "src\\copy7.png";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        bos.write(bytes);
        bos.close();

        //11.向客户端回复收到
        //通过Socket获取输出流
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bufferedWriter.write("服务端回复:收到图片");
        bufferedWriter.flush();//把内容刷新到数据通道
        socket.shutdownOutput();//设置写入结束标记

        //10.关流
        bufferedWriter.close();
        bis.close();
        socket.close();
        serverSocket.close();

    }
}

package com.xxx;

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

public class TcpFileUploadClient {
    public static void main(String[] args) throws Exception {
        //3.客户端连接服务器,得到Socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(),7777);
        //4.创建读取磁盘文件的输入流
        String filePath = "D:\\a\\b\\7.png";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));

        //5.调用StreamUtils
        //bytes就filPath是对应的字节数组
        byte[] bytes = StreamUtils.streamToByteArray(bis);

        //6.通过socket获取输出流,把bytes数据发送给服务端
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        bos.write(bytes);//将文件对应的字节数组内容,写入到数据通道
        bis.close();
        socket.shutdownOutput();//写入数据的结束标记

        //12.接收从服务端回复的消息
        InputStream inputStream = socket.getInputStream();
        //使用StreamUtils的方法,将inputStream读取到的内容转为字符串
        String s = StreamUtils.streamToString(inputStream);
        System.out.println(s);

        //7.关流
        bos.close();
        socket.close();
    }
}

package com.xxx;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;

public class StreamUtils {
    /**
     * 功能:将输入流转换成 byte[],即可以把文件的内容读取到byte[]
     * @param is
     * @return
     * @throws Exception
     */
    public static byte[] streamToByteArray(InputStream is) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        //创建输出流对象
        byte[] b = new byte[1024];//字节数组
        int len;
        while ((len = is.read(b)) != -1) {//循环读取
            bos.write(b, 0, len);//吧读取到的数据,写入bos
        }
        byte[] array = bos.toByteArray();//把bos转换成字节数组
        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 builder = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {//当读取到 null 时,就表示结束
            builder.append(line + "\r\n");
        }
        return builder.toString();
    }
}

4. TCP网络通信编程

netstat指令

  1. 在dos控制台输入命令:netstat -an可以查看当前主机网络情况,包括端口监听情况和网络连接情况
  2. netstat - an | more 可以分页显示
  3. Listening表示某个端口在监听

*客户端Socket连接成功后会有一个随机的端口号

5. UDP网络通信编程

  1. 类DatagramSocket和DatagramPacket[数据包/数据报]实现了基于UDP协议网络程序
  2. 数据包套接字DatagramSocket发送和接收,系统不保证能否送到目的地,也不能确定何时送达
  3. 数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号
  4. UDP无需建立发送方和接收方的连接

UDP基本流程

  1. DatagramSocket和DatagramPacket
  2. 建立发送端,接收端
  3. 建立数据报DatagramPacket对象
  4. 调用DatagramSocket的发送、接收方法
  5. 关闭DatagramSocket
package com.xxx;

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

public class UdpReceiverA {
    public static void main(String[] args) throws IOException, IOException {
        //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.1.117"), 9998);
        socket.send(packet);//发送
        // 5. 关闭资源
        socket.close();
        System.out.println("A 端退出...");
    }
}
package com.xxx;

import java.io.IOException;
import java.net.*;

@SuppressWarnings({"all"})
public class UdpSenderB {
    public static void main(String[] args) throws IOException {
        //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.1.117"), 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 端退出");
    }
}

posted @ 2022-10-30 20:05  Rix里克斯  阅读(56)  评论(0)    收藏  举报