在客户/服务器通信模式中,服务器端需要创建监听特定端口的ServerSocket,ServerSocket负责接收客户连接请求。首先介绍ServerSocket类的各个构造方法,以及成员方法的用法,接着介绍服务器如何用多线程来处理与多个客户的通信任务。
ServerSocket的构造方法有以下几种重载形式:
◆ServerSocket()throws IOException
◆ServerSocket(int port) throws IOException
◆ServerSocket(int port, int backlog) throws IOException
◆ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException
在以上构造方法中,参数port指定服务器要绑定的端口(服务器要监听的端口),参数backlog指定客户连接请求队列的长度,参数bindAddr指定服务器要绑定的IP地址。
除了第一个不带参数的构造方法以外,其他构造方法都会使服务器与特定端口绑定,该端口由参数port指定。例如,以下代码创建了一个与9999端口绑定的服务器:
ServerSocket serverSocket=new ServerSocket(9999);
如果运行时无法绑定到80端口,以上代码会抛出IOException,更确切地说,是抛出BindException,它是IOException的子类。BindException一般是由以下原因造成的:
◆端口已经被其他服务器进程占用;
◆在某些操作系统中,如果没有以超级用户的身份来运行服务器程序,那么操作系统不允许服务器绑定到1~1023之间的端口。0~1023之间的端口是用于绑定Binding的一些服务的端口。
如果把参数port设为0,表示由操作系统来为服务器分配一个任意可用的端口。由操作系统分配的端口也称为匿名端口。对于多数服务器,会使用明确的端口,而不会使用匿名端口,因为客户程序需要事先知道服务器的端口,才能方便地访问服务器。
聊天服务器小Demo
/**
* ServerSocket简单聊天服务器
* @author 钟宇峰
*/
public class ServerDemo {
private Socket socket;
private ServerSocket server;
//主函数
public static void main(String[] args) {
ServerDemo sd = new ServerDemo();
sd.createServer(9999);
}
/**
* 创建服务器
* @param port 端口号
*/
public void createServer(int port){
try {
//创建服务器
server = new ServerSocket(port);
System.out.println("服务器创建成功....");
System.out.println("服务器处于阻塞状态中,等待客户机连接.....");
//服务器阻塞中等待客户机连接
whlie(true){
socket = server.accept();
System.out.println("客户机"+(i-1)+"连接成功!"+socket.getRemoteSocketAddress());
ClientDemo cd = new ClientDemo(server,socket);
cd.start();
}
//客户机已连接
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
-----------------------------------------------------------------------------
public class ClientDemo extends Thread {
private ServerSocket server;
private Socket socket;
public ClientDemo(ServerSocket server, Socket socket) {
this.socket = socket;
this.server = server;
}
public void run() {
while (true) {
try {
OutputStream os = socket.getOutputStream();
// BufferedReader br = new BufferedReader(new
// InputStreamReader(is));
InputStream is = socket.getInputStream();
String msg = "欢迎登陆聊天室!\r\n";
os.write(msg.getBytes());
msg = "";
while (!msg.equals("exit")) {
int i = 0;
StringBuffer strb = new StringBuffer();
while ((i = is.read()) != 13) {//
// 读取客户机发送过来的消息,只要不是13就继续读取,如果是13则结束
strb.append((char) i);
}
is.read();// 读取换行符,丢掉
msg = strb.toString();
// msg = br.readLine();
System.out.println("客户机说:" + msg);
os.write(("服务器说:" + msg + "\r\n").getBytes());
}
os.close();
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
在服务器创建中我们使用while(true){}循环确保服务器不会停止,一直处于阻塞状态中等待连接进来的客户机。ClientDemo类继承了线程,在run方法中创建客户机对象,初始化IO流。这样我们就可以生成多个客户端从而实现聊天功能。在这个基础上可以逐步加深,实现群聊功能,私聊,写自己设计的界面,制定通信协议以实现诸多功能。