Java高级篇——网络通信

JAdam

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 adamjwh10  *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 + "\n");
                }
            }

            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())+"\n");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句,完成上述题目。

  

原文地址:http://blog.51cto.com/11436461/2104177

时间: 2024-11-09 03:17:08

Java高级篇——网络通信的相关文章

JAVA高级篇(二、JVM内存模型、内存管理之第二篇)

本文转自https://zhuanlan.zhihu.com/p/25713880. JVM的基础概念 JVM的中文名称叫Java虚拟机,它是由软件技术模拟出计算机运行的一个虚拟的计算机. JVM也充当着一个翻译官的角色,我们编写出的Java程序,是不能够被操作系统所直接识别的,这时候JVM的作用就体现出来了,它负责把我们的程序翻译给系统"听",告诉它我们的程序需要做什么操作. 我们都知道Java的程序需要经过编译后,产生.Class文件,JVM才能识别并运行它,JVM针对每个操作系统

Java高级篇(四)——反射

之前写到了设计模式的代理模式,因为下一篇动态代理等内容需要用到反射的知识,所以在之前Java篇的基础上再写一篇有关反射的内容,还是以实际的程序为主,了解反射是做什么的.应该怎么用. 一.什么是反射 反射就是把Java类中的各个成分映射成一个个的Java对象.即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法:对于任意一个对象,都能调用它的任意一个方法和属性.这种动态获取信息及动态调用对象方法的功能叫Java的反射机制. 1. 反射机制的功能 Java反射机制主要提供了以下功能: 在

JAVA高级篇(一、JVM基本概念)

一.什么是JVM VM的中文名称叫Java虚拟机,它是由软件技术模拟出计算机运行的一个虚拟的计算机. JVM也充当着一个翻译官的角色,我们编写出的Java程序,是不能够被操作系统所直接识别的,这时候JVM的作用就体现出来了,它负责把我们的程序翻译给系统"听",告诉它我们的程序需要做什么操作. 我们都知道Java的程序需要经过编译后,产生.Class文件,JVM才能识别并运行它,JVM针对每个操作系统开发其对应的解释器,所以只要其操作系统有对应版本的JVM,那么这份Java编译后的代码就

JAVA高级篇(三、JVM编译机制、类加载机制)

一.类的加载过程 JVM将类的加载分为3个步骤: 1.装载(Load) 2.链接(Link) 3.初始化(Initialize) 其中 链接(Link)又分3个步骤,如下图所示: 1) 装载:查找并加载类的二进制数据(查找和导入Class文件) 加载是类加载过程的第一个阶段,在加载阶段,虚拟机需要完成以下三件事情: 1.通过一个类的全限定名来获取其定义的二进制字节流. 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构. 3.在Java堆中生成一个代表这个类的java.lang.C

java 第十一天 面向对象(高级篇之接口、对象的多态性,instanceof)

接口的基本概念:接口的访问权限是public,关键字interface,子类通过implements关键字实现接口.一个子类可以同时实现多个接口. 对象的多态性: 两种体现形式:(1)方法的重载与覆写.(2)对象的多态性. 对象的多态性分两种: (1)向上转型:子类对象--→父类对象 (2)向下转型:父类对象--→子类对象 java中可以使用instanceof关键字判断对象到底是哪个类的实例 java 第十一天 面向对象(高级篇之接口.对象的多态性,instanceof),码迷,mamicod

Java 编程之美:并发极速赛车平台出租编程高级篇

借用 Java 并发极速赛车平台出租haozbbs.comQ1446595067 编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了. 相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作的顺序是不可预期的. 并发编程相比 Java 中其他知识点学习起来门槛相对较高,学习起来比较费劲,从而导致很多人望而却步: 而无论是职场面试和高并发高流量的系统的实现却都还离不开并发编程,从而导致能够真正掌握并发编程的人才成为市场比较迫

Spark性能优化指南——高级篇

Spark性能优化指南--高级篇 [TOC] 前言 继基础篇讲解了每个Spark开发人员都必须熟知的开发调优与资源调优之后,本文作为<Spark性能优化指南>的高级篇,将深入分析数据倾斜调优与shuffle调优,以解决更加棘手的性能问题. 数据倾斜调优 调优概述 有的时候,我们可能会遇到大数据计算中一个最棘手的问题--数据倾斜,此时Spark作业的性能会比期望差很多.数据倾斜调优,就是使用各种技术方案解决不同类型的数据倾斜问题,以保证Spark作业的性能. 数据倾斜发生时的现象 绝大多数tas

java基础篇---I/O技术(三)

接上一篇java基础篇---I/O技术(二) Java对象的序列化和反序列化 什么叫对象的序列化和反序列化 要想完成对象的输入或输出,还必须依靠对象输出流(ObjectOutputStream)和对象输入流(ObjectInputStream).使用对象输出流输出序列化对象的步骤,有时也成序列化,而使用对象输入流读入对象的过程,有时也称为反序列化 一个对象产生之后实际上是在内存中为其开辟了一个存储空间,方便存储信息. 对象序列化就是把一个对象变成二进制的数据流的一个方法,通过对象序列化可以反驳的

在Eclipse中使用JUnit4进行单元测试(高级篇)【转】

通过前 2 篇文章,您一定对 JUnit 有了一个基本的了解,下面我们来探讨一下JUnit4 中一些高级特性. 一.     高级 Fixture 上一篇文章中我们介绍了两个 Fixture 标注,分别是 @Before 和 @After ,我们来看看他们是否适合完成如下功能:有一个类是负责对大文件(超过 500 兆)进行读写,他的每一个方法都是对文件进行操作.换句话说,在调用每一个方法之前,我们都要打开一个大文件并读入文件内容,这绝对是一个非常耗费时间的操作.如果我们使用 @Before 和