大可制作:QQ群:31564239(asp|jsp|php|mysql)

Java Gossip: Socket 类

在TCP/IP 底层的运作必须处理封包、标头、格式、交握等的细节,这实在不是什么好差事,为此Berkeley UNIX提出Socket的概念,将网路连线简化为数据流(data stream)的概念,这个数据流在客户端与伺服端各有一个接口(port),而数据流就像是在一个连接两接口的缆线中传递,程序设计人员使用 Socket的概念来撰写网路连线程序,只要处理主机信息与连接埠,而不用关心底层的琐碎运作。

简而言之,就如同文件输出入一样,Socket将网路连线也视作一种输出入的动作,数据的传递就像是将数据写入与读入。

在Java中提供Socket类来支持Socket概念,这边介绍四个建构式:
public Socket(String host, int port)
public Socket(InetAddress host, int port)
public Socket(String host, int port, InetAddress interface, int localPort)
public Socket(InetAddress host, int port, InetAddress interface, int localPort)


除了第一个构造函数必须同时处理UnknownHostException(无法识别主机)与IOException(无法建立连线时)之外,其它的建构式只需处理IOException。

第一与第二个建构式让您指定远端主机与连接时所使用的连接埠,而本机的部份则交由程序自行决定,第三与第四个构造函数可以让您指定远端与本身的信息。

您可以直接指定主机名称来建立Socket对象,然而使用InetAddress会比较有效率,在真正进行Socket连线之前,如果在建立InetAddress对象时无法取得主机信息,则可以提前进行相关的处理。

下面这个程序可以让您扫描指定主机上所开启的连接埠(0~1023),这边指定本机为对象建立Socket连线,如果某个连接埠有开启,就会建立连线,此时显示该连接埠开启的讯息:
  • ScanPort.java
package onlyfun.caterpillar;

import java.io.*;
import java.net.*;

public class ScanPort {
public static void main(String[] args) {
try {
String hostname = "localhost";
InetAddress address = InetAddress.getByName(hostname);

for(int i = 0; i < 1024; i++) {
try {
Socket skt = new Socket(address, i);
// 连线表示有开启 Port
System.out.printf("%nPort: %d Opened..", i);
skt.close();
}
catch(IOException e) {
System.out.print(".");
// 无法建立连线,没有开启 Port
}
}
}
catch(UnknownHostException e) {
e.printStackTrace();
}
}
}

在建立了Socket对象之后,可以取得Socket对象的相关信息,例如:
public InetAddress getInetAddress()
public int getPort()
public int getLocalPort()
public inetAddress getLocalAddress()

以上的方法由上而下分别为取得Socket连接对象位址、连接对象连接埠、本机连接埠、本机位址。

如果要取过Socket对象接受或输出信息,可以使用getInputStream()与getOutputStream()两个方法,就如同文件I/O 一样,您只要将它当作串流数据来处理即可,至于网路上的信息是如何交换的,您并不用得知,Java会自动帮您完成相关的协定确认。

下面这个程序模拟Telnet程序,您可以用它来与远端主机进行“以行为主”的文字或指令沟通,也就是每下一行文字或指令就按Enter键,然后程序会将您的指令传送出去,并显示远端主机的回应讯息,为了同时处理远端主机的回应与本机使用者的输入,程序使用多线程:
  • SocketToStdout.java
package onlyfun.caterpillar;

import java.io.*;
import java.net.*;

public class SocketToStdout implements Runnable {
private Socket skt;

public SocketToStdout(Socket skt) {
this.skt = skt;
}

public void run() {
BufferedReader sktReader;
try {
sktReader = new BufferedReader(
new InputStreamReader(skt.getInputStream()));

String sktMessage = null;
while((sktMessage = sktReader.readLine()) != null) {
System.out.println(sktMessage);
}

skt.close();
}
catch(IOException e) {
System.out.println(e.toString());
}
}
}

  • StdInToSocket.java
package onlyfun.caterpillar;

import java.io.*;
import java.net.Socket;

public class StdInToSocket implements Runnable {
private Socket skt;

public StdInToSocket(Socket skt) {
this.skt = skt;
}

public void run() {
String userInput;
BufferedReader stdInReader;
PrintStream socketStream;

try {
stdInReader = new BufferedReader(
new InputStreamReader(System.in));
socketStream = new PrintStream(skt.getOutputStream());

while(true) {
if(skt.isClosed()) {
break;
}
userInput = stdInReader.readLine();
socketStream.println(userInput);
}
}
catch(IOException e) {
e.printStackTrace();
}

}
}

  • ConnectDemo.java
package onlyfun.caterpillar;

import java.io.*;
import java.net.*;

public class ConnectDemo {
public static void main(String[] args) {
String hostname = "localhost";
int port = 23;
InetAddress address;
BufferedReader buf;
String read;

if(args.length > 1) {
hostname = args[0];
port = Integer.parseInt(args[1]);
}

try {
address = InetAddress.getByName(hostname);
try {
Socket skt = new Socket(address, port);
Thread sktToStd = new Thread(new SocketToStdout(skt));
Thread stdToSkt = new Thread(new StdInToSocket(skt));

sktToStd.start();
stdToSkt.start();

} catch (IOException e) {
e.printStackTrace();
}
}
catch(UnknownHostException e) {
System.out.println(e.toString());
}

}
}

下面的执行结果是连接至FTP站台的测试,可以输入简单的ASCII指令跟FTP站台互动(使用FTP协定指令):

java onlyfun.caterpillar.ConnectDemo ftp.isu.edu.tw 21
220-欢迎光临义守大学文件服务器
220-
220-本站提供以下软体可供下载:
220-*******************************************************************************
220-/pub/BeOS/       BeOS 作业系统
220-/pub/CPAN/       Perl 程序语言 (Comprehensive Perl Archive Network)
220-/pub/CPatch/     中文化软体 (收集大量的 Windows 共享软体与中文化程序)
220-/pub/Documents/  各类文件收集
220-/pub/FreeBSD/    FreeBSD 作业系统
220-/pub/Game/       免费游戏软体
220-/pub/Hardware/   硬体驱动程序
220-/pub/Linux/      Linux 作业系统
220-/pub/MsDownload/ 微软相关软体更新 (例如 Service Pack 等)
220-/pub/RFC/        Request for Comments (RFC 文件)
220-/pub/Solaris/    Solaris 作业系统
220-/pub/Yesterday/  昨日小筑完整 mirror (收集大量 Windows 相关软体)
220-*******************************************************************************
220-
220-另外,欢迎使用者多多利用 HTTP 的方式登入,一来有较佳的
220-传输效能,接口功能也较为完善,您还可以利用文件搜寻引擎
220-快速找到您所需求的文件,网址如下:
220-
220-http://ftp.isu.edu.tw
220
QUIT
221 Goodbye.