网络编程基础

端口

用于标识计算机上某个特定的网络程序

以整数的形式,端口范围 0 ~ 2^16-1

0 ~ 1024端口已经被占用 如 ssh: 22 ftp: 21 smtp: 25 http: 80

常见的网络端口号:

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

image-20210926160524712

网络协议

网络传输数据的组织形式

TCP/IP 传输控制协议 因特网互联协议

image-20210926162408594

TCP和UDP的区别

TCP协议:

  1. 使用TCP协议之前 必须先建立TCP连接 形成传输数据通道
  2. 传输前 采用“三次握手”方式 是可靠的
  3. TCP协议进行通信的两个应用进程: 客户端 服务端
  4. 在连接中可进行大数据量的传输
  5. 传输完毕 需释放已建立的连接 效率低

UDP协议:

  1. 将数据 源 目的封装成数据包 不需要建立连接
  2. 每个数据报的大小限制在64K内 不适合传输大量数据
  3. 无需建立连接 所以是不可靠的
  4. 发送数据结束时无需释放资源 因为不是面向连接的 速度快

InetAddress类

public class InetAddressDemo01 {
    public static void main(String[] args) throws UnknownHostException {
        // 1.获取本机InetAddress对象
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost); // LAPTOP-KORV82EM/10.108.38.215

        // 2.根据指定主机名 获取对象
        InetAddress host1 = InetAddress.getByName("LAPTOP-KORV82EM");
        System.out.println(host1); //LAPTOP-KORV82EM/10.108.38.215

        // 3.根据域名返回对象
        InetAddress host2 = InetAddress.getByName("www.baidu.com");
        System.out.println(host2); //www.baidu.com/14.215.177.38

        // 4.通过InetAddress对象获取对应地址
        String ip = host2.getHostAddress();
        System.out.println(ip); //14.215.177.38

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

Socket

Socket开发网络应用程序被广泛采用 以至于成为事实上的标准

通信的两端都要有Socket 是两台机器通信的端点

网络通信其实就是Socket之间的通信

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

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

TCP编程

客户端

    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对象关联的输出流对象 socket.getOutputStream()
        OutputStream outputStream = socket.getOutputStream();
        // 3. 通过输出流 写入数据到数据通道 设置结束标记
        outputStream.write("hello, server".getBytes());
        socket.shutdownOutput();
        // 4. 通过输入流 获取通道数据
        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();
    }

服务端

    public static void main(String[] args) throws IOException {
        // 服务端
        // 1. 在本机9999端口监听 等待连接 要求9999端口没有被占用
        // ServerSocket 可以通过accept返回多个Socket[多个客户端连接服务器的并发]
        ServerSocket serverSocket = new ServerSocket(9999);

        System.out.println("服务端等待连接...");
        // 2. 当没有客户端连接9999端口时 程序会阻塞 等待连接
        // 如果有客户端连接 则会返回一个Socket对象 程序继续
        Socket socket = serverSocket.accept();

        System.out.println("服务端完成连接...");

        // 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. 写入数据 设置结束标记
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello, client".getBytes());
        socket.shutdownOutput();
        // 6. 关闭流
        inputStream.close();
        outputStream.close();
        socket.close();
        serverSocket.close();
    }

注:

  1. 当服务端监听9999端口未开启时 客户端进行访问 会出现异常

    image-20210926172939794

  2. 当服务端开启 但客户端并未访问时 socket会阻塞

    image-20210926173044375

  3. 当客户端对服务端访问后 阻塞结束

    image-20210926173125238

    image-20210926173137999

  4. 服务端结束时 需关闭 serverSocket

  5. 发送数据时 要设置结束标记 否则客户端和服务端都会阻塞

    image-20210926192740466

  6. 字符流可以使用 write.newLine() 设置结束标记 但需要使用 redLine() 来读取

        public static void main(String[] args) throws IOException {
            // 服务端
            ServerSocket serverSocket = new ServerSocket(9999);
    
            Socket socket = serverSocket.accept();
    
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
    
            String input = br.readLine();
            System.out.println(input);
    
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
            bw.write("hello, client");
            bw.newLine();
            bw.flush();
    
            bw.close();
            br.close();
            socket.close();
            serverSocket.close();
        }
    
        public static void main(String[] args) throws IOException {
            // 客户端
            Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
    
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();
    
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
            bw.write("hello, server");
            bw.newLine(); // 插入一个换行符 表示写入内容结束 要求对方使用readLine()
            bw.flush(); // 字符流需要手动刷新
    
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            String input = br.readLine();
            System.out.println(input);
    
            br.close();
            bw.close();
            socket.close();
        }
    

拷贝文件

思路:

  1. 读取本地文件信息 将信息转为字节数组
  2. 通过客户端 将字节数组发送给服务端
  3. 服务端接收数据
  4. 服务端将数据写入指定路径
  5. 服务端返回信息
  6. 客户端收取信息

服务端

public class FileCopyServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        Socket socket = serverSocket.accept();

        // 1. 读取客户端发送的数据
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = FileCopyUtils.streamTOByteArray(bis);

        // 2. 将得到的bytes数组 写入到指定路径
        String path = "d:\\wx.jpg";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path));
        bos.write(bytes);

        // 3. 向客户端回复请求
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write("收到文件~");
        bw.flush(); // 刷新内容到数据通道
        socket.shutdownOutput(); // 设置结束标记

        bw.close();
        bos.close();
        bis.close();
        socket.close();
        serverSocket.close();
    }
}

客户端

public class FileCopyClient {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
        // 1. 读取磁盘文件
        String path = "C:\\Users\\81288\\Pictures\\wx.jpg";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));
        byte[] bytes = FileCopyUtils.streamTOByteArray(bis);

        // 2. 获取Socket输出流 发送byte[]给服务端
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        bos.write(bytes); // 将文件对应的字节数组写入数据通道
        socket.shutdownOutput(); // 结束标记

        // 3. 获取服务器传送的信息
        InputStream inputStream = socket.getInputStream();
        String s = FileCopyUtils.streamTOString(inputStream);
        System.out.println(s);

        inputStream.close();
        bis.close();
        bos.close();
        socket.close();
    }
}

工具类

public class FileCopyUtils {
    // 通过ByteArrayOutputStream输出流 将输入流的信息转换为字节数组
    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) {
            bos.write(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();
    }
}

natstat

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

image-20210926204503743

netstat -an | more 可以分页显示

image-20210926204602727

要求在dos控制台下执行

netstat -anb 可以查看使用应用程序信息 需管理员身份运行

image-20210926205630684

TCP细节

当客户端连接服务端后 实际上客户端也是通过一个端口和服务端进行通讯的 这个端口是TCP/IP来分配的 是不确定的 是随机的 当传输完毕后 端口会被释放

image-20210926210831332

UDP编程

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

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

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

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

UDP没有明确的服务端和客户端 会演变成数据的发送端和接收端 发送和接收数据都是通过 DatagramSocket 对象来完成 将数据封装到 DatagramPacket对象中 DatagramSocket可以指定在哪个端口接收数据

// 发送端
public class UDPSend01 {
    public static void main(String[] args) throws IOException {
        // 1. 创建一个DatagramSocket对象 准备在9998端口接收数据
        DatagramSocket socket = new DatagramSocket(9998);

        // 2. 将需要发送的数据封装到 DatagramPacket对象中
        byte[] bytes = "hello world".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("10.108.105.38"), 9999);

        // 3. 发送数据
        socket.send(packet);

        // 接收数据
        byte[] buf = new byte[1024 * 64];
        DatagramPacket receivePacket = new DatagramPacket(buf, buf.length);

        socket.receive(receivePacket);

        int len = receivePacket.getLength();
        byte[] data = receivePacket.getData();
        System.out.println(new String(buf, 0, len));

        // 4. 关闭资源
        socket.close();
    }
}
// 接收端
public class UDPReceive01 {
    public static void main(String[] args) throws IOException {
        // 1. 创建一个DatagramSocket对象 准备在9999端口接收数据
        DatagramSocket socket = new DatagramSocket(9999);

        // 2. 构建一个DatagramPacket对象 准备接收数据
        // UDP 数据包最大是64K
        byte[] bytes = new byte[1024 * 64];
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);

        System.out.println("接收端等待数据..");
        // 3. 调用接收方法 将通过网络传输的packet对象接收
        // 有数据包发送到9999端口时会接收数据 否则会阻塞等待
        socket.receive(packet);

        System.out.println("接收端收到数据...");

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

        // 发送数据
        byte[] buf = "hello hello".getBytes();
        DatagramPacket sendPacket = new DatagramPacket(buf, buf.length, InetAddress.getByName("10.108.105.38"), 9998);
        socket.send(sendPacket);


        // 5. 关闭资源
        socket.close();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值