JavaSE丨网络编程核心指南:从协议原理到对象传输实战(TCP/UDP)

一、基本概念

1.1 计算机网络

        也称计算机通信网,其是利用通信线路将地理上分散的、具有独立功能的计算机系统和通信设备按不同的形式连接起来,以功能完善的网络软件及协议实现资源共享和信息传递的系统。最简单的计算机网络就只有两台计算机和连接它们的一条链路,即两个节点和一条链路。

        通过编程的方式,使得计算机网络中不同计算机上的应用程序间能够进行数据的传输,这就是网络编程要做的事情。

1.2 软件结构

常见的软件结构有C/S和B/S:

        Client/Server(C/S结构),表示 客户端/服务器 的软件结构,例如QQ、微信、百度网盘客户端等,只要是需要我们下载安装,并且和服务器通信的这一类软件,都属于C/S的软件结构。

        Browser/Server(B/S结构),表示 浏览器/服务器 的软件结构,例如淘宝网、新浪新闻、凤凰网等,只要是需要使用浏览器,并且和服务器通信的这 一类软件,都属于B/S的软件结构。

C/S和B/S对比:
  • C/S在图形的表现能力上以及运行的速度上肯定是强于B/S的
  • C/S/需要运行专门的客户端,并且它不能跨平台,用c++在windows下写的程序肯定是不能在linux下运行
  • B/S不需要专门的客户端,只要系统中安装了浏览器即可访问,方便用户的使用
  • B/S是基于网页语言的、与操作系统无关,所以跨平台也是它的优势

无论是C/S结构的软件,还是B/S结构的软件,都必须依赖网络编程。

1.3 通信三要素

如果两台计算机上的应用程序能够实现通信,那么必须解决3个问题:找到对方计算机,找到对方应用程序,采用相同的通信协议。

  • IP地址

可以认为IP地址是个标识号,通过这个标识号能够找到网络世界中唯一的那个计算机。

  • 端口

端口号可以用来标识计算机中唯一的那个应用程序。网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,在网络通信时,就采用端口号进行区分这些应用程序。

  • 协议

当我们和其他人沟通交流的时候都要使用互相能听懂的语言。计算机也一样,计算机与计算机通过网络进行数据和信息交换的时候,也要使用同样的 “语言”,这个语言被称为网络通讯协议。 网络通信协议对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议和TCP协议。

二、通信协议

        计算机网络中,不同设备进行连接和通信的规则被称为网络通信协议。通信协议,对两台计算机之间所传输数据的传输格式、传输步骤等做了统一规定要求, 通信双方必须同时遵守才能完成数据交换。

        OSI(Open System Interconnect),即开放式网络互连标准。 一般叫OSI参考模型,是ISO(国际标准化组织)在1985年研究的网络互连模型,它共包含七层, 具体可参考下图。

        TCP/IP网络模型,是事实上的国际标准,它被简化为了四个层,从下到上分别依次是应用层、传输层、网络层、网络接口层。

应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。

传输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。

网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。

链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。

三、TCP和UDP

        虽然完整的通信过程比较复杂,但是JavaAPI中把这些通信实现的细节进行了封装,使得我们可以直接使用相应的类和接口,来进行网络程序开发,而不用考虑通信的细节。

         java.net 包中对常见的两种通信协议进行了封装和支持:UDP和TCP

UDP,用户数据报协议(User Datagram Protocol)

        UDP是无连接通信协议,在数据传输时,数据的发送端和接收端不建立连接,也不能保证对方能接收成功。 例如,当一台计算机向另外一台计算机发送数据时(UDP),发送端不会确认接收端是否存在,就会直接发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。

        由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。 但是在传输重要数据时,不建议使用UDP协议,因为它不能保证数据传输的完整性。

TCP,传输控制协议 (Transmission Control Protocol)

        TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立连接,然后再传输数据,它提供了两台计算机之间可靠的、无差错的数据传输。 在TCP连接中,将计算机明确划分为客户端与服务器端,并且由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”的过程,四次挥手断开连接。

TCP的三次握手:

TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠

  • 第一次握手,客户端向服务器端发出连接请求,等待服务器确认
  • 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求
  • 第三次握手,客户端再次向服务器端发送确认信息,确认连接

TCP的四次挥手:

用于断开连接

四、TCP网络编程

4.1 概述

        在TCP通信协议下,计算机网络中不同设备上的应用程序之间可以通信,通信时需严格区分客户端(Client)与服务器端(Server)

         在Java中,对于这样基于TCP协议下连接通信的客户端和服务端,分别进行了抽象:

  • java.net.ServerSocket 类表示服务端
  • java.net.Socket 类表示客户端

使用 Socket 和 ServerSocket 进行的编程,也称为套接字编程

4.2 通信流程

TCP客户端和服务器进行通信,其通信流程是固定的,具体如下:

服务器端:
  • 创建ServerSocket(需绑定端口,方便客户端连接)
  • 调用ServerSocket对象的accept()方法接收一个客户端请求,得到一个Socket
  • 调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流
  • 输入流可以读取客户端发送过来的数据
  • 输出流可以发送数据到客户端
  • 操作完成,关闭资源

注意:accept方法是阻塞的,作用是等待客户端连接,如果有客户端连接则立马返回,如果没有客户端连接则一致阻塞等待。

客户端:
  • 创建Socket连接服务端(需指定服务器ip地址、端口),找对应的服务器进行连接
  • 调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流
  • 输入流可以读取服务端输出流写出的数据
  • 输出流可以写出数据到服务端的输入流
  • 操作完成,关闭资源

注意,在整个过程中,服务端不能主动连接客户端,必须由客户端先行发起连接才行

4.3 基础案例

搭建TCP客户端,发送信息到服务器

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TcpClient {
    public static void main(String[] args) throws Exception {
        //1.创建socket对象(指定服务器IP port) 连接到服务器
        Socket socket = new Socket("127.0.0.1",8989);
        System.out.println("成功连接到服务器:"+socket);

        //2.获取数据传输的io流
        InputStream is = socket.getInputStream();
        OutputStream os = socket.getOutputStream();

        //3.数据传输【需跟服务器配合】
        //先发送数据给服务器
        os.write("hello server,我是tcp客户端".getBytes());
        System.out.println("数据发送完成!");

        //再接收从服务器返回的数据
        byte[] buff = new byte[1024];
        int len = is.read(buff);
        System.out.println("read:"+new String(buff,0,len));

        //4.关闭资源
        os.close();
        is.close();
        socket.close();
    }
}

搭建TCP服务器端,接收客户端发送的信息,并返回数据给客户端

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

public class TcpServer {
    public static void main(String[] args) throws Exception {
        //1.创建ServerSocket(需绑定端口,方便客户端连接)
        ServerSocket server = new ServerSocket(8989);
        System.out.println("服务器启动成功");

        //2.调用ServerSocket地accept方法接收客户端请求,得到一个socket
        //如果客户端成功连接到服务器,直接返回 套接字对象(通信)
        //如果没有客户端连接服务器,则堵塞
        Socket socket = server.accept();
        System.out.println("客户端成功连接:"+socket);

        //3.获取网络通信的io流对象
        InputStream is = socket.getInputStream();
        OutputStream os = socket.getOutputStream();

        //4.数据传输
        //先读取客户端发送来的数据 并输出
        byte[] buff = new byte[1024];
        int len = is.read(buff);
        System.out.println("成功读取:");
        System.out.println(new String(buff,0,len));

        //再返回一条数据给客户端
        System.out.println("即将发送数据给客户端...");
        os.write("hello client".getBytes());
        System.out.println("数据发送成功!");

        //5.关闭资源
        os.close();
        is.close();
        socket.close();
        server.close(); 
    }
}

4.4 传输对象

准备一个stud.txt文件,放到src/dir目录下,内容如下:

搭建TCP客户端,逐行读取stud.txt中数据,然后转化为Student对象,最后将所有对象发送到服务器端。

Student基础类:

import java.io.Serializable;

// 注意,必须实现序列化接口
public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String id;
    private String name;
    private int age;

    // 无参构造方法
    public Student() {
    }

    // 有参构造方法
    public Student(String id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    // getter 和 setter 方法
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    // 重写 toString 方法,方便打印对象信息
    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

客户端代码:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class Test_ObjectClient {
    public static void main(String[] args) throws Exception {
        //1.搭建客户端
        Socket socket = new Socket("127.0.0.1",8989);
        System.out.println("客户端成功连接:"+socket);

        //2.获取流对象 并包装增强
        OutputStream os = socket.getOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(os);

        //3.解析文件 得到对象 并发送
        BufferedReader br = new BufferedReader(new FileReader("src/dir/stud.txt"));
        String line = null;
        List<Student> list = new ArrayList<>();
        while ((line = br.readLine()) != null) {
            //拆分数据
            String[] arr = line.split("[.]");
            String id = arr[0];
            String name = arr[1];
            int age = Integer.parseInt(arr[2]);

            //封装学生对象 并添加到list集合
            Student s = new Student(id,name,age);
            list.add(s);
        }

        //发送集合(含所有学生)
        oos.writeObject(list);
        System.out.println("学生发送成功,数量:"+list.size());

        //4.关闭资源
        br.close();
        oos.close();
        socket.close();

    }
    
}

搭建TCP服务器,接收从客户端发送过来的所有学生,然后遍历输出。

import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;

public class Test_ObjectServer {
    public static void main(String[] args) throws Exception {
        //1.搭建服务器,指定端口
        ServerSocket server = new ServerSocket(8989);
        System.out.println("服务器启动成功,端口:8989...");

        //2.接收客户端的连接
        Socket socket = server.accept();
        System.out.println("客户端成功连接:"+socket);

        //3.获取输入流并包装增强
        InputStream is = socket.getInputStream();
        ObjectInputStream ois = new ObjectInputStream(is);

        //4.接收 遍历数据
        List<Student> list = (List<Student>)ois.readObject();
        System.out.println("成功接收学生数量:"+list.size());
        for (Student s : list) {
            System.out.println(s);
        }

        //5.关闭资源
        ois.close();
        socket.close();
        server.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值