Java的网络编程(TCP/UDP)

1.网络编程相关知识总结

1.1网络知识:

首先,想要理解好网络编程,我们需要先理解信息在网络中是如何传递的。这便不得不提一提计算机网络结构中的五层模型。

在计算机网络中,信息是通过五层模型中的不同层次进行传递的。五层模型分别为物理层、数据链路层、网络层、传输层和应用层。下面简单介绍一下信息在网络中的传递过程:

》》》 三种说明结构知识补充说明:

OSI体系结构:         概念清楚,理论也比较完整,但是它既复杂又不实用。
TCP/IP体系结构:    CP/IP是一个四层体系结构,得到了广泛的运用。
五层体系结构:         为了方便学习,折中OSI体系结构和TCP/IP体系结构,综合二者的优   点,这样既简洁,又能将概念讲清楚。

如下图:

当用户在向另一个用户发送信息时,数据的格式会随着经过网络五层结构的不同层次而发生变化。

  1. 物理层:它负责将比特流传输到物理介质上,将数字信号转换成物理信号,例如电流、电压等。在这一层,数据被表示为比特流,没有任何协议头或其他控制信息。物理层的主要任务是传输数据,而不关心数据的内容或意义。

  2. 数据链路层:数据链路层是OSI参考模型的第二层,它负责将比特流组装成帧,添加MAC地址等控制信息,进行错误检测和纠正。在这一层,数据被表示为帧,包括了帧头、帧尾和数据部分。数据链路层的主要任务是提供可靠的数据传输和错误检测,以确保数据的完整性和可靠性。

  3. 网络层:网络层负责将数据传输到目的主机,添加IP地址等控制信息,进行路由选择和分组。在这一层,数据被表示为数据报,包括了IP头部和数据部分。

    IP数据报包含了网络层的协议头和数据部分。网络层的协议头包含了源IP地址和目的IP地址等信息,这些信息用于在网络上路由数据报到达目的地。数据部分则是由上层协议传递下来的数据,如TCP或UDP数据段。

    在实际传输中,由于网络传输的限制,IP数据报通常被分割成更小的数据包进行传输。这些小的数据包被称为IP分片,每个IP分片包含了IP数据报的一部分数据和一个分片头,用于指示该分片在原始数据报中的位置。

    在接收端,IP分片被重新组装成原始的IP数据报,以便上层协议可以处理完整的数据。IP数据报的传输是不可靠的,因为它不提供确认和重传机制,如果数据在传输过程中发生丢失或损坏,它不会进行重新传输。因此,上层协议需要自己实现可靠的数据传输机制。

  4. 传输层:(也称运输层)

    传输层会将应用层的数据进行分段(segmentation)和重组(reassembly),以便进行可靠的传输。传输层会将应用层的数据分割成多个数据段(segment),然后添加一些传输层的协议信息,如TCP协议会添加序列号、确认号、窗口大小等信息,UDP协议不添加任何信息,然后将这些数据段发送到网络层进行传输。

    在接收数据时,传输层会将接收到的数据段进行重组,还原成原始的应用层数据。传输层会根据序列号和确认号等信息来确认数据的可靠传输,如果出现丢包或错误,传输层会进行重传或者错误纠正。

    因此,传输层主要的作用是在应用层和网络层之间提供可靠的数据传输服务,并对数据进行分段和重组。传输层会添加一些传输层的协议信息来保证数据的可靠传输。

  5. 应用层:应用层负责处理应用程序之间的通信,添加应用层协议头和数据部分,例如HTTP头部、SMTP头部等。在这一层,数据被表示为报文。当用户向另一个用户发送信息时,数据从应用层开始,被封装成报文。报文被传递到传输层,传输层会添加TCP或UDP头部,将报文封装成数据段。在TCP头部中,会添加源端口号和目的端口号等控制信息,以便将数据传输到正确的应用程序。在UDP头部中,只会添加源端口号和目的端口号等基本信息,不进行可靠传输和流量控制。 

通俗易懂的说如下图所示: 

通俗易懂来讲的话,在主机A到主机B之间发送信息如下

应用层(此图将会话层,表示层,应用层看成一层的应用层):应用程序在主机设备A上生成数据,比如一个HTTP请求。该层负责处理应用程序和网络之间的交互。在这个层次上,应用程序可以通过各种协议和接口来实现数据的生成、传输和处理。

比如,当我们在浏览器中输入一个网址,浏览器会生成一个HTTP请求,并将请求发送到服务器。在这个过程中,浏览器就是应用程序,HTTP就是应用层协议。浏览器将HTTP请求发送到操作系统的传输层,然后经过网络传输到服务器,服务器接收到HTTP请求后,解析请求并返回响应数据。浏览器再次接收到响应数据,然后在应用层解析数据并呈现给用户。

传输层:数据被封装成一个TCP段,并在段头部添加源端口和目的端口等信息。

网络层:TCP段被封装成一个IP数据包,并在数据包头部添加源IP地址和目的IP地址等信息。

数据链路层:IP数据包被封装成一个数据帧,并在帧头部添加源MAC地址和目的MAC地址等信息。

物理层:数据帧被转换成物理信号,并通过网络适配器(NIC)发送到网络上。

路由器:如果主机设备A和主机设备B不在同一个网络中,数据包会经过一系列的路由器转发,直到到达主机设备B。

对于A到B之间的传输说明,在从主机A到主机B的数据传输过程中,可能会经过多个路由器,这些路由器被称为传输中间节点。这些传输中间节点会对数据进行层层转化和解析,以实现数据的传输和路由选择。

中间的ccp部分是指传输中间节点(如路由器)上的协议栈。当数据从主机A到达传输中间节点时,它会被解封装,然后再根据目标地址进行路由选择和转发。在传输中间节点上,数据也需要经过物理层、数据链路层和网络层等层次的转化和解析,以实现数据的传输和路由选择。当数据到达主机B时,它会再次经过层层解析,直到到达应用层,最终被交付给目标应用程序。

总之,从主机A到主机B的数据传输过程中,涉及到多个网络层次和传输中间节点,每个层次都有其对应的协议和数据格式,数据需要经过层层转化和解析,以实现数据的传输和路由选择。

2.知其意,懂其理(相关名词解释说明)

数据帧(Data Frame)

是数据链路层中的一个概念,用于在网络中传输数据。数据帧是数据链路层协议封装的最小数据单元,它包含了数据和控制信息,以及帧头和帧尾等标识信息。

数据帧通常由以下几个部分组成:

  1. 帧头(Frame Header):帧头包含了一些控制信息,如源地址、目的地址、帧类型、帧序号等,用于标识数据帧的发送和接收信息。

  2. 数据(Data):数据部分包含了要传输的数据,可以是文本、图像、音频等任何类型的数据。

  3. 帧尾(Frame Trailer):帧尾包含了一些控制信息,如帧校验序列(FCS),用于检测数据帧是否出现了错误。

数据报(Datagram

数据报是在计算机网络中用于传输数据的基本单位。它包含了数据和一些控制信息,如源地址、目的地址、数据长度等。数据报的大小是固定的,通常为MTU(最大传输单元)的大小。数据报可以通过各种协议进行传输,如IP协议、TCP协议、UDP协议等。

在IP协议中,数据报是通过IP数据包进行传输的。数据报的结构包括IP头部和数据部分。IP头部包含了源地址、目的地址、协议类型、数据报长度等信息。数据部分则是要传输的数据。IP数据包在传输过程中可能会被分割成多个数据报进行传输,这就需要进行分片和重组操作。

在TCP协议和UDP协议中,数据报被称为数据段。数据段也包含了源地址、目的地址等信息,但是与IP协议不同的是,它还包含了端口号等信息。TCP协议和UDP协议的数据段也有不同的结构和长度限制。

网络层的数据格式通常是IP数据报,也称为IP包。IP数据报是一种在互联网上进行数据传输的标准格式,它包含了源地址、目标地址、协议类型、数据等信息。在传输过程中,IP数据报会通过不同的路由器进行转发,直到到达目标主机。由于IP数据报的标准格式是通用的,因此可以在不同的网络协议中使用,例如IPv4和IPv6。

套接字:

套接字(Socket)是一种用于网络通信的编程接口,它提供了一种标准的网络编程方式,使得应用程序可以通过网络进行数据的传输和接收。套接字是应用层和传输层之间的接口,它可以将应用程序生成的数据封装成传输层可以理解的格式,并将数据发送到网络上。

可以简单理解为:套接字是个发送和接收IP数据报的端口

套接字可以看作是一个网络通信的端点,它由IP地址和端口号组成。在套接字编程中,应用程序可以通过套接字来创建TCP连接、发送和接收数据、关闭连接等操作。套接字提供了一系列的API,使得应用程序可以方便地进行网络编程,实现各种网络应用。

在套接字编程中,常用的套接字类包括Socket、ServerSocket和DatagramSocket等。Socket类用于创建TCP连接,ServerSocket类用于创建TCP服务器,DatagramSocket类用于创建UDP连接。这些类都提供了一系列的方法,可以方便地进行网络通信。

3.Java中的基于UDP和TCP的网络编程的实现

Datagram:           翻译过来意思是   数据报

Socket:                翻译过来意思是   套接字

DatagramSocket:翻译过来意思是   数据报套接字

DatagramPacket:翻译过来意思是   数据报包

套接字是在应用层和传输层之间的接口。在操作系统中,套接字通常被实现为一组API,用于应用程序和传输层协议之间的通信。因此,套接字可以看作是应用程序和传输层之间的接口。

3.1Java中常用的基础方法介绍

 3.1.1 InetAdderss

InetAdderss类,该类用于封装一个IP地址,并提供了一系列与IP地址相关的方法,下表中列出了InetAddress类的一些常用方法。

方法名说明
public static InetAddress getLocalHost()返回本地主机。
public static InetAddress getByName(String host)根据ip和主机名获取InetAddress
public String getHostAddress()获取ip地址
public String getHostName()获取主机名

3.1.2 UDP发送数据

 UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象,因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念。

DatagramSocket类

Java提供了DatagramSocket类作为基于UDP协议的Socket

DatagramSocket构造方法

方法名说明
DatagramSocket()创建数据报套接字并将其绑定到本机地址上的任何可用端口
DatagramPacket(byte[] buf,int len,InetAddress add,int port)创建数据包,发送长度为len的数据包到指定主机的指定端口

DatagramSocket常用方法

方法名说明
void send(DatagramPacket p)发送数据报包
void close()关闭数据报套接字
void receive(DatagramPacket p)从此套接字接受数据报包

DatagramSocket使用步骤

  • 创建发送端的Socket对象(DatagramSocket)

  • 创建数据,并把数据打包

  • 调用DatagramSocket对象的方法发送数据

  • 关闭发送端

3.1.3 UDP接收数据

  • 创建接收端的Socket对象(DatagramSocket)

  • 创建一个数据包,用于接收数据

  • 调用DatagramSocket对象的方法接收数据

  • 解析数据包,并把数据在控制台显示

  • 关闭接收端

DatagramPacket类构造

方法名说明
DatagramPacket(byte[] buf, int len)创建一个DatagramPacket用于接收长度为len的数据包

DatagramPacket类方法

方法名说明
byte[] getData()返回数据缓冲区
int getLength()返回要发送的数据的长度或接收的数据的长度

UDP方式案例(一对一单线程,发送信息与接收信息的案例)

实现思路:

(1)发送端:创建一个DatagramSocket对象,该对象表示一个UDP套接字,用于发送和接收数据报。通过Scanner类读取控制台输入的字符串,并使用getBytes()方法将字符串转换成字节数组。然后创建一个DatagramPacket对象,该对象包含了要发送的数据、目标IP地址和端口号等信息。通过DatagramSocket的send()方法将数据包发送出去,并关闭套接字端口。

(2)接收端:创建一个DatagramSocket对象,指定接收的端口号。然后创建一个字节数组用于接收数据,以及一个DatagramPacket对象用于接收数据包。通过DatagramSocket的receive()方法接收数据包,并将其存储在字节数组中。最后,通过getData()方法获取数据包中的数据,并使用new String()方法将其转换成字符串输出。

理解:如何理解其中的DatagramSocket,DatagramPacket的创建。

打一个比法:例如你想从你家所在的地方寄一个包裹到目的地,首先需要在你住的地方有一家快递叫DatagramSocket驿站,你需要把你的东西先打包成包裹,此时需要用DatagramPacket打包你要邮寄的数据,而邮寄的数据是字节数据,所以用字节数组去装,然后用DatagramPacket打包,并在包裹上填上邮寄的是什么(数据,数据长度),邮寄的地址(IP地址),接收的人(端口)而在你要邮寄的目的地同样需要一家对应的快递驿站DatagramSocket)去接收你的包裹,通过包裹传递来的信息,可以让你知道用对应的字节数组的箱子去取出东西放入箱子中。

服务器端(接收方):

public class UDPRceive {
    public static void main(String[] args) throws IOException {
        //定义一个套接字端口用来接收,需要写上参数,参数为接收的端口。
        DatagramSocket Socket2=new DatagramSocket(10024);
        //定义一个字节数组来接收传来的数据。
        byte[] bytes2=new byte[1024];
        DatagramPacket packet=new DatagramPacket(bytes2, bytes2.length);
        Socket2.receive(packet);
        System.out.println("接受的数据是:"+new String(packet.getData(), 0,packet.getLength()));
    }
}

客户端(发送方):

public class UDPSend02 {
    public static void main(String[] args) throws IOException {
        //创建套接字端口
        DatagramSocket Socket01=new DatagramSocket();
        Scanner sc=new Scanner(System.in);
        //接收键盘信息进行传输
        String scStr=sc.nextLine();
        byte[] bytes=scStr.getBytes();
        //将数据打包指定发送的IP地址以及接收的端口号。
        DatagramPacket datagramPacket=new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),10024);
        //通过套接字端口去发送包裹
        Socket01.send(datagramPacket);
        Socket01.close();
    }
}

3.3 TCP网络传输

Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。

3.3.1 TCP发送数据

Socket类

Java为客户端提供了Socket类,用于创建客户端发送数据

 Socket类构造

方法名说明
Socket(InetAddress address,int port)创建流套接字并将其连接到指定IP指定端口号
Socket(String host, int port)创建流套接字并将其连接到指定主机上的指定端口号

Socket类方法

方法名说明
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

3.3.2 TCP接收数据

Java为服务器端提供了ServerSocket类,用于接收数据和处理数据

ServerSocket类构造

方法名说明
ServletSocket(int port)创建绑定到指定端口的服务器套接字

ServerSocket类方法

方法名说明
Socket accept()监听要连接到此的套接字并接受它

TCP方式案例(一对一单线程,发送信息与接收信息的案例)

服务器端

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

public class ServerTest {
    public static void main(String[] args) throws IOException {
        //模拟的服务器端
        ServerSocket serverSocket=new ServerSocket(10024);

        Socket socket= serverSocket.accept();//监听套接字数据
        InputStream is= socket.getInputStream();
        byte[] bytes=new byte[1024];
//      用len获取接收到字节长度
        int len= is.read(bytes);
        String data=new String(bytes,0,len);
//      输出提示信息
        System.out.println("服务器:接受的数据为:"+data);

        OutputStream os=socket.getOutputStream();
        /*OutputStream so=socket.getOutputStream()   解释==》 该对象是通过Socket对象s的getOutputStream()方法获取的。
        Socket是Java中用于实现网络通信的类,通过Socket可以连接到远程主机并在网络上进行数据传输。
        getOutputStream()方法返回一个输出流,用于向连接的远程主机发送数据。
        因此,以上代码的含义是创建一个用于向远程主机发送数据的输出流对象os。
        */
//      可以使用os.write()方法向输出流中写入数据,实现与远程主机的数据交换。
        os.write("数据已接收".getBytes());
//      释放资源,对于服务器而言,一般不会关闭资源
//      socket.close();
        serverSocket.close();

    }

}

客户端(发送端)

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class TCPSend {
    public static void main(String[] args) throws IOException {
        //客户端(发送端)
//      根据参数连接到对应的 服务器端 地址IP(127.0.0.1)和对应的端口10024
        Socket socket=new Socket("127.0.0.1",10024);
//      接下来,代码通过s.getOutputStream()方法获取输出流对象os,用于向服务器发送数据。
        OutputStream os= socket.getOutputStream();
        System.out.println("TCP:输入你要发送的信息");
        Scanner sc=new Scanner(System.in);
        String scStr= sc.nextLine();
        os.write(scStr.getBytes());
        System.out.println("输入完毕");

//      通过 输入流 读取来自接收服务器的反馈
        InputStream is=socket.getInputStream();
        byte[] bytes_is=new byte[1024];
        String str_read="";
        int len=is.read(bytes_is);

        str_read=str_read+new String(bytes_is,0,len);

        System.out.println("服务器反馈信息:"+str_read);
        socket.close();
    }
}

运行截图:

客户端输入:

服务器端接收截图:

对比二者可以发现:

UDP协议是无连接的,发送数据时不需要先建立连接,因此发送数据时只需要将数据封装成数据包并指定目标地址和端口号即可,不需要像TCP协议那样需要IO流来传输数据。

而TCP协议是面向连接的,建立连接后才能传输数据,因此需要通过IO流来进行数据传输。TCP协议要求传输的数据必须按照顺序到达,且不允许有丢失和重复数据,因此需要通过IO流来保证数据的可靠传输。在TCP协议中,发送端将数据分成若干个数据段,通过IO流一个一个地发送,接收端通过IO流一个一个地接收并组装成完整的数据。

TCP多线程接收发送消息:

发送端:



//客户端代码:


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

public class TCPClient {

  public static void main(String[] args) throws Exception {
    // 连接服务器
    Socket socket = new Socket("localhost", 8888);

    // 发送数据给服务器
    OutputStream outputStream = socket.getOutputStream();
    PrintWriter writer = new PrintWriter(outputStream);
    writer.println("你好,服务器!");
    writer.flush();

    // 接收服务器响应的数据
    InputStream inputStream = socket.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    String responseData = reader.readLine();
    System.out.println("接收到服务器的响应数据: " + responseData);

    // 关闭连接
    outputStream.close();
    inputStream.close();
    writer.close();
    reader.close();
    socket.close();
  }
}

服务端代码:

//服务器端代码:


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

public class TCPServer {

  public static void main(String[] args) throws Exception {
    // 创建一个服务器Socket,监听端口8888
    ServerSocket serverSocket = new ServerSocket(8888);

    while (true) {
      // 等待客户端的连接
      System.out.println("等待客户端的连接...");
      Socket socket = serverSocket.accept();

      // 创建一个新线程,用于处理客户端的请求
      Thread handlerThread = new Thread(() -> {
        try {
          // 获取输入流,用于接收客户端传输的数据
          InputStream inputStream = socket.getInputStream();
          BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

          // 获取输出流,用于向客户端发送数据
          OutputStream outputStream = socket.getOutputStream();
          PrintWriter writer = new PrintWriter(outputStream);

          // 读取客户端发送的数据,并回传一个响应
          String clientData = reader.readLine();
          System.out.println("接收到客户端的数据: " + clientData);
          writer.println("你好,客户端!");
          writer.flush();

          // 关闭连接
          inputStream.close();
          outputStream.close();
          socket.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      });

      // 启动新线程处理客户端请求,并继续等待下一个客户端连接
      handlerThread.start();
    }
  }
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ヾ草木萤火(≧▽≦*)o

希望大家多多支持,我会继续分享

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值