本文目的是大概了解 Java 网络编程体系,需要一点点 Java IO 基础,推荐教程 系统学习 Java IO。主要参考 JavaDoc 和 Jakob Jenkov 的英文教程《Java Networking》 http://tutorials.jenkov.com/java-networking/index.html

Java 网络编程概览

Java 有一个相当容易使用的内置网络 API,可以很容易地通过互联网上的 TCP / IP 套接字或 UDP 套接字进行通信。 TCP 通常比 UDP 使用得更频繁。

即使 Java Networking API 允许通过套接字打开和关闭网络连接,但所有通信都通过 Java IO 类 InputStream 和 OutputStream 实现的。

或者,我们可以使用 Java NIO API 中的网络类。 用法类似于 Java Networking API 中的类,但 Java NIO API 可以在非阻塞模式下工作。 在某些情况下,非阻塞模式可提升性能。

Java TCP 网络基础

通常,客户端会打开与服务器的 TCP / IP 连接,然后开始与服务器通信,当通信结束后客户端关闭连接。如下图:

客户端可以通过一个已打开的连接发送多个请求,实际上,客户端可以向服务器发送尽可能多的数据。 当然,如果需要,服务器也可以关闭连接。

Java 中 Socket 类和 ServerSocket 类

当客户端想要打开到服务器的 TCP / IP 连接时,它使用 Java Socket 类来实现。 套接字被告知连接到哪个 IP 地址和 TCP 端口,其余部分由 Java 完成。

如果要启动服务器以侦听来自某个 TCP 端口上的客户端的传入连接,则必须使用 Java ServerSocket 类。 当客户端通过客户端套接字连接到服务器的 ServerSocket 时,服务器上会为该连接分配一个 Socket 。 客户端和服务器的通信就是 Socket 到 Socket 的通信了。

Socket和ServerSocket在后面的文本中有更详细的介绍。

Java UDP 网络基础

UDP 的工作方式与 TCP 略有不同。 使用 UDP ,客户端和服务器之间没有连接。 客户端可以向服务器发送数据,并且服务器可以(或可以不)接收该数据。 客户端永远不会知道数据是否在另一端收到。 从服务器到客户端发送的数据也是如此。

由于无法保证数据传输,因此 UDP 协议的协议开销较小。

在一些情况下,无连接 UDP 模型优于 TCP ,比如传输视频等多媒体文件,缺少一些数据是不影响观看的。

TCP Socket(套接字)

为了通过 Internet 连接到服务器(通过TCP / IP),需要创建一个 Socket 并将其连接到服务器。 或者,如果您更喜欢使用 Java NIO ,则可以使用 Java NIO SocketChannel 。

创建一个Socket
Socket socket = new Socket("baidu.com", 80);

第一个参数是地址,可以是 ip 或者域名字符串,第二个参数是端口,端口80是Web服务器端口。

写入 Socket

要写入 Socket,必须获取其 OutputStream :

Socket socket = new Socket("baidu.com", 80);
OutputStream outputStream = socket.getOutputStream(); outputStream.write("some data".getBytes());
outputStream.flush();
outputStream.close(); socket.close();

当真的希望通过互联网向服务器发送数据时,不要忘记调用 flush() 。操作系统中的底层 TCP / IP 实现会先缓冲数据,缓冲块的大小是与 TCP ​​/ IP 数据包的大小相适应的,这就是说,调用 flush() 只是通知系统发送,但系统并不是立即就帮忙发出去。

从 Socket 读取

要从 Socket 读取,需要获取其 InputStream :

Socket socket = new Socket("baidu.com", 80);
InputStream in = socket.getInputStream(); int data = in.read();
//... read more data... in.close();
socket.close();

记住,在读取时我们不能使用读取 InputStream 返回 -1 来判断数据读取结束 ,因为只有在服务器关闭连接时才返回 -1 。 但是服务器可能并不总是关闭连接,比如通过同一连接发送多个请求。 在这种情况下,关闭连接将是非常愚蠢的。

相反,必须知道从 Socket 的 InputStream 中读取多少字节。 服务器会告知 Socket 它发送的字节数,或者通过查找特殊的数据结束字符来完成。

使用 Socket 后,必须关闭它以关闭与服务器的连接,这可以通过调用 Socket 对象的 close() 方法完成。

ServerSocket

可以使用 ServerSocket 来实现 Java 服务器,这样就可以通过 TCP / IP 侦听来自客户端的传入连接。如果更喜欢使用 Java NIO 而不是 Java Networking(标准API),那么也可以使用 ServerSocketChannel 。

创建一个 ServerSocket

这是一个简单的代码示例,它创建一个侦听端口 9000 的 ServerSocket:

ServerSocket serverSocket = new ServerSocket(9000);
监听传入的连接

要接受传入连接,必须调用 ServerSocket.accept() 方法。 accept() 方法返回一个 Socket ,其行为类似于普通的 Socket ,示例:

ServerSocket serverSocket = new ServerSocket(9000);
boolean isStopped = false;
while(!isStopped){
Socket clientSocket = serverSocket.accept();
//do something with clientSocket
}

每次调用 accept() 方法时只打开一个传入连接。

此外,只有在运行服务器的线程调用 accept() 时才能接受传入连接。 线程在此方法之外执行的所有时间都没有客户端可以连接。 因此,“accept”线程通常将传入连接(Socket)传递给工作线程池,然后工作线程与客户端进行通信。 有关多线程服务器设计的更多信息,请参阅教程跟踪 Java 多线程服务器。

关闭客户端 Sockets

一旦客户端请求完成,并且不会从该客户端收到进一步的请求,必须关闭该Socket,就像关闭普通客户端Socket一样。调用:socket.close();

关闭服务端 Sockets

一旦服务器关闭,就需要关闭 ServerSocket 。 调用:serverSocket.close();

UDP DatagramSocket(UDP数据报套接字)

DatagramSocket 是 Java 通过 UDP 而不是 TCP 进行网络通信的机制。 UDP 也是 IP 协议的上层。 可以使用 DatagramSocket 来发送和接收 UPD 数据报。

UDP 对比 TCP

通过 TCP 发送数据时,首先要创建连接。 建立 TCP 连接后,TCP 保证数据到达另一端,或者它会告诉你发生了错误。

使用 UDP,只需将数据包(数据报)发送到网络上的某个 IP 地址。 无法保证数据会到达,也无法保证 UDP 数据包到达的顺序。 这意味着 UDP 比 TCP 具有更少的协议开销(没有流完整性检查)。

UDP 适用于数据传输,如果数据包在转换过程中丢失则无关紧要。 例如,想象一下通过互联网传输直播电视信号,如果一两帧丢失,这是无关紧要的。我们更不希望直播延迟只是为了确保所有帧都显示出来。 宁愿跳过错过的帧,并直接查看最新的帧。

还有实时监控视频,宁愿丢失一两帧,也不想延迟于现实 30 秒。与摄像机录像的存储有点不同,将图像从相机录制到磁盘时, 为了保证完整性,可能不希望丢失单帧,而是更愿意稍微延迟。

DatagramPacket 类

此类表示数据报包。数据报包用来实现无连接包投递服务。

Java 使用 DatagramSocket 代表 UDP 协议的 Socket ,DatagramSocket 本身只是码头,不维护状态,不能产生IO流,它的唯一作用就是接收和发送数据报,使用 DatagramPacket 来代表数据报,DatagramSocket 接收和发送的数据都是通过 DatagramPacket 对象完成的。

每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。

引用自 李刚《疯狂Java讲义(第2版)》

其所有构造器如下:

方法 描述
DatagramPacket(byte[] buf, int length) 构造 DatagramPacket,用来接收长度为 length 的数据包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
DatagramPacket(byte[] buf, int length, SocketAddress address) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
以上3个在 byte[] buf 参数后面追加 int offset 为长度为 length 的包设置偏移量为 offset

其中

  • InetAddress 类表示互联网协议 (IP) 地址,可以通过静态方法 getByName(String host) 获得其对象。
  • SocketAddress 类里面什么都没有。其子类 InetSocketAddress是(IP地址+端口号)类型,也就是端口地址类型,同样可以使用静态方法 createUnresolved(String host, int port) 获取对象,另外也能由构造函数 InetSocketAddress(InetAddress addr, int port) 创建,其中 InetAddress 对象可省略,也可用字符串代替。
通过 DatagramSocket 发送数据(DatagramPacket )

要通过 DatagramSocket 发送数据,必须首先创建一个 DatagramPacket :

byte[] buffer = new byte[65508];
InetAddress address = InetAddress.getByName("baidu.com"); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, 9000);

字节缓冲区(字节数组)是要在 UDP 数据报中发送的数据。 上述缓冲区的长度(65508字节)是可以在单个 UDP 数据包中发送的最大数据量。

DatagramPacket 构造函数中的 buffer.length 是要发送的缓冲区中数据的长度,忽略该数据量之后缓冲区中的所有数据。

InetAddress 实例包含发送 UDP 数据包的节点(例如服务器)的地址。 InetAddress 类表示 IP 地址(Internet地址)。 getByName() 方法返回一个 InetAddress 实例,其 IP 地址与给定的主机名匹配。

port 参数是服务器接收数据正在侦听的 UDP 端口,UDP 和 TCP 端口是不一样的。同一台计算机可以有不同的线程同时监听 UDP 的 80 端口和 TCP 中的 80 端口。不同协议下,端口号互不干扰,端口只是应用程序的标识。

创建一个 DatagramSocket :

DatagramSocket datagramSocket = new DatagramSocket();

要发送数据,请调用 send() 方法,如下所示:

datagramSocket.send(packet);

这是一个完整的例子:


public class DatagramExample {
public static void main(String[] args) throws Exception {
DatagramSocket datagramSocket = new DatagramSocket(); byte[] buffer = "123456789".getBytes();
InetAddress receiverAddress = InetAddress.getLocalHost(); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, receiverAddress, 80);
datagramSocket.send(packet);
}
}
通过 DatagramSocket 接收数据 (DatagramPacket )

通过 DatagramSocket 接收数据是通过首先创建 DatagramPacket 然后通过 DatagramSocket 的 receive() 方法接收数据来完成的。 这是一个例子:

DatagramSocket socket = new DatagramSocket(80);
byte[] buffer = new byte[10];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet);

使用传递给构造函数的参数值 80 来实例化 DatagramSocket , 此参数是 DatagramSocket 接收 UDP 数据包的 UDP 端口。 如前所述,TCP 和 UDP 端口不相同,因此不重叠。 可以在 TCP 和 UDP 80 端口上侦听两个不同的进程,而不会发生任何冲突。

其次,创建字节缓冲区和 DatagramPacket 。 注意 DatagramPacket 没有关于要发送数据的节点的信息,就像创建 DatagramPacket 用于发送数据时一样。 这是因为我们将使用 DatagramPacket 接收数据而不是发送数据,因此,不需要目标地址。

最后调用 DatagramSocket 的 receive() 方法。 此方法将一直阻塞,直到收到 DatagramPacket 。

收到的数据位于 DatagramPacket 的字节缓冲区中。 这个缓冲区可以通过调用如下代码获取:

byte[] buffer = packet.getData();

缓冲区会接收多少数据应该由你找到答案。 正在使用的协议应指定每个 UDP 数据包发送的数据量,或指定可以查找到的数据结束标记。真正的服务器程序可能会在循环中调用 receive() 方法,并将所有收到的 DatagramPacket 传递给工作线程池,就像 TCP 服务器对传入连接一样。

URL + URLConnection

java.net 包中两个有趣的类:URL 类和 URLConnection 类,这些类可用于创建与 Web 服务器(HTTP 服务器)的客户端连接。 这是一个简单的代码示例:

public class URLExample {
public static void main(String[] args) throws IOException {
URL url = new URL("http://baidu.com"); URLConnection urlConnection = url.openConnection();
InputStream inputStream = urlConnection.getInputStream(); int data = inputStream.read();
while (data != -1) {
System.out.print((char) data);
data = inputStream.read();
}
inputStream.close();
}
}

将会输出

<html>
<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">
</html>
HTTP GET 和 POST

URLConnection 类的作用是构造一个到指定 URL 的 URL 连接。它只有一个构造函数:URLConnection(URL url)

默认情况下,URLConnection 向 Web 服务器发送 HTTP GET 请求,即查询数据。如果要发送 HTTP POST 请求提交数据,请调用URLConnection.setDoOutput(true) 方法,如下所示:

URL url = new URL("http://baidu.com");
URLConnection urlConnection = url.openConnection();
urlConnection.setDoOutput(true);

一旦设置了 setDoOutput(true) ,因为要提交数据,所以需要输出流。可以打开 URLConnection 的 OutputStream ,如下所示:

OutputStream output = urlConnection.getOutputStream();

使用此 OutputStream ,可以在 HTTP 请求的正文中编写所需的任何数据。 请记住对其进行 URL 编码(参考 【基础进阶】URL详解与URL编码 ,并记得在完成向其写入数据后关闭 OutputStream 。

本地文件的URL

URL 类还可用于访问本地文件系统中的文件。 因此,如果需要代码处理来源不明的文件,比如是来自网络还是本地文件系统,则 URL 类是打开文件的便捷方式。

以下是使用 URL 类在本地文件系统中打开文件的示例:

URL url = new URL("file:/D:/test/test.txt");

URLConnection urlConnection = url.openConnection();
InputStream input = urlConnection.getInputStream(); int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
input.close();

请注意,这和通过 HTTP 访问 Web 服务器上的文件的唯一区别是 URL :"file:/D:/test/test.txt""http://baidu.com"

JarURLConnection

JarURLConnection 类用于连接 Java Jar 文件。 连接后可以获取有关 Jar 文件内容的信息。 这是一个简单的例子:

String urlString = "http://butterfly.jenkov.com/"
+ "container/download/"
+ "jenkov-butterfly-container-2.9.9-beta.jar"; URL jarUrl = new URL(urlString);
JarURLConnection connection = new JarURLConnection(jarUrl); Manifest manifest = connection.getManifest();
JarFile jarFile = connection.getJarFile();
//do something with Jar file...

初步接触 Java Net 网络编程的更多相关文章

  1. 二十三、Java基础--------网络编程

    Java中另一个重要技术就是网络编程了,为了更好的学习web方向的知识,有必要对java之网络编程好好学习,本文将围绕网络编程技术进行分析. 常见的网络协议:UDP.TCP UDP 1. 将数据源和目 ...

  2. JAVA的网络编程

    网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编程的大门而放弃了对于该部分技术的学习. 在 学习网络编程以前,很多初学者可能觉得网络编 ...

  3. Java Socket 网络编程心跳设计概念

    Java Socket 网络编程心跳设计概念   1.一般是用来判断对方(设备,进程或其它网元)是否正常动行,一 般采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经当掉.用于 ...

  4. 20145325张梓靖 实验五 "JAVA的网络编程"

    20145325张梓靖 实验五 "JAVA的网络编程" 实验内容 使用 JVAV语言 进行网络编程 对明文进行加密 设计过程 我完成的是客户端,服务端同伴 20145308刘昊阳 ...

  5. 【转】JAVA之网络编程

    转自:火之光 网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编程的大门而放弃了对于该部分技术的学习. 在 学习网络编程以前,很多初学者 ...

  6. JAVA的网络编程【转】

    JAVA的网络编程[转] Posted on 2009-12-03 18:04 火之光 阅读(93441) 评论(20) 编辑 收藏 网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能, ...

  7. Java面向对象 网络编程 下

    Java面向对象 网络编程  下 知识概要:                   (1)Tcp 练习 (2)客户端向服务端上传一个图片. (3) 请求登陆 (4)url 需求:上传图片. 客户端:   ...

  8. Java面向对象 网络编程 上

     Java面向对象 网络编程 上 知识概要:                     (1)网络模型 (2)网络通讯要素 (3)UDP TCP 概念 (4)Socket (5)UDP TCP 传输 ...

  9. Java基础——网络编程(二)

    一.套接字 Socket 网络驱动程序提供给应用程序编程的接口和一种机制,可以比喻成一个港口码头 应用程序只要把货放在这,就算完成了货物的运送.它在应用程序中创建,通过一种绑定机制与驱动程序建立关系, ...

随机推荐

  1. ssm框架插入mysql数据库中文乱码问题解决

    1.      检查web.xml <!-- 编码过滤器 --> <filter> <filter-name>encodingFilter</filter-n ...

  2. 关于iis里面 .net framework 版本的切换

    然而,即便这样还是不够的 在iis上浏览网站会报以下错误: 这是什么原因呢?这是因为应用程序池中没有使用asp.net v4.0 网站程序是用v4.0写的,所以我们要用4.0的程序池版本,当前应用程序 ...

  3. 学习vi和vim编辑(4):高速移动定位

    平时.第一步是编辑文本需要做将光标移动到需要编辑.因此,根据需要,将光标移动到目标数字键来编辑文本的速度在一定程度上. 一篇文章.主要介绍怎样高速移动光标. 依据屏幕来移动: 在一个有几千行文本的文件 ...

  4. 微信公众号开发系列-Http请求包基类

    HttpHelper请求包基类,支持get请求和POS要求.以促进微通道交互界面的开发,为了准备的人机交互界面,背部. 1.HttpHelper帮助基类 [csharp] view plaincopy ...

  5. XF 绝对布局

    using System; using Xamarin.Forms; using Xamarin.Forms.Xaml; [assembly: XamlCompilation (XamlCompila ...

  6. 反编译 war 包成传统项目的方法

    需求 项目老大让外包做了官网,不甚满意,想自己搞搞,遂叫我反编译他们发过来的 war 包. 方法 第一步:解压 war 包其实就是 zip 压缩包,用 zip 解压. 第二步:反编译 查看 war 包 ...

  7. JAVASCRIPT高程笔记-------第六章 面向对象的程序设计

    理解对象的概念  js中的对象与其他 编程语言中的类不一样  ECMAscript 没有类的概念      ECMA-262 把对象定义为 “无序属性的集合,其属性可以包含基本值,对象或者函数”   ...

  8. Sql 执行查询顺序

  9. DevExpress XtraTreeList TreeList复选框选择

    权限管理涉及复选框多勾选. 1.控件属性设置 TreeList.OperationView.ShowCheckBoxes=true;用于显示CheckBox: TreeList.OperationBe ...

  10. 如何在 Linux 中添加一块大于 2TB 的新磁盘?

    你有没有试过使用 fdisk 对大于 2TB 的硬盘进行分区,并且纳闷为什么会得到需要使用 GPT 的警告? 是的,你看到的没错.我们无法使用 fdisk 对大于 2TB 的硬盘进行分区. 在这种情况 ...