云计算百科
云计算领域专业知识百科平台

Java网络编程学习笔记,从网络编程三要素到TCP/UDP协议

什么是网络编程

什么是网络编程,相比于编写程序在本机上运行,网络编程是指编写两台不同的计算机的程序,基于网络协议,通过网络进行数据通信。

常见的网络程序软件架构有:BS(Broser浏览器/Server服务器)架构、CS(Client客户端/Server服务器)架构

  • BS架构:优势是用户不用下载客户端,使用浏览器即可快速获取服务,运营者可随时更新程序。劣势是所有程序和资源需要通过网络传输,产品精美和细腻程度不及CS架构的产品。
  • CS架构:优略势与BS架构相反。优势是用户提前下载好程序的所有资源,产品的精美和细腻程序可以做到非常高,而且可以更好的利用本地计算机算力,比如3A游戏都是CS架构。劣势是需要用户下载客户端程序,程序更新时比较麻烦,需要客户端更新软件。
  • 网络编程三要素

    要实现网络编程,必须实现网络通信需要确定通信对象(IP)、对象中的具体程序(端口)、通信规则(协议)。

    第一步,首先要在网络中,找到对方的计算机设备。计算机网络中,IP是网络设备的唯一地址。 第二步,在对方的计算机设备中,找到要通信的软件,因为计算机中可能运行很多程序。比如微信服务端要发送数据到用户手机,需要找到用户手机中的微信客户端,而用户手机中可能有微信、QQ、淘宝等很多应用程序。计算机网络中,端口号是区分程序进程的编号。 第三步,需要确定通信方式,即网络协议。计算机网络协议有TCP、UDP、HTTP等协议。

    所以,网络编程的三要素就是:IP、端口号、网络协议

    IP

    IP是网络通信中,设备的地址,具有唯一性。

    IP有IPV4和IPV6两种。

    IPV4

    IPV4是指IP互联网通信协议第四版,IPV4用4个字节共32位,用4组(每组1个字节)来表示,比如:11000000 10101000 00000001 00000001。IPV4采用点分十进制,即使用符号点.来分组,采用十进制表示。因此,上述IPV4地址表示为:192.168.1.1

    IPV4共32位,因此可以区分的地址有2的32次方共计4,294,967,296,而全球有数十亿人,每个人又有数台可上网的设备。因此,IPV4的地址是不够用的,实际上,IPV4早在2019年就已经用完了。

    IPV4已经用完了,而IPV6并没有大规模普及,那IPV4是如何解决当前设备IP地址不够的问题?答案是使用局域网。IPV4规定192.168.0.0~192.168.255.255这些为局域网IP(还有其他私有IP段,不展开),一个区域内(比如一个网吧)的所有设备,使用一个公网IP,而所有区域内的网络设备,使用局域网IP。局域网内的所有设备上网,都使用公网IP对外传输和接收数据,然后通过NAT(网络地址转换)技术,将局域网各设备的数据包,再分发给各自设备。

    公网IP+局域网IP的技术,造成了我们目前使用设备的IPV4地址都是192.168.X.X的原因。

    局域网IP地址,一般是路由器通过DHCP技术随机分配给网络设备的,一段时间后局域网IP地址会改版。除此之外,我们的设备在不同的上网环境,路由器分配的局域网地址一般都是不同的,比如在教师里连WIFI,笔记本电脑被分配的局域网地址可能是192.168.10.245,而回到寝室,连有线网,笔记本电脑被分配的局域网地址变成了192.168.1.3。因此,本机程序之间进行通信,不能使用局域网IP地址,而是使用一个被称为本机IP或者回环地址的特殊IP:127.0.0.1。网络设备在识别到回环地址作为目标地址,不会将数据传输到路由器或者网关,而是直接传给本机。

    IPV6

    为解决IP枯竭问题,IPV6横空出世。IPV6采用16个字节128位,共8组(每组两个字节)来表示,比如:11111101 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001。IPV6采用冒分十六进制,即使用符号冒号:来分组,采用十六进制来表示。因此,上述IPV6地址表示为:fd00:0000:0000:0000:0000:0000:0000:0001。由于IPV6地址很长,因此固定可以省略每一最前面的一连串0,因此上述IPV6地址可简写为:fd00:0:0:0:0:0:0:1。并且,规定可以进一步将中间的0省略,即压缩表示为:fd00::1

    IPV6共128位,因此可以区分的地址为2的128次方个,可以为地球上每一粒沙子都分配一个唯一的IP地址。因此,IPV6一定是足够人们使用。目前,IPV6正在逐步推广,全面取代IPV4仍需时间,目前很多环境是双栈(IPV4+IPV6)运行。

    端口

    端口是计算机中应用程序向外发送数据/接收数据的“口”,每个应用程序都有一个端口号。

    端口号是用两个字节来表示的,也就是一共有0~65535,一共65536个端口号。

    应用程序启动时,操作系统会为应用程序分配一个端口号,应用程序就用这个端口号向网络收发数据。

    协议

    网络协议是指网络通信、数据传输的标准或规范。计算机网络中有OSI七层模型和TCP/IP模型,其中TCP/IP模型将网络划分为应用层、传输层、网络层和物理链路层,TCP/IP模型是行业默认的标准。

    TCP/IP模型中不同层有不同的网络协议,比如HTTP、FTP等就是应用层的协议,而TCP、UDP是传输层的协议,IP是网络层的协议。

    UDP 程序通信

    UDP协议特点是无连接,发送后不管,所以速度快。

    创建UDP发送数据的程序步骤:

  • 创建一个发送数据的通信端点,需包括网络编程三要素:IP、端口和协议。发送时的IP是本机IP,无法自定义(更准确的是:在创建DatagramSocket时,可以用带IP对象的构造函数绑定到指定的本地IP地址和端口,但是一般不用IP对象作为构造函数参数,默认为本机IP)。端口号可指定发送端口号,但是由于端口号不能与别的程序占用的端口号,因此一般使用系统给的端口号。协议则体现到类上,UDP使用DatagramSocket。
  • 封装数据包。就像封装发送出去的快递包需要包含收货地址和收货人。UDP数据包需要包含发送的数据本身(字节数组)、目标地址(IP),目标端口号。其中,IP需要使用IP类InetAddress创建对象。UDP数据包为DatagramPacket类,需要用字节数组数据本身、IP对象和端口号封装。
  • 发送数据。DatagramSocket对象调用方法发送数据包。
  • 释放资源。DatagramSocket对象存在时占用了系统资源,因为系统开辟了端口号让程序独占使用,因此需要调用close()方法释放资源。
  • // 发送端
    public class SendMessage {
    public static void main(String[] args) throws IOException {
    // 1. 创建一个发送UDP的通信对象
    DatagramSocket socket = new DatagramSocket();

    // 2. 封装数据包
    // 目的地址和端口
    String ip = "127.0.0.1"; // 发送到本机,用回环地址
    InetAddress destinationIP = InetAddress.getByName(ip); // 目的地址
    int destinationPort = 10086;
    // 待发送的数据
    String data = "hello, world"; // 待发送的数据:hello, world
    byte[] dataBytes = data.getBytes(); // 转换为字节数组
    DatagramPacket packet = new DatagramPacket(dataBytes, dataBytes.length, destinationIP, destinationPort); // 数据包对象

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

    // 4. 释放资源
    socket.close();
    }
    }

    UDP接收数据的程序步骤:

  • 同发送数据相似,需要创建一个接收数据的通信端点,也是包括网络编程三要素:IP、端口和协议。接收时的IP是本机号。端口号需要注意,要使用与发送数据中的发送数据包的目标端口号,不对应则接收不到数据。协议也是体现到类上,UDP为DatagramSocket。
  • 接收数据。发送数据的是DatagramPacket对象,接收数据也需要用一个DatagramPacket对象进行接收,可以想象为收快递时,需要用一个快递盒子去接收。与发送时需要数据包DatagramPacket对象需要包含数据本身(字节数组)、目标地址(IP),目标端口号等内容时不同,接收数据的数据包,只需要定义数据包的大小即可(字节数组和接收数据的长度)。
  • 解析数据。接收的是DatagramPacket对象,调用相应的方法,可以获取数据包的数据本身、来源地址(IP)、来源端口号。
  • 关闭资源。与发送数据相同,需要调用close()方法释放资源。
  • // 接收端
    public class ReceiveMessage {
    public static void main(String[] args) throws IOException {
    // 1. 创建一个接收UDP的通信对象
    DatagramSocket socket = new DatagramSocket(10086); // 指定接收端口为10086

    // 2. 接收数据
    byte[] buffer = new byte[1024]; // 创建一个1024字节的字节数组作为数据缓冲区,用于临时存储从网络接收到的数据
    DatagramPacket packet = new DatagramPacket(buffer, buffer.length); // 创建一个数据包对象,最大接收数据长度为数组长度
    socket.receive(packet);

    // 3. 解析数据
    byte[] data = packet.getData(); // 获取数据包中的数据
    String receivedData = new String(data, 0, packet.getLength()); // 将数据转换为字符串
    InetAddress sourceAddress = packet.getAddress(); // 获取发送方的IP地址
    String sourceAddressStr = sourceAddress.getHostAddress(); // 解析为IP地址字符串
    int sourcePort = packet.getPort(); // 获取发送方的端口号

    // 打印出接收到的数据和发送方的IP地址及端口号
    System.out.println("收到来自IP为" + sourceAddressStr + ",端口号为" + sourcePort + "的数据:" + receivedData);

    // 4. 释放资源
    socket.close();
    }
    }

    收到来自IP为127.0.0.1,端口号为65290的数据:hello, world

    进程已结束,退出代码为 0

    TCP 程序通信

    TCP协议是建立连接的协议,也就是在发送数据之前,需要先建立连接,连接建立失败则会异常,不可发送数据。TCP协议与UDP协议不同,TCP发送数据无需封装成数据包,还是通过数据流发送数据,因此发送和接收数据如同IO流。

    TCP协议需要通过三次握手建立连接:

  • 第一次握手:客户端往服务端发送信息,请求建立连接
  • 第二次握手:服务端发送信息,表明统一建立连接。此时,服务端已就绪,等待客户端确认就绪
  • 第三次握手:客户端往服务端发送消息,表明自己已就绪,准备发送数据。服务端收到消息,确认客户端就绪,等待收取消息。
  • 创建TCP发送消息的程序步骤为:

  • 创建一个客户端的通信端点,与UDP的DatagramSocket不需要接收端IP和端口号作为参数不同(因为接收端IP和端口号被封装到了数据包DatagramPacket内),客户端Socket对象在创建时就需要接收端IP和端口号,因为在发送数据之前,就要与服务端建立连接(创建对象时就建立连接)。客户端Socket对象创建与DatagramSocket相似点在于,可以不指定本机的IP和端口号,使用系统给的端口号和本机IP。
  • 从Socket中获取发送数据的输出流(原始字节流出留),通过输出流发送数据。Socket输出流是IO流,因此也可以使用字节流、字符流、转换流、缓冲流等原始流和处理流。
  • 释放资源。创建Socket占用系统资源,因为系统分配了端口号,需要调用close()方法释放资源。此外,IO流也需要关闭。需要注意,释放资源有一定顺序,客户端发送完数据立马进行关闭,存在两种可能:1. 数据还在缓冲区未被完全发送;2. 服务端未完全接收数据。因此,使用flush()让缓冲区的数据立即发送。
  • // 客户端
    public class Client {
    public static void main(String[] args) throws IOException {
    // 1. 创建一个Socket对象
    Socket socket = new Socket("127.0.0.1", 52522); // 指定接收端的IP和端口

    // 2. 通过Socket对象获取输出流
    OutputStream os = socket.getOutputStream();
    BufferedOutputStream bos = new BufferedOutputStream(os); // 使用缓冲流(仅做演示,非必须)
    OutputStreamWriter osw = new OutputStreamWriter(bos); // 使用OutputStreamWriter将字节流转换为字符流
    osw.write("Hello, Server!"); // 写入数据
    osw.flush(); // 刷新缓冲区,确保数据发送

    // 3. 释放资源(按正确顺序)
    osw.close(); // 释放字符流资源
    bos.close(); // 释放缓冲流资源
    socket.close(); // 释放Socket资源,自动关闭底层的字节输入流和字节输出流
    }
    }

    创建TCP接收消息的程序步骤为:

  • 创建一个接收数据的通信端点。与UDP不同,TCP的服务端采用与客户端不同的类ServerSocket。端口号与UDP协议相似,要使用客户端Socket目标端口号相同的端口号,不对应则接收不到数据。
  • 等待与客户端建立连接。调用accept()方法,一直监听端口,等待客户端进行三次握手建立连接。建立连接之前,线程是阻塞的状态。链接建立后,返回一个客户端Socket对象。
  • TCP建立连接后,通过IO流传输数据。发送端是Output,接收端是Input。因此,从Socket对象中获得IO基本的字节输入流。
  • 解析数据。字节输入流可以通过字符流、转换流、缓冲流等处理流,处理解析数据。
  • 释放资源。释放IO流和Socket资源。
  • // 服务器端
    public class Server {
    public static void main(String[] args) throws IOException {
    // 1. 创建一个ServerSocket对象
    ServerSocket serverSocket = new ServerSocket(52522);

    // 2. 等待与客户端建立连接
    Socket socket = serverSocket.accept(); // 链接建立后,返回一个Socket对象

    // 3. 获得字节输入流对象
    InputStream is = socket.getInputStream();

    // 4. 将字节输入流转换为字符输入流,读取数据并打印成字符串
    InputStreamReader isr = new InputStreamReader(is);
    int len = 0;
    char[] chs = new char[1024];
    if ((len = isr.read(chs)) != 1) {
    System.out.println(new String(chs, 0, len));
    }

    // 一般情况下,服务器端会循环接收多个客户端的请求
    // 无需手动关闭Socket
    // 5. 释放资源
    isr.close(); // 关闭字符输入流
    // is.close(); // 无需手动关闭字节输入流
    socket.close(); // 关闭Socket,会自动关闭底层的字节输入流和字节输出流
    }
    }

    TCP协议建立连接需要三次握手,关闭连接需要四次挥手。

  • 第一次挥手:客户端发起信息,请求关闭连接。
  • 第二次挥手:服务端收到信息,但是收到客户端发送关闭连接请求时,服务端还没有完全收全所有之前发送的数据。但是,服务端需要让客户端知道它收到了关闭连接的请求。因此,服务端会给客户端发送确认收到关闭请求的信息。
  • 第三次挥手:当服务端接收完所有数据后,会再次给客户端发送一条信息,表明自己已经安全收到所有传输的数据。发送确认关闭连接的信息给客户端,表明服务器已就绪,准备关闭。
  • 第四次挥手:当客户端收到服务端发送的关闭确认请求后,发送一个自己已确认关闭连接的请求,发送后,客户端关闭连接。而服务器收到信息后,也关闭连接。
  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » Java网络编程学习笔记,从网络编程三要素到TCP/UDP协议
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!