网络编程是每个开发人员工具箱中的核心部分,我们在学习了诸多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 来监听一个指定的端口。

 import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.ServerSocket;
import java.net.Socket; public class Server { public static void main(String[] args) throws IOException {
int port = 9999; System.out.println("-----------服务器启动-----------"); ServerSocket server = new ServerSocket(port);
Socket socket = server.accept();
Reader reader = new InputStreamReader(socket.getInputStream());
char chars[] = new char[1024];
int len;
StringBuilder builder = new StringBuilder();
while ((len=reader.read(chars)) != -1) {
builder.append(new String(chars, 0, len));
}
System.out.println("收到来自客户端的信息: " + builder);
reader.close();
socket.close();
server.close();
} }

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

 import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket;
import java.util.Scanner; public class Client { public static void main(String[] args) throws IOException {
String host = "127.0.0.1";
int port = 9999; System.out.println("-----------客户端启动-----------"); Socket client = new Socket(host, port);
Writer writer = new OutputStreamWriter(client.getOutputStream());
Scanner in = new Scanner(System.in);
writer.write(in.nextLine());
writer.flush();
writer.close();
client.close();
in.close();
} }

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

  

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

  

  此时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

  该类为客户端类。

 import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List; /**
*
* @author adamjwh
*
*/
public class Server { public static List<String> sentence;
private static String filename = "src/com/adamjwh/jnp/ex6_2/English900.txt"; public static void main(String[] args) throws IOException {
sentence=new ArrayList<>();
System.out.println("-----------服务器启动-----------"); FileReader fileReader = new FileReader(filename);
BufferedReader br = new BufferedReader(fileReader); String inputLine = null;
while ((inputLine = br.readLine()) != null) {
sentence.add(inputLine);
} ServerSocket ss = new ServerSocket(9999);
while(true){
Socket sk =ss.accept();
ServerThread st = new ServerThread(sk);
st.start();
} }
}

  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

  通过线程实现信息交互。

 import java.io.*;
import java.net.Socket; /**
*
* @author adamjwh
*
*/
public class ServerThread extends Thread{
Socket sk;
public ServerThread(Socket sk){
this.sk= sk;
}
public void run() {
BufferedReader br=null;
try{
br = new BufferedReader(new InputStreamReader(sk.getInputStream()));
String line = br.readLine();
System.out.println("客户端:"+line);
String[] split = line.split(":");
String[] split1 = split[split.length - 1].split(",");
sk.shutdownInput(); OutputStream os = sk.getOutputStream(); PrintStream bw = new PrintStream(os); //给客户端返回信息
for(int i=0;i<split1.length;i++) {
bw.print(Server.sentence.get(Integer.parseInt(split1[i])%Server.sentence.size())+"\n");
}
bw.flush();
br.close();
sk.close();
}
catch(IOException e){
e.printStackTrace();
}
}
}

  4. 运行结果

  先运行服务器

  

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

  

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

  

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

  

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

  

Java高级篇(二)——网络通信的更多相关文章

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

    本文转自https://zhuanlan.zhihu.com/p/25713880. JVM的基础概念 JVM的中文名称叫Java虚拟机,它是由软件技术模拟出计算机运行的一个虚拟的计算机. JVM也充 ...

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

    JVM内存结构如 Java堆(Heap),是Java虚拟机所管理的内存中最大的一块.Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建.此内存区域的唯一目的就是存放对象实例,几乎所有的对象实 ...

  3. 【转】java提高篇(二)-----理解java的三大特性之继承

    [转]java提高篇(二)-----理解java的三大特性之继承 原文地址:http://www.cnblogs.com/chenssy/p/3354884.html 在<Think in ja ...

  4. java提高篇(二)-----理解java的三大特性之继承

    在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情.在这句 ...

  5. (转)java提高篇(二)-----理解java的三大特性之继承

    在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情.在这句 ...

  6. java架构《并发线程高级篇二》

    本章主要记录讲解并发线程的线程池.使用Executor框架自定义线程池. 自定义线程池使用Queue队列所表示出来的形式: 1 ArrayBlockingQueue<Runnable>(3 ...

  7. Java高级篇(一)——线程

    前面我们系统的了解了Java的基础知识,本篇开始将进入到Java更深层次的介绍,我们先来介绍一下Java中的一个重要的概念--线程. 一.什么是线程 在了解线程前,我们首先要了解进程的概念.进程是操作 ...

  8. Java高级篇(三)——JDBC数据库编程

    JDBC是连接数据库和Java程序的桥梁,通过JDBC API可以方便地实现对各种主流数据库的操作.本篇将介绍一下如何使用JDBC操作数据库(以MySQL为例). 一.JDBC JDBC制定了统一访问 ...

  9. Java高级篇(四)——反射

    之前写到了设计模式的代理模式,因为下一篇动态代理等内容需要用到反射的知识,所以在之前Java篇的基础上再写一篇有关反射的内容,还是以实际的程序为主,了解反射是做什么的.应该怎么用. 一.什么是反射 反 ...

随机推荐

  1. 如何正确使用Java异常处理机制

    文章来源:leaforbook - 如何正确使用Java异常处理机制作者:士别三日 第一节 异常处理概述 第二节 Java异常处理类 2.1 Throwable 2.1.1 Throwable有五种构 ...

  2. java排序算法(八):希尔排序(shell排序)

    java排序算法(八):希尔排序(shell排序) 希尔排序(缩小增量法)属于插入类排序,由shell提出,希尔排序对直接插入排序进行了简单的改进,它通过加大插入排序中元素之间的间隔,并在这些有间隔的 ...

  3. java排序算法(三):堆排序

    java排序算法(三)堆排序 堆积排序(HeapSort)是指利用堆积树这种结构所设计的排序算法,可以利用数组的特点快速定位指定索引的元素.堆排序是不稳定的排序方法.辅助空间为O(1).最坏时间复杂度 ...

  4. javaAPI中的常用 类 以及接口

    java.lang包中的常用类以及接口 类 1. Integer :Integer 类在对象中包装了一个基本类型 int 的值.Integer 类型的对象包含一个 int 类型的字段. 2. Math ...

  5. iOS 代码开发规范

    最近公司因为业务增多,又招了一个iOS小伙伴,内心无比开心呀.但是多了一个人,日常代码开发就要统一规范起来,方便日后维护和查找.下边列一下我日常用到一些代码规范. (一).SVN规范 1.代码一定要先 ...

  6. 云计算之路-阿里云上-容器难容:优化自建 docker swarm 集群的部署

    在上周六遭遇阿里云容器服务 swarm 版的故障之后,我们决定还是走自建 docker swarm 之路,只要不是阿里云底层的问题,我们相信会找到办法解决或避开自建 docker swarm 不稳定的 ...

  7. 进程与fork()、wait()、exec函数组

    进程与fork().wait().exec函数组 内容简介:本文将引入进程的基本概念:着重学习exec函数组.fork().wait()的用法:最后,我们将基于以上知识编写Linux shell作为练 ...

  8. Beta冲刺计划---Day0

    Beta阶段报告---Day0 1.需要改进完善的功能   我们上一阶段开发由于开发时间匆忙,对于爬虫耗时的优化没有考虑.优化的空间我在Alpha阶段的总结报告里说过,具体看下图.   这张图显示出爱 ...

  9. Scrum 冲刺 第四日

    目录 要求 项目链接 燃尽图 问题 今日任务 明日计划 成员贡献量 小组会议 要求 各个成员今日完成的任务(如果完成的任务为开发或测试任务,需给出对应的Github代码签入记录截图:如果完成的任务为调 ...

  10. HTTP协议以及HTTP2.0/1.1/1.0区别

    HTTP协议以及HTTP2.0/1.1/1.0区别 一.简介 摘自百度百科: 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议.所 ...