对于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关闭以后再次打开,程序又能正常运行,所以我做出此推测,不过具体对不对 还需要继续深入研究。