zoukankan      html  css  js  c++  java
  • Java高级篇(二)——网络通信

      网络编程是每个开发人员工具箱中的核心部分,我们在学习了诸多Java的知识后,也将步入几个大的方向,Java网络编程就是其中之一。

      如今强调网络的程序不比涉及网络的更多。除了经典的应用程序,如电子邮件、Web浏览器和远程登陆外,大多数主要的应用程序都有某种程度的内质网络功能。比如我们最常使用的IDE(Eclipse/IDEA)与源代码存储库(GitHub等等)进行通信;再比如Word,可以从URL打开文件;又或者是我们玩的众多联机游戏,玩家实时相互对战等等。Java是第一个从一开始就为网络应用而设计的编程语言,最早的两个实用Java应用的程序之一就是Web浏览器,随着Internet的不断发展,Java成为了唯一适合构建下一代网络应用程序的语言。(节选自Java NetWork Programming——Elliotte Rusty Harold著

    一、Client/Server

      为了实现两台计算机的通信,必须要用一个网络线路连接两台计算机。服务器(Server)是指提供信息的计算机或程序,客户机(Client)是指请求信息的计算机或程序,而网络用于连接服务器与客户机,实现两者相互通信。

      下面我们看一个简单的通信例子。

      如下的Server程序是一个服务器端应用程序,使用 Socket 来监听一个指定的端口。

     1 import java.io.IOException;
     2 import java.io.InputStreamReader;
     3 import java.io.Reader;
     4 import java.net.ServerSocket;
     5 import java.net.Socket;
     6 
     7 public class Server {
     8 
     9     public static void main(String[] args) throws IOException {
    10         int port = 9999;
    11         
    12         System.out.println("-----------服务器启动-----------");
    13         
    14         ServerSocket server = new ServerSocket(port);
    15         Socket socket = server.accept();
    16         Reader reader = new InputStreamReader(socket.getInputStream());
    17         char chars[] = new char[1024];
    18         int len;
    19         StringBuilder builder = new StringBuilder();
    20         while ((len=reader.read(chars)) != -1) {
    21            builder.append(new String(chars, 0, len));
    22         }
    23         System.out.println("收到来自客户端的信息: " + builder);
    24         reader.close();
    25         socket.close();
    26         server.close();
    27     }
    28 
    29 }

       如下的Client是一个客户端程序,该程序通过 socket 连接到服务器并发送一个请求,然后等待一个响应。

     1 import java.io.IOException;
     2 import java.io.OutputStreamWriter;
     3 import java.io.Writer;
     4 import java.net.Socket;
     5 import java.util.Scanner;
     6 
     7 public class Client {
     8 
     9     public static void main(String[] args) throws IOException {
    10         String host = "127.0.0.1";
    11         int port = 9999;
    12         
    13         System.out.println("-----------客户端启动-----------");
    14         
    15         Socket client = new Socket(host, port);
    16         Writer writer = new OutputStreamWriter(client.getOutputStream());
    17         Scanner in = new Scanner(System.in);
    18         writer.write(in.nextLine());
    19         writer.flush();
    20         writer.close();
    21         client.close();
    22         in.close();
    23     }
    24     
    25 }

      先启动服务器,运行结果如下:

      

      再运行客户端,并输入要发送的信息,如下:

      

      此时Server就接收到了Client发送的消息,如下:

      

      这就是一个简单的套接字通信了,具体分析见下方TCP。

    二、TCP

      TCP网络程序设计是指利用Socket类编写通信程序,具体实例参照上例。

      套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。

      两台计算机间使用套接字建立TCP连接步骤如下:

    • 服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。

    • 服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。

    • 服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。

    • Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。

    • 在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。

      连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。

      1. InetAddress

      java.net包中的InetAddress类是与IP地址相关的类,利用该类可以获取IP地址、主机地址等信息。

    InetAddress ip = InetAddress.getLocalHost();
    String localName = ip.getHostName();    //获取本地主机名
    String localIp = ip.getHostAddress();    //获取本地主机的ip地址

      2. ServerSocket 

      java.net包中的ServetSocket类用于表示服务器套接字,其主要功能是等待来自网络上的“请求”,它可以通过指定的端口来等待连接的套接字(如上方实例中的9999)。

      而服务器套接字一次可以与一个套接字连接,如果多台客户机同时提出连接请求,服务器套接字会将请求连接的客户机存入队列中,然后从中取出一个套接字,与服务器新建的套接字连接起来。若请求连接数大于最大容纳数,则多出的连接请求被拒绝。

      ServerSocket的具体方法可参照API,这里只对accept()方法进行一个说明。调用accept()方法将返回一个与客户端Socket对象相连的Socket对象,服务器端的Socket对象使用getOutputStream()方法获得的输出流对象,将指向客户端Socket对象使用getInputStream()方法获得的输入流对象,反之亦然。

      需要注意的是,accept()方法会阻塞线程的继续执行,直到接受客户的呼叫,如果没有客户呼叫服务器,那么下面的代码将不会执行。

    三、UDP

      用户数据包协议(UDP)是网络信息传输的另一种形式,基于UDP的通信与基于TCP的通信不同,UDP的信息传递更快,但不提供可靠的保证。

      1. DatagramPacket

      java.net包中的DatagramPacket类用来表示数据包,构造方法如下:

    DatagramPacket(byte[] buf, int length)
    DatagramPacket(byte[] buf, int length, InetAddress address, int port)

      上述构造方法中,第一种指定了数据包的内存空间和大小,第二种不仅指定了数据包的内存空间和大小,并且指定了数据包的目标地址和端口。

      2. DatagramSocket

      java.net包中的DatagramSocket类用于表示发送和接受数据包的套接字,构造方法如下:

    DatagramSocket()
    DatagramSocket(int port)
    DatagramSocket(int port, InetAddress addr)

      上述构造方法中,第一种创建数据包套接字并将其绑定到本地主机上任何可用的端口,第二种创建数据包套接字并将其绑定到本地主机上的指定端口,创建数据包套接字并将其绑定到指定的本机地址

     四、实例测试

      下面写一个完整的实例,题目如下:

    • 建立服务端程序,服务器端程序接收来自客户端的请求;
    • 从网上下载程序,英语900句,每句占一行;
    • 服务端读取该文件,保存到集合或者列表中;
    • 建立客户端程序,使用”sentence: <编号#>,<编号#>”的格式发生数据。例如:发送”sentense:1,2,3” , 服务端把相应编号的句子发送给客户端,并加以呈现;
    • 客户端需要把服务端发送的句子保存起来,如果已经保存有相应的句子,将不再保存;
    • 客户端需要把从服务端获取的数据存储到文件中。

       1. 程序结构

      

      2. Server.java

      该类为客户端类。

     1 import java.io.*;
     2 import java.net.ServerSocket;
     3 import java.net.Socket;
     4 import java.util.ArrayList;
     5 import java.util.List;
     6 
     7 /**
     8  * 
     9  * @author adamjwh
    10  *
    11  */
    12 public class Server {
    13     
    14     public static List<String> sentence;
    15     private static String filename = "src/com/adamjwh/jnp/ex6_2/English900.txt";
    16     
    17     public static void main(String[] args) throws IOException {
    18         sentence=new ArrayList<>();
    19         System.out.println("-----------服务器启动-----------");
    20         
    21         FileReader fileReader = new FileReader(filename);
    22         BufferedReader br = new BufferedReader(fileReader);
    23         
    24         String inputLine = null;
    25         while ((inputLine = br.readLine()) != null) {
    26             sentence.add(inputLine);
    27         }
    28 
    29         ServerSocket ss = new ServerSocket(9999);
    30         while(true){
    31             Socket sk =ss.accept();
    32             ServerThread st = new ServerThread(sk);
    33             st.start();
    34         }
    35         
    36     }
    37 }

      2. Client.java

      该类为服务器类。

    import java.io.BufferedReader;
    import java.io.FileWriter;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.PrintStream;
    import java.net.Socket;
    import java.util.Scanner;
    
    /**
     * 
     * @author adamjwh
     *
     */
    public class Client {
        
        private static String filename = "src/com/adamjwh/jnp/ex6_2/result.txt";
        
        public static void main(String[] args) {
            try {
                Socket sk = new Socket("127.0.0.1", 9999);
                System.out.println("-----------客户端启动-----------");
    
                PrintStream ps = new PrintStream(sk.getOutputStream());
                System.out.print("发送:");
                Scanner sn = new Scanner(System.in);
                String str = sn.nextLine();
                ps.println(str);
    
                InputStream is = sk.getInputStream();
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
                
                //写文件
                FileWriter fw = new FileWriter(filename, true);
                //读文件
                String result = new ReadFile().readFile(filename);
                
                String s;
                while ((s = br.readLine()) != null) {
                    System.out.println("服务器推送:" + s);
                    if(!result.contains(s)) {
                        fw.write(s + "
    ");
                    }
                }
    
                sk.shutdownInput();
                ps.close();
                sk.close();
                fw.close();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    }

      3. ServerThread

      通过线程实现信息交互。

     1 import java.io.*;
     2 import java.net.Socket;
     3 
     4 /**
     5  * 
     6  * @author adamjwh
     7  *
     8  */
     9 public class ServerThread extends Thread{
    10     Socket sk;
    11     public ServerThread(Socket sk){
    12         this.sk= sk;
    13     }
    14     public void run() {
    15         BufferedReader br=null;
    16         try{
    17             br  = new BufferedReader(new InputStreamReader(sk.getInputStream()));
    18             String line = br.readLine();
    19             System.out.println("客户端:"+line);
    20             String[] split = line.split(":");
    21             String[] split1 = split[split.length - 1].split(",");
    22             sk.shutdownInput();
    23 
    24             OutputStream os = sk.getOutputStream();
    25 
    26             PrintStream bw = new PrintStream(os);
    27 
    28             //给客户端返回信息
    29             for(int i=0;i<split1.length;i++) {
    30                 bw.print(Server.sentence.get(Integer.parseInt(split1[i])%Server.sentence.size())+"
    ");
    31             }
    32             bw.flush();
    33             br.close();
    34             sk.close();
    35         }
    36         catch(IOException e){
    37             e.printStackTrace();
    38         }
    39     }
    40 }

       4. 运行结果

      先运行服务器

      

      再运行客户端,并输入信息

      

      服务器接收到客户端发来的请求

      

       服务器将请求结果推送给客户端,这里客户端就获取到了它请求的第174句、第258句及第5句,并输出到文件中

      

      这里的测试文件我测试到了162(也就是该文件的第15行),可以看到在文件中追加了两行新的句子(174和258),而第5句因为在之前已经出现过了(该文件的第1行),所以没有追加第5句,完成上述题目。

      

  • 相关阅读:
    MySQL-基础知识整理
    设计模式-适配器模式
    MySQL
    MySQL-5.7 填坑
    MySQL
    Oracle
    SQL 注入
    Apache JMeter
    AppScan
    DNS 搜索
  • 原文地址:https://www.cnblogs.com/adamjwh/p/8856516.html
Copyright © 2011-2022 走看看