在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连线,如果某个连接埠有开启,就会建立连线,此时显示该连接埠开启的讯息:
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键,然后程序会将您的指令传送出去,并显示远端主机的回应讯息,为了同时处理远端主机的回应与本机使用者的输入,程序使用多线程:
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()); } } }
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(); }
} }
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.
|
|