Java通信

66 阅读6分钟
  • 在了解java通信之前,需要知道三次握手建立通信连接,四次挥手释放通信连接。

tcp,全双工通信,建立连接的双方可以同时发送消息和同时接收消息,无需等待另外一方消息发送才进行消息的发送。

半双工通信,典型例子就是对讲机,在半双工通信中,信息的传输是双向的,但是同一时间只能在一个方向上传输。这意味着通信的两个参与者可以交替地发送和接收信息,但不能同时进行发送和接收。

  • 三次握手

image.png

第一步:客户端发送一个带有SYN(同步)标志的TCP报文段给服务器,表示客户端请求建立连接。

第二步:服务器接收到客户端的请求后,回复一个带有SYN/ACK(同步/确认)标志的报文段,表示接受连接请求。

第三步:客户端收到服务器的回复后,再发送一个带有ACK(确认)标志的报文段给服务器,表示连接建立成功。

因为需要创建是全双工通信,第一步是确保客户端具有发送请求的能力,第二部是确保服务端有接收和发送请求的能力,第三步是确保客户端具有接收请求的能力。

  • 四次挥手

image.png

FIN1:当主动关闭连接的一方(比如说 A)完成数据发送任务之后,它需要向另一方(比如说 B)发送一个 FIN(结束)报文,表明 A 不再发送数据了。

ACK1:B 收到 A 的 FIN 报文后,会发送一个 ACK(确认)报文,表明它已经知道 A 不再发送数据了。但是,这个时候 B 可能还有数据需要发送给 A。

FIN2:当 B 也完成数据发送任务后,它会向 A 发送一个 FIN 报文,表明 B 也不再发送数据了。

ACK2:A 收到 B 的 FIN 报文后,也会发送一个 ACK 报文,表明它已经知道 B 不再发送数据了。

在fin2发送完成之后,客户端进入了time_wait状态,需要等待2msl才能关闭。准确来说是2msl内没有接收到任何报文了才会进行关闭。msl是Maximum Segment Lifetime的缩写,表示的是报文在网络中存活的最大时长。

为什么不是1msl或者3msl呢?在第四次挥手之后(发送报文)开始计时,第四次发送的报文如果正常到达了服务端,这是没有任何问题的。

但是如果说第四次发送的报文出问题了(可能各种情况没有达到服务端),这条出问题的最大时间是1msl,因为出问题了,服务端没有接收到,所以服务端会再次发送一条fin2报文,这条报文的最大时间也是1msl,所以客户端最大等待时间为2msl,就可以确保客户端和服务端都能够正常关闭连接。

同时还有一个作用就是,在这个2msl内能够让本次连接产生的请求在网络中消失,否则下次创建连接,可能会影响到下次的的连接创建。

  • 对基本的网络有了概念之后,我们目的就是为了了解java的网络通信

需要认识几个常用的io流,InputStream,OutputStream,InputStreamReader, OutputStreamWriter,这里输入输出的参考对象是程序

  1. InputStream,字节输入流,将字节消息输入到程序中
  2. OutputStream,字节输出流,将在程序中的字节信息输出到其它地方
  3. InputStreamReader,字符输入流,故名思意,这是用于存储字符的,本质是将字节转化为字符输入到程序中
  4. OutputStreamWriter,字符输入流,这也是用于存储字符的,本质上是将字符转化为字节输入到其它地方
package package0527;

import java.io.*;

//java io
public class Class527_IO {

    public static void main(String[] args) throws IOException {
        //InputStream和OutputStream都是字节输入输出流,是通过字节进行存储的byte
        InputStream inputStream = new FileInputStream("D:/我的文件/input.txt");
        OutputStream outputStream = new FileOutputStream("D:/我的文件/output.txt");

        //将input.txt文字复制到output.txt

        byte[] buffer = new byte[1024];
        int bytesRead;
        int read = inputStream.read(buffer);


        //inputStream.read(buffer)表示从输入流(inputStream)中读取字节数据 等于-1代表文件末尾
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }

        System.out.println("文件复制成功!");

        inputStream.close();
        outputStream.close();
    }
}
package package0527;

import java.io.*;

//java io
public class Class527_IO_WriterAndReader {

    public static void main(String[] args) throws IOException {
        //读取文件
        InputStreamReader fileReader = new FileReader("D:/我的文件/input.txt");
        OutputStreamWriter fileWriter = new FileWriter("D:/我的文件/output.txt",true);//增量复制,如果文件不存在则进行创建

        char[] chars = new char[1024];
        int charsRead;

        //将input文件复制到output文件
        while ((charsRead=fileReader.read(chars))!=-1){
            String s = new String(chars, 0, charsRead);
            System.out.println(s);
            fileWriter.write(chars,0,charsRead);
            fileWriter.write("\n");//换行
        }

        System.out.println("文件复制成功");

        fileReader.close();
        fileWriter.close();
    }
}

以上是io 流的基本操作,在了解清楚了java的io流的操作之后,对于java 通信,那么也是很好解决了,java的通信本质上也是Java io的操作。

  • java提供了Socket和ServerSocket两个套接字,一个表示客户端,一个表示服务端,服务端可以通过accept()方法来获取客户端(监听)。
package package0527;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.TimeUnit;

/**
 * 通信(客户端)
 * */
public class Class527_Socket {

    public static void main(String[] args) {
        String serverAddress="127.0.0.1";
        int serverPort=8080;

        try{

            //创建客户端(连接)
            Socket socketClient = new Socket(serverAddress, serverPort);
            TimeUnit.SECONDS.sleep(10);

            OutputStream outputStream = socketClient.getOutputStream();
            String message="Hello,TCP";
            outputStream.write(message.getBytes());

            TimeUnit.SECONDS.sleep(5);

            InputStream inputStream = socketClient.getInputStream();
            byte[] buffer = new byte[1024];
            int read = inputStream.read(buffer);
            String receiveMessage = new String(buffer, 0, read);
            System.out.println("接收到服务器数据为:"+receiveMessage);

            socketClient.close();
        }catch (Exception e){
            e.printStackTrace();
        }


    }

}
package package0527;

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

/**
 * 通信(服务端)
 * */
public class Class517_ServerSocket {

    public static void main(String[] args) throws IOException {

        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("服务器已启动,等待客户端连接!");
        String info = serverSocket.getInetAddress().toString() + serverSocket.getLocalPort();
        System.out.println(info + serverSocket.getLocalSocketAddress());

        try {

            while (true) {
                Socket socketClient = serverSocket.accept(); // 阻塞

                String s = socketClient.getInetAddress().toString() + socketClient.getLocalPort();
                System.out.println(s);

                InputStream inputStream = socketClient.getInputStream();
                OutputStream outputStream = socketClient.getOutputStream();
                System.out.println(inputStream);
                System.out.println(outputStream);

                // 服务端接收客户端消息
                byte[] buffer = new byte[1024];
                int read = inputStream.read(buffer);
                String receiveMessage = new String(buffer, 0, read);
                System.out.println("接收到客户端数据为:" + receiveMessage);

                // 服务端发送数据
                outputStream.write("Hello, Client!".getBytes());

                // 关闭当前客户端连接
                socketClient.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭服务器套接字
            serverSocket.close();
        }
    }
}

额外认识一下BufferedReader和BufferedWriter,这两个为字符缓冲输入流和字符缓冲输出流,拿上面文件复制举例,从input.txt文件复制到output.txt文件,input.txt文件到程序,就是字节到字符的过程,程序到output.txt文件,就是字符到字节的过程。

BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D:/我的文件/output.txt")));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("D:/我的文件/input.txt")));

String s = bufferedReader.readLine();
System.out.println("input文件内容为"+s);

bufferedWriter.write(s);
bufferedWriter.newLine();
bufferedWriter.flush();

bufferedReader.close();
bufferedWriter.close();