网络通信
Java与Internet
InetAddress与URL
Socket通信
数据报通信
Java与Internet——网络基本概念
TCP/IP网络参考模型包括五个层次:应用层、传输层、网络层、链路层、物理层。
大多数基于Internet的应用程序被看作TCP/IP网络的最上层, 如:ftp,http,smtp,pop3,telnet等。
传输层:在TCP/IP网络中,不同的机器之间进行通信时,数据的传输是由传输层控制的,这包括数据要发往的目标机器及应用程序、数据的质量控制等。 TCP/IP网络中最常用的传输协议就是TCP(Transport Control Protocol)和UDP(User Datagram Protocol)。
网络层:对TCP/IP网络中的硬件资源进行标识。连接到网络中的每台计算机(或其他设备)都有唯一的地址,这就是IP地址。IP地址实质上是一个32位的整数,通常以“d. d. d. d”的形式表示,每个d是一个8位整数。
传输层中两类传输协议
- TCP (Transport Control Protocol )
面向连接的能够提供可靠的流式数据传输的协议。类似于打电话的过程。
URL, Socket, ServerSocket等类都使用TCP协议进行网络通讯。
- UDP (User Datagram Protocol )
非面向连接的提供不可靠的数据包式的数据传输的协议。类似于从邮局发送信件的过程。
DatagramPacket, DatagramSocket等类使用UDP协议进行网络通讯。
注:当对所传输的数据具有时序性和可靠性等要求时,应使用TCP协议;当传输的数据比较简单、对时序等无要求时,UDP协议能发挥更好的作用。
一台机器只通过一条链路连接到网络上,但一台机器中往往有很多应用程序需要进行网络通信,如何区分呢?这就要靠网络端口号(port)了。
端口号是一个标记机器的逻辑通信信道的正整数,端口号不是物理实体。IP地址和端口号组成了所谓的Socket,Socket是网络上运行的程序之间双向通信链路的最后终结点。
IP与端口号组合而得出的Socket,可以完全分辨Internet上运行的程序。
Java与Internet ——端口号
端口号是用一个16位的整数来表达的,其范围为0~65535,其中0~1023为系统所保留,专门给那些通用的服务(well-known services),如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口为23,…因此,当我们编写通信程序时,应选择一个大于1023的数作为端口号,以免发生冲突。
Java与Internet ——Java中的网络支持
针对网络通信的不同层次,Java提供的网络功能有四大类:InetAddress 、URL、Socket、Datagram。
InetAddress面向的是IP层,用于标识网络上的硬件资源。
URL面向的应用层,通过URL,Java程序可以直接送出或读入网络上的数据。
Socket和Datagram面向的则是传输层。Sockets使用的是TCP协议,这是传统网络程序最常用的方式,可以想象为两个不同的程序通过网络的通信信道进行通信。Datagram则使用UDP协议,是另一种网络传输方式,它把数据的目的地纪录在数据包中,然后直接放在网络上。
使用InetAddress
类InetAddress可以用于标识网络上的硬件资源,它提供了一系列方法以描述、获取及使用网络资源。
InetAddress类实例通常是用它提供的静态方法来获取:
public static InetAddress getByName(String host)
host可以是一个机器名,也可以是一个形如“%d.%d.%d.%d”的IP地址或一个DNS 域名。
public static InetAddress getLocalHost()
这两个方法通常会产生UnknownHostException例外,应在程序中捕获处理。
以下是InetAddress类的几个主要方法:
public String getHostAddress():获得本对象的IP地址“%d.%d.%d.%d”。
public String getHostName():获得本对象的机器名。
测试InetAddress类
import java.net.InetAddress;
public class InetAddressDemo {
public static void main(String[] args) throws Exception { // 所有异常抛出
InetAddress locAdd = null; // 声明InetAddress对象
InetAddress remAdd = null; // 声明InetAddress对象
locAdd = InetAddress.getLocalHost(); // 得到本地InetAddress对象
remAdd = InetAddress.getByName("www.google.com"); // 取得远程InetAddress
System.out.println("本机IP地址:"
+ locAdd.getHostAddress());// 得到本地IP地址
System.out.println(“google的IP地址:"
+ remAdd.getHostAddress());// 得到远程IP地址
}
}
Socket通信
在Java中,基于TCP协议实现网络通信的类有两个:在客户端的Socket类和在服务器端的ServerSocket类。
在服务器端通过指定一个用来等待的连接的端口号创建一个 ServerSocket实例。
在客户端通过规定一个主机和端口号创建一个 socket实例,连到服务器上。
ServerSocket类的accept方法使服务器处于阻塞状态,等待用户请求。
类Socket
构造方法:
public Socket(String host, int port)
Socket的输入/输出流管理
public InputStream getInputStream()
public OutputStream getOutputStream()
关闭Socket
public void close() throws IOException
注:上述方法都将抛出例外IOException,程序中需要捕获处理。
类ServerSocket
构造方法:
public ServerSocket(int port)
主要方法
public Socket accept():等待客户端的连接
public void close():关闭Socket
注:这些方法都将抛出例外IOException,程序中需要捕获处理。
无论一个Socket通信程序的功能多么齐全、程序多么复杂,其基本结构都是一样的,都包括以下四个基本步骤:
1、在客户方和服务器方创建Socket/ServerSocket。
2、打开连接到Socket的输入/输出流。
3、利用输入/输出流,按照一定的协议对Socket进行读/写操作。
4、关闭输入/输出流和Socket。
通常,我们的主要工作是针对所要完成的功能在第3步进行编程,第1、2、4步对所有的通信程序来说几乎都是一样的。

Socket通信——支持多Client
解决方案一:
在一台计算机上一次启动多个服务器程序,只要端口号不同。
myserver1 <-------->myclient1
myserver2<-------->myclient2
解决方案二:
将服务器写成多线程的,不同的处理线程为不同的客户服务。主线程只负责循环等待,处理线程负责网络连接,接收客户输入的信息。
//主线程
while (true)
{
accept a connection ;
create a thread to deal with the client ;
}
serverSocket = new ServerSocket(4444);
while (listening)
{
Socket socket;
socket = serverSocket.accept(); //程序将在此等候客户端的连接
new MultiTalkServerThread(socket).start();
}
serverSocket.close();
class MultiTalkServerThread extends Thread
{
public MultiTalkServerThread(Socket socket)
{
this.socket = socket;
}
public void run()
{
…
}
}
Computer_client.java Computer_server.java
数据报通信
UDP协议是无连接的协议,它以数据报作为数据传输的载体。数据报是一个在网络上发送的独立信息,它的到达、到达时间以及内容本身等都不能得到保证。数据报的大小是受限制的,每个数据报的大小限定在64KB以内。
UDP协议无需在发送方和接收方建立连接。数据报在网上可以以任何可能的路径传往目的地。
在Java中,下面两个类是基于UDP协议实现网络通信的:
§ 用于表达通信数据的数据报类DatagramPacket
§ 用于进行端到端通信的类DatagramSocket
DatagramPacket
No. | 方法 | 类型 | 描述 |
1 | public DatagramPacket(byte[] buf,int length) | 构造 | 实例化DatagramPacket对象时,指定接收数据长度 |
2 | public DatagramPacket(byte[] buf,int length,InetAddress address,int port) | 构造 | 实例化DatagramPacket对象时指定发送的数据、数据的长度、目标地址及端口 |
3 | public byte[] getData() | 普通 | 返回接收的数据 |
4 | public int getLength() | 普通 | 返回要发送或接收数据的长度 |
DatagramSocket
No. | 方法 | 类型 | 描述 |
1 | public DatagramSocket(int port) throws SocketException | 构造 | 创建DatagramPacket对象,并指定监听的端口 |
2 | public void send(DatagramPacket p) throws IOException | 普通 | 发送数据报 |
3 | public void receive(DatagramPacket p) throws IOException | 普通 | 接收数据报 |
UDP客户端 —— UDPClient
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPClient {
public static void main(String[] args) throws Exception {
DatagramSocket ds = null; // 声明DatagramSocket对象
byte[] buf = new byte[1024]; // 定义接收数据的字节数组
DatagramPacket dp = null; // 声明DatagramPacket对象
ds = new DatagramSocket(9000); // 此客户端在9000端口监听
dp = new DatagramPacket(buf, 1024); // 指定接收数据的长度为1024
System.out.println("等待接收数据。") ; // 输出信息
ds.receive(dp); // 接收数据
String str = new String(dp.getData(), 0, dp.getLength()) + " from "
+ dp.getAddress().getHostAddress()
+ " : " + dp.getPort(); // 接收数据
System.out.println(str); // 输出数据
ds.close(); // 关闭
}
}
编写UDP发送的服务器端程序 —— UDPServer
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPServer {
public static void main(String[] args) throws Exception {
DatagramSocket ds = null; // 声明DatagamSocket对象
DatagramPacket dp = null; // 声明DatagramPacket对象
ds = new DatagramSocket(3000); // 服务器端在3000端口监听
String str = "hello world "; // 准备好要发送的信息
// 实例化DatagramPacket对象,指定数据内容,数据长度,要发送的目标地址,发送端口
dp = new DatagramPacket(str.getBytes(), str.length(), InetAddress
.getByName("localhost"), 9000); // 此处向客户端所在的9000端口发送信息
System.out.println("发送信息。") ; // 信息输出
ds.send(dp); // 发送数据报
ds.close(); // 关闭
}
}