From Gossip@Openhome

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.