think in java第十五章關於網路程式設計的讀書筆記
對於java的網路程式設計書中討論的全部歸納起來有Socket的使用,DatagramSocket的使用,java與CGI的配合使用,java連線資料庫以及java中呼叫遠端方法。
首先是Socket的使用,對於服務端來說,應該宣告的是ServerSocket物件,然後使用Socket accept()方法進行對埠進行監聽。如果有客戶機建立一個Socket(伺服器IP, 伺服器埠)物件進行連線(建立一個Socket的時候就會自動進行連線)。accept方法就會返回一個Socket物件。此時伺服器與客戶機就能就能 通過Socket提供的getInputStream()和getOutputStream()返回的IO進行互動了。但是要注意的就是一旦 ServerSocket呼叫了accept()這個方法,那麼這個執行緒就會用於監聽埠,如果沒有客戶端連線進來這個執行緒就會一直停留在那個方法裡。所 以Socket最好還是和多執行緒進行結合使用。比如寫一個繼承了Thread的類裡面包含一個Socket物件。
之後是DatagramSocket的使用,這個和Socket的不同在於:DatagramSocket是UDP協議而Socket是TCP/IP協 議。UDP協議的可靠性比TCP/IP協議略差,但是UDP協議卻有很高的傳輸速度,使用用於即時性很強的程式(魔獸爭霸與暗黑都是建立UDP連線的)。 對於DatagramSocket的使用方法和Socket的有很大不同。具體來說DatagramSocket要很DatagramPacket類配合 使用,DatagramSocket不必像Socket那樣建立一個一一對應的連線,DatagramSocket在互動上常用的兩個方法是void receive(DatagramPacket p)和void send(DatagramPacket p)。對於DatagramSocket對像的建立方法有多種,其中new DatagramSocket(int port)是將其繫結到本地主機上的指定埠,而new DatagramSocket()則是將其繫結到本地主機上任何可用的埠。然後只需使用receive和send收發DatagramPacket對 象。DatagramPacket中包含了本地地址、埠、訊息以及目的地的地址和埠。所以收到一個DatagramPacket包以後,只須呼叫方法 getAddress(),getPort()就能獲得傳送機器的資訊,有了這些資訊就能對傳送者進行反饋。
具體可以參看書中給出的使用示例:
//: Dgram.java
// A utility class to convert back and forth
// Between Strings and DataGramPackets.
import java.net.*;
public class Dgram {
public static DatagramPacket toDatagram(String s, InetAddress destIA, int destPort) {
byte[] buf = new byte[s.length() + 1];
s.getBytes(0, s.length(), buf, 0);
return new DatagramPacket(buf, buf.length,
destIA, destPort);
}
public static String toString(DatagramPacket p){
return new String(p.getData(), 0, p.getLength());
}
}
//: ChatterServer.java
// A server that echoes datagrams
import java.net.*;
import java.io.*;
import java.util.*;
public class ChatterServer {
static final int INPORT = 1711;
private byte[] buf = new byte[1000];
private DatagramPacket dp =
new DatagramPacket(buf, buf.length);
// Can listen & send on the same socket:
private DatagramSocket socket;
public ChatterServer() {
try {
socket = new DatagramSocket(INPORT);
System.out.println("Server started");
while(true) {
// Block until a datagram appears:
socket.receive(dp);
String rcvd = Dgram.toString(dp) + ", from address: " + dp.getAddress() +
", port: " + dp.getPort();
System.out.println(rcvd);
String echoString = "Echoed: " + rcvd;
// Extract the address and port from the
// received datagram to find out where to
// send it back:
DatagramPacket echo = Dgram.toDatagram(echoString, dp.getAddress(), dp.getPort());
socket.send(echo);
}
} catch(SocketException e) {
System.err.println("Can't open socket");
System.exit(1);
} catch(IOException e) {
System.err.println("Communication error");
e.printStackTrace();
}
}
public static void main(String[] args) {
new ChatterServer();
}
}
//: ChatterClient.java
// Tests the ChatterServer by starting multiple
// clients, each of which sends datagrams.
import java.lang.Thread;
import java.net.*;
import java.io.*;
public class ChatterClient extends Thread {
// Can listen & send on the same socket:
private DatagramSocket s;
private InetAddress hostAddress;
private byte[] buf = new byte[1000];
private DatagramPacket dp = new DatagramPacket(buf, buf.length);
private int id;
public ChatterClient(int identifier) {
id = identifier;
try {
// Auto-assign port number:
s = new DatagramSocket();
hostAddress = InetAddress.getByName("localhost");
} catch(UnknownHostException e) {
System.err.println("Cannot find host");
System.exit(1);
} catch(SocketException e) {
System.err.println("Can't open socket");
e.printStackTrace();
System.exit(1);
}
System.out.println("ChatterClient starting");
}
public void run() {
try {
for(int i = 0; i < 25; i++) {
String outMessage = "Client #" + id + ", message #" + i;
// Make and send a datagram:
s.send(Dgram.toDatagram(outMessage,
hostAddress, ChatterServer.INPORT));
// Block until it echoes back:
s.receive(dp);
// Print out the echoed contents:
String rcvd = "Client #" + id + ", rcvd from " +
dp.getAddress() + ", " + dp.getPort() + ": " + Dgram.toString(dp);
System.out.println(rcvd);
}
} catch(IOException e) {
e.printStackTrace();
System.exit(1);
}
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++)
new ChatterClient(i).start();
}
}
對於java與其它語言通訊以及與CGI互動個人看的不是很懂,只是知道程式之間使用IO流互動。這兩節的內容暫時擱置,等看完書以後回來仔細研究。
之後是java連線資料庫,需要知道的一點是java連線其他的資料庫軟體提供的服務必須下載支援該資料庫軟體的JDBC(由資料庫軟體開發者提供)。各 種資料庫的連線方法都能在網上查閱得到,書中使用的是由sun本身提供的方法。sun本身提供了4種方法(本人在網上查閱得到,可能不正確)。書中使用的 是其中的“jdbc-odbc橋”。對於書中給出的程式碼閱讀的時候需要注意的就是作者給出的資料庫名稱是people,使用者DNS的標識也是 people,使用表的名稱也是people。所以閱讀的時候一定要弄清楚其中使用到people的地方到底是指哪一個,不然自己運用的時候會不知所措。
最後就是java中遠端方法的呼叫,直接照著書中給出的程式碼以及步驟作似乎會出現問題。其中使用rmic編譯 並沒有出現PerfectTime_Skel.class這個檔案。之後我在網上進行查閱。要使用遠端方法還需要在jdk目錄下的這個檔案 Java/jdk1.5.0_04/jre/lib/security/java.policy中加入如下程式碼:
grant {
permission java.net.SocketPermission "*:1024-65535",
"connect,accept";
permission java.net.SocketPermission "*:80","connect";
};
以開放埠connect訪問許可權。
當然也可以不向上面這樣開放1024-65535這些全部埠,只開放自己需要的即可。
除了這種直接更改java檔案的辦法以外的方法還有就是給需要使用該服務的類新增許可權檔案。
形式如:
PerfectTime.policy
grant codeBase
"file:/D:/java/javafile/新建資料夾/測試用/"
{
permission java.net.SocketPermission
"*:1000-9999","accept,connect,listen,resolve";
};
添加了許可權檔案之後還是不能執行的,因為RMI註冊伺服器無法找到stub的位置,解決的辦法是可以更改系統的classpath環境變數。另外網上有人給出更簡單的方法:
使用“java.exe -Djava.rmi.server.codebase=file:/D:/java/javafile/新建資料夾/測試用/ PerfectTime” 這個語句來執行PerfectTime.class檔案。
之後DisplayPerfectTime.class只需用java DisplayPerfectTime這種一般的語句執行即可。這樣就能看到程式正常執行了。
但是當PerfectTime執行了一次以後退出,再次執行就會出錯。我想大概是因為程式中指名呼叫2005埠的原因吧,因為第一次執行程式時就 把2005端口占用了,第二次就無法正常執行了。因為我把rmiregistry關閉以後再次開啟,程式又能正常執行,所以我做出此推測,不過具體對不對 還需要繼續深入研究。