Java与TCP
类别: JAVA教程
传输控制协议是一种基于流的网络通讯方法,它与其它的任何协议都有很大的不同。本文讨论TCP流以及在Java中怎样操作它。
一、概述
TCP提供的网络通讯接口与用户数据报协议(UDP)截然不同。TCP的特性使网络编程很具魅力,而且它删除了UDP的很多干扰部分(例如数据包的排序和丢失),简化了网络通讯。UDP关心的是数据包的传输,而TCP关注的是建立网络连接,并在网络连接中发送和接收字节流。
数据包可以通过网络用多种方法发送,并且它们到达的时间可能不同。这有利于性能的提高和程序的健壮性,因为单个包的丢失不一定干扰其它包的传输。但是,这样的系统使程序员必须作更多的工作,他们必须保证数据的送达(delivery)。TCP通过对发送和次序的保证消除了这些额外的工作,为客户端和支持两路(two-way)通讯的服务器之间提供了可靠的字节通讯流。它在两台计算机之间建立了"虚拟连接",可以通过虚拟连接发送数据流。
图1:TCP建立虚拟连接传输数据
TCP使用更低层的(lower-level)的IP通讯协议在两台计算机之间建立连接。这种连接提供了一个允许字节流发送和接收的接口,并且采用透明的方式把数据转换为IP数据报。数据报(datagram)的问题之一是不能保证数据包到达目的地。TCP解决了这个问题,它提供了有保证的数据字节的送达。当然,网络错误影响了送达也是可能的,但是TCP通过类似重新发送数据包解决了这种实现的问题,并且只在情况很严重(例如没有到网络主机的路由或连接丢失了)的时候才提醒程序员。
四、Socket类
Socket类表现了客户端套接字,它是属于一台或两台计算机的两个TCP通讯端口之间的通讯通道。端口可以连接到本地系统的另一个端口,这样可以避免使用另一台计算机,但是大多数网络软件将使用两台计算机。但是TCP套接字不能与两台以上的计算机通讯。如果需要这种功能,客户端应用程序必须建立多个套接字连接,每台计算机一个套接字。
构造函数
java.net.Socket类有几个构造函数。其中两个构造函数允许使用布尔型参数指定是否使用UDP或TCP套接字,我们不赞成使用它们。这儿没有使用这两个构造函数,并且没有列举在此处--如果需要UDP功能,请使用DatagramSocket。
try
{
// 连接到指定的主机和端口
Socket mySocket = new Socket ( "www.awl.com", 80);
// ......
}
catch (Exception e)
{
System.err.println ("Err - " + e);
}
但是还有很多构造函数可以用于不同的情形。除非特别指出,所有的构造函数都是公共的。
・ protected Socket ()-使用当前套接字产生组件提供的默认实现建立不连接的套接字。开发者一般不应该使用这个方法,因为它不允许指定主机名称和端口。
・ Socket (InetAddress address, int port)产生 java.io.IOException异常。
・ java.lang.SecurityException-建立连接到指定的IP地址和端口的套接字。如果不能建立连接,或连接到主机违反了安全性约束条件(例如某个小的服务程序试图连接到某台计算机而不是载入它的计算机时),就产生这种异常。
・ Socket (InetAddress address, int port, InetAddress localAddress, int localPort)产生java.io.IOException、java.lang.SecurityException异常-建立连接到指定的地址和端口的套接字,并把它绑定到特定的本地地址和本地端口。默认情况下,使用一个自由(空)的端口,但是在多地址主机环境(例如本地主机有两个或多个的计算机)中,该方法也允许你指定一个特定的端口号、地址。
・ protected Socket (SocketImpl implementation)--使用特定的套接字的实现(implementation)建立未连接的套接字。通常情况下开发者不应该使用这个方法,因为它允许指定主机名称和端口。
・ Socket (String host, int port)产生java.net.UnknownHostException、java.io.IOException、java.lang.SecurityException异常--建立连接到特定主机和端口的套接字。这个方法允许指定一个字符串而不是一个InetAddress。如果指定的主机名称不能够解析,就不能建立连接,如果违反了安全性约束条件就产生异常。
・ Socket (String host, int port, InetAddress localAddress, int localPort)产生java.net.UnknownHostException、java.io.IOException、java.lang.SecurityException异常--建立连接到特定主机和端口的套接字,并绑定到特定的本地端口和地址。它允许指定字符串形式的主机名称,而不是指定InetAddress实例,同时它允许指定一个将绑定的本地地址和端口。这些本地参数对于多地址主机(如果可以通过两个或更多IP地址访问的计算机)是有用的。如果主机名称不能解析,就不能建立连接,如果违反了安全性约束条件会产生异常。
五、建立TCP客户端
讨论了套接字类的功能后,我们将分析一个完整的TCP客户端程序。此处我们将看到的客户端程序是一个daytime客户端,它连接到一个daytime服务器程序以读取当前的日期和时间。建立套接字连接并读取信息是一个相当简单的过程,只需要少量的代码。默认情况下daytime服务运行在13端口上。并非每台计算机都运行了daytime服务器程序,但是Unix服务器是客户端运行的很好的系统。
DaytimeClient的代码
import java.net.*
import java.io.*;
public class DaytimeClient
{
public static final int SERVICE_PORT = 13;
public static void main(String args[])
{
// 检查主机名称参数
if (args.length != 1)
{
System.out.println ("Syntax - DaytimeClient host");
return;
}
// 获取服务器程序的主机名称
String hostname = args[0];
try
{
// 获取一个连接到daytime服务的套接字
Socket daytime = new Socket (hostname,
SERVICE_PORT);
System.out.println ("Connection established");
// 在服务器程序停止的情况下设置套接字选项
daytime.setSoTimeout ( 2000 );
// 从服务器程序读取信息
BufferedReader reader = new BufferedReader (
new InputStreamReader
(daytime.getInputStream()
));
System.out.println ("Results : " +
reader.readLine());
// 关闭连接
daytime.close();
}
catch (IOException ioe)
{
System.err.println ("Error " + ioe);
}
}
}
DaytimeClient是如何工作的
Daytime应用程序是很容易理解的,它使用了文章前面谈到的概念。建立套接字、获取输入流,在很少的事件中(在连接时像daytime一样简单的服务器程序失败)激活超时设置。不是连接已筛选过的流,而是把有缓冲的读取程序连接到套接字输入流,并且把结果显示给用户。最后,在关闭套接字连接后客户端终止。这是你可能得到的最简单的套接字应用程序了--复杂性来自实现的网络协议,而不是来自具体网络的编程。
运行DaytimeClient
运行上面的应用程序很简单。简单地把运行daytime服务的计算机的主机名称作为命令行参数指定并运行它就可以了。如果daytime服务器程序使用了非标准的端口号,记得需要改变端口号并重新编译。
例如,如果服务器程序在本机上,为了运行客户端将使用下面的命令:
java DaytimeClient localhost
注意
Daytime服务器程序必须正在运行中,否则该客户端程序将不能建立连接。例如如果你正在使用Wintel系统而不是Unix,那么你需要运行DaytimeServer。
一、概述
TCP提供的网络通讯接口与用户数据报协议(UDP)截然不同。TCP的特性使网络编程很具魅力,而且它删除了UDP的很多干扰部分(例如数据包的排序和丢失),简化了网络通讯。UDP关心的是数据包的传输,而TCP关注的是建立网络连接,并在网络连接中发送和接收字节流。
数据包可以通过网络用多种方法发送,并且它们到达的时间可能不同。这有利于性能的提高和程序的健壮性,因为单个包的丢失不一定干扰其它包的传输。但是,这样的系统使程序员必须作更多的工作,他们必须保证数据的送达(delivery)。TCP通过对发送和次序的保证消除了这些额外的工作,为客户端和支持两路(two-way)通讯的服务器之间提供了可靠的字节通讯流。它在两台计算机之间建立了"虚拟连接",可以通过虚拟连接发送数据流。
图1:TCP建立虚拟连接传输数据
TCP使用更低层的(lower-level)的IP通讯协议在两台计算机之间建立连接。这种连接提供了一个允许字节流发送和接收的接口,并且采用透明的方式把数据转换为IP数据报。数据报(datagram)的问题之一是不能保证数据包到达目的地。TCP解决了这个问题,它提供了有保证的数据字节的送达。当然,网络错误影响了送达也是可能的,但是TCP通过类似重新发送数据包解决了这种实现的问题,并且只在情况很严重(例如没有到网络主机的路由或连接丢失了)的时候才提醒程序员。
四、Socket类
Socket类表现了客户端套接字,它是属于一台或两台计算机的两个TCP通讯端口之间的通讯通道。端口可以连接到本地系统的另一个端口,这样可以避免使用另一台计算机,但是大多数网络软件将使用两台计算机。但是TCP套接字不能与两台以上的计算机通讯。如果需要这种功能,客户端应用程序必须建立多个套接字连接,每台计算机一个套接字。
构造函数
java.net.Socket类有几个构造函数。其中两个构造函数允许使用布尔型参数指定是否使用UDP或TCP套接字,我们不赞成使用它们。这儿没有使用这两个构造函数,并且没有列举在此处--如果需要UDP功能,请使用DatagramSocket。
try
{
// 连接到指定的主机和端口
Socket mySocket = new Socket ( "www.awl.com", 80);
// ......
}
catch (Exception e)
{
System.err.println ("Err - " + e);
}
但是还有很多构造函数可以用于不同的情形。除非特别指出,所有的构造函数都是公共的。
・ protected Socket ()-使用当前套接字产生组件提供的默认实现建立不连接的套接字。开发者一般不应该使用这个方法,因为它不允许指定主机名称和端口。
・ Socket (InetAddress address, int port)产生 java.io.IOException异常。
・ java.lang.SecurityException-建立连接到指定的IP地址和端口的套接字。如果不能建立连接,或连接到主机违反了安全性约束条件(例如某个小的服务程序试图连接到某台计算机而不是载入它的计算机时),就产生这种异常。
・ Socket (InetAddress address, int port, InetAddress localAddress, int localPort)产生java.io.IOException、java.lang.SecurityException异常-建立连接到指定的地址和端口的套接字,并把它绑定到特定的本地地址和本地端口。默认情况下,使用一个自由(空)的端口,但是在多地址主机环境(例如本地主机有两个或多个的计算机)中,该方法也允许你指定一个特定的端口号、地址。
・ protected Socket (SocketImpl implementation)--使用特定的套接字的实现(implementation)建立未连接的套接字。通常情况下开发者不应该使用这个方法,因为它允许指定主机名称和端口。
・ Socket (String host, int port)产生java.net.UnknownHostException、java.io.IOException、java.lang.SecurityException异常--建立连接到特定主机和端口的套接字。这个方法允许指定一个字符串而不是一个InetAddress。如果指定的主机名称不能够解析,就不能建立连接,如果违反了安全性约束条件就产生异常。
・ Socket (String host, int port, InetAddress localAddress, int localPort)产生java.net.UnknownHostException、java.io.IOException、java.lang.SecurityException异常--建立连接到特定主机和端口的套接字,并绑定到特定的本地端口和地址。它允许指定字符串形式的主机名称,而不是指定InetAddress实例,同时它允许指定一个将绑定的本地地址和端口。这些本地参数对于多地址主机(如果可以通过两个或更多IP地址访问的计算机)是有用的。如果主机名称不能解析,就不能建立连接,如果违反了安全性约束条件会产生异常。
五、建立TCP客户端
讨论了套接字类的功能后,我们将分析一个完整的TCP客户端程序。此处我们将看到的客户端程序是一个daytime客户端,它连接到一个daytime服务器程序以读取当前的日期和时间。建立套接字连接并读取信息是一个相当简单的过程,只需要少量的代码。默认情况下daytime服务运行在13端口上。并非每台计算机都运行了daytime服务器程序,但是Unix服务器是客户端运行的很好的系统。
DaytimeClient的代码
import java.net.*
import java.io.*;
public class DaytimeClient
{
public static final int SERVICE_PORT = 13;
public static void main(String args[])
{
// 检查主机名称参数
if (args.length != 1)
{
System.out.println ("Syntax - DaytimeClient host");
return;
}
// 获取服务器程序的主机名称
String hostname = args[0];
try
{
// 获取一个连接到daytime服务的套接字
Socket daytime = new Socket (hostname,
SERVICE_PORT);
System.out.println ("Connection established");
// 在服务器程序停止的情况下设置套接字选项
daytime.setSoTimeout ( 2000 );
// 从服务器程序读取信息
BufferedReader reader = new BufferedReader (
new InputStreamReader
(daytime.getInputStream()
));
System.out.println ("Results : " +
reader.readLine());
// 关闭连接
daytime.close();
}
catch (IOException ioe)
{
System.err.println ("Error " + ioe);
}
}
}
DaytimeClient是如何工作的
Daytime应用程序是很容易理解的,它使用了文章前面谈到的概念。建立套接字、获取输入流,在很少的事件中(在连接时像daytime一样简单的服务器程序失败)激活超时设置。不是连接已筛选过的流,而是把有缓冲的读取程序连接到套接字输入流,并且把结果显示给用户。最后,在关闭套接字连接后客户端终止。这是你可能得到的最简单的套接字应用程序了--复杂性来自实现的网络协议,而不是来自具体网络的编程。
运行DaytimeClient
运行上面的应用程序很简单。简单地把运行daytime服务的计算机的主机名称作为命令行参数指定并运行它就可以了。如果daytime服务器程序使用了非标准的端口号,记得需要改变端口号并重新编译。
例如,如果服务器程序在本机上,为了运行客户端将使用下面的命令:
java DaytimeClient localhost
注意
Daytime服务器程序必须正在运行中,否则该客户端程序将不能建立连接。例如如果你正在使用Wintel系统而不是Unix,那么你需要运行DaytimeServer。
- 上一篇: 两步学会Java Socket编程
- 下一篇: java硬件
-= 资 源 教 程 =-
文 章 搜 索