29.1 Java进阶之网络IP,DNS,TCP,UDP,客户端服务器通信过程,套接字

1.网络术语解释

1.1 什么是IP?

IP是一种互联网协议地址,用它我们可以唯一的标识互联网上的计算机。它由四段用点隔开的0~225的十进制数组成,例如:130.254.204.31

1.2 什么叫DNS?

由于前面的ip地址由于数字很抽象难记,所以我们经常将它们映射为被称为域名的好记的名字。例如:www.baidu.com。在互联网上有特殊的称为域名服务器(DNS)的服务器,它把主机的名字转换成ip地址,然后用这个ip发送请求。

1.3 TCP和UDP

两个ip之间相互通信可以通过传输控制协议(TCP)和用户数据报协议(UDP)实现。
TCP能够让两台主机建立连接并交换数据流,确保数据的传输,也确保数据包以它们发送的顺序传输。
UDP允许一台计算机上的应用程序向另一台计算机上的应用程序发送数据报,UDP不确保数据完整到达。

2.客户端/服务器通信

Java支持基于流的通信和基于包的通信。其中基于流的通信使用TCP进行数据传输,因为TCP协议能够发现丢失的数据信息并重新发送。基于包的使用UDP,此协议不能保证传输没有丢失。所以大多数的java程序使用的是TCP协议(就是基于流的通信方式),保证数据完整传输。

2.1 Java如何进行网络通信?

JavaAPI提供用于创建套接字的类来便于程序通过Internet通信。

一旦Java程序通过Socket建立连接,客户端和服务器就可以通过Socket进行通信。
当客户端尝试连接到服务器时,服务器必须正在运行且为监听状态。

java通信过程如下图

在这里插入图片描述
首先启动服务器程序,服务器程序对指定端口进行监听。
然后客户端通过ip:port尝试连接服务端,连接成功后就能进行通信了。

2.2 下面我们学习Socket类

2.2.1 ServerSocket

要创建服务器,我们可以用ServerSocket类来创建一个套接字对象,并把它附在一个端口上,服务器从这个端口监听连接。
端口号的范围为:0~65536 但其中的0 ~1024是为特定服务保留的端口号,建议不用。

创建语句如下:

//port为设置的服务器端口号
ServerSocket serverSocket=new ServerSocket(port);

创建之后我们可以使用下面语句进行监听:

Socket socket=ServerSocket.accept();

这个语句会一直等待,直到有客户端请求连接到此服务器。

2.2 2 Socket

客户端用以下语句来请求与服务器连接:

//serverName是服务器的互联网主机名或IP,port为服务器建立服务对应的端口号
Socket socket=new Socket(serverName,port);

2.2.3 利用I/O流进行数据传输

服务器和客户端连接后,我们可以从Socket中获取I/O流,然后通过I/O流机制就能实现网络数据传输了。
如图:
在这里插入图片描述
显然我们可以看到,关键是:

socket.getInputStream();//获得套接字输入流
socket.getOutputStream();//获得套接字输出流

2.示例演示(提供两个版本:UI版和无UI版)

3.1 UI版

下面开发出一个在线计算圆面积的例子:
在这里插入图片描述

3.1.1 Server.java

public class Server extends Application {
    public void start(Stage primaryStage){
        TextArea ta=new TextArea();
        Scene scene=new Scene(new ScrollPane(ta),450,200);
        primaryStage.setTitle("Server");
        primaryStage.setScene(scene);
        primaryStage.show();

        /*
          建立一个线程来进行端口监听
         */
        new Thread(()->{
            try{
                //创建服务器套接字,服务器端口号设为8000
                ServerSocket serverSocket=new ServerSocket(8000);

                Platform.runLater(()->ta.appendText("服务器启动成功 日期:"+new Date()+'\n'));

                //服务器进入监听状态
                System.out.println("服务器进入监听状态");
                Socket socket = serverSocket.accept();
                System.out.println("监听到连接,即将进入下一步");
                //创建服务器接受和发送数据的流
                DataInputStream inputFromClient = new DataInputStream(socket.getInputStream());
                DataOutputStream outputToClient=new DataOutputStream(socket.getOutputStream());

                while(true){
                    double radius=inputFromClient.readDouble();//接受到的数据赋给radius

                    double area=radius*radius*Math.PI;//计算面积

                    outputToClient.writeDouble(area);//将计算值发送出去

                    Platform.runLater(()->{
                        ta.appendText("接收到客户端传来的半径::"+radius+'\n');
                        ta.appendText("面积是:"+area+'\n');
                    });
                }

            }catch(IOException ex){
                ex.printStackTrace();
            }
        }).start();
    }
}

3.1.2 Client.java

public class Client extends Application {
    DataOutputStream toServer=null;//声明输出流
    DataInputStream fromServer=null;//声明输入流

    public void start(Stage primaryStage) {

        BorderPane paneForTextField=new BorderPane();
        paneForTextField.setPadding(new Insets(5,5,5,5));
        paneForTextField.setStyle("-fx-border-color:green");
        paneForTextField.setLeft(new Label("请输入半径:"));

        TextField tf = new TextField();
        tf.setAlignment(Pos.BOTTOM_RIGHT);
        paneForTextField.setCenter(tf);

        BorderPane mainPane = new BorderPane();
        TextArea ta = new TextArea();
        mainPane.setCenter(new ScrollPane(ta));
        mainPane.setTop(paneForTextField);

        Scene scene=new Scene(mainPane,450,200);
        primaryStage.setTitle("Client");
        primaryStage.setScene(scene);
        primaryStage.show();

        tf.setOnAction(e->{
            try{
                double radius = Double.parseDouble(tf.getText().trim());
                //将radius写入输出流发送出去
                toServer.writeDouble(radius);
                toServer.flush();//清空输出流
                //从输入流中获取的值赋给area(此方法会阻塞)
                double area = fromServer.readDouble();
                ta.appendText("半径:"+radius+'\n');
                ta.appendText("面积是: "+area+'\n');
            }catch (IOException ex) {
                System.err.println(ex);
            }
        });
        try{
            //创建套接字
            Socket socket = new Socket("localhost",8000);

            //创建客户端接受和发送数据的流
            fromServer = new DataInputStream(socket.getInputStream());
            toServer = new DataOutputStream(socket.getOutputStream());

        }catch(IOException ex) {
            ta.appendText(ex.toString()+'\n');
        }
    }
}

3.2 无UI版

在这里插入图片描述

3.2.1 Server.java

public class Server {

    public static void main(String[] args) {
             /*
          建立一个线程来进行端口监听
         */
        new Thread(()->{
            try{
                //创建服务器套接字,服务器端口号设为8000
                ServerSocket serverSocket=new ServerSocket(8000);
                System.out.println("服务器启动成功 日期:"+new Date()+'\n');
                //服务器进入监听状态
                System.out.println("服务器进入监听状态");
                Socket socket = serverSocket.accept();
                System.out.println("监听到连接,即将进入下一步");
                //创建服务器接受和发送数据的流
                DataInputStream inputFromClient = new DataInputStream(socket.getInputStream());
                DataOutputStream outputToClient=new DataOutputStream(socket.getOutputStream());

                while(true){
                    //接受到的数据赋给radius
                    double radius=inputFromClient.readDouble();
                    double area=radius*radius*Math.PI;//计算面积
                    outputToClient.writeDouble(area);//将计算值发送出去
                    System.out.println("接收到客户端传来的半径::"+radius+'\n');
                    System.out.println("面积是:"+area+'\n');
                }

            }catch(IOException ex){
                ex.printStackTrace();
            }
        }).start();
    }

}

3.2.2 Client.java

public class Client {

    public static void main(String[] args) {
        DataOutputStream toServer=null;//声明输出流
        DataInputStream fromServer=null;//声明输入流
        try{
            //创建套接字
            Socket socket = new Socket("localhost",8000);

            //创建客户端接受和发送数据的流
            fromServer = new DataInputStream(socket.getInputStream());
            toServer = new DataOutputStream(socket.getOutputStream());

            while (true){
                System.out.println("请输入半径:");
                Scanner sc = new Scanner(System.in);
                double radius = sc.nextDouble();
                //将radius写入输出流发送出去
                toServer.writeDouble(radius);
                toServer.flush();//清空输出流
                //从输入流中获取的值赋给area(此方法会阻塞)
                double area = fromServer.readDouble();
                System.out.println("半径:"+radius+'\n');
                System.out.println("面积是: "+area+'\n');
            }

        }catch(IOException ex) {
            ex.printStackTrace();
        }

    }

}

4 代码地址

Java基础学习/src/main/java/Progress/exa29_1 · 严家豆/Study - 码云 - 开源中国 (gitee.com)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值