/**
*Title:ChatServer
*Description:1、这里的改进方法是将接收端和数据端分开。从而避免了当一个客户端连上以后,因为已经用过accept(),
进入dis.readUTF();循环而不能接收其他的客户端的情况。
2、运用多线程的方法打开多个客户端。
*@Copyright:
*@Company:
*@autor:firefly
*@version:1.0
*@time:2012.10.3
*/
import java.io.*;
import java.net.*;
import java.util.*;
public class ChatServer {
boolean started = false;
ServerSocket ss = null;
List<Client> clients = new ArrayList<Client>();
public static void main(String[] args) {
new ChatServer().start();
}
public void start() { //start是里面的东西是动态的,不能写在的下静态的main里面,所以单独构造一个start方法,
//而在main里面也只不过是调用 ,这也更符合面向对象的特点。
try { //这里用trycatch单独对ServerSocket进行捕获,单独处理和下面的trycatch分开,会更好一点。
ss = new ServerSocket(8888); //基于TCP的服务,先把服务起来. 创建绑定到特定端口的服务器套接字.
started = true;
} catch (BindException e){ //绑定异常,它是runtime异常可以不捕获,如果能加上这个细节就更好了。
System.out.println("端口正在使用中……");
System.out.println("请关闭相关的程序并重新运行服务器。");
System.exit(0); //如果启动两个ChatServer会出现NullPointerException,可以给出上面的提示,并退出程序。
}catch (IOException e) {
e.printStackTrace();
}
try {
while(started) {
Socket s = ss.accept(); //侦听并接受到此套接字的连接。
Client c = new Client(s);
new Thread(c).start();
clients.add(c);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Client implements Runnable {
private Socket s = null;
private DataInputStream dis = null;
private DataOutputStream dos = null;
private boolean cConnected = false;
public Client(Socket s) {
this.s = s;
try {
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
cConnected = true;
} catch (IOException e) {
e.printStackTrace();
}
}
public void send (String str) {
try {
dos.writeUTF(str);
} catch (IOException e) {
clients.remove(this); //当出现异常的时候说明已经有客户端关闭,这时执行此操作把当前的对象从List中删除。
System.out.println("对方退出了!我从List中去掉了。");
}
}
public void run() {
System.out.println("a client connected!");
try {
while (cConnected) {
String str = dis.readUTF();
//如果把chatClient的客户端关了之后chatServer会出现java.io.EOFException.(end of file);
//所以在Client端关了之后,紧接着也要把ChatServer这里的Socket和InputStream也要关了。
System.out.println(str);
for(int i = 0; i<clients.size(); i++) {
Client c = clients.get(i);
c.send(str);
}
/* //也可以用这种for循环的表示方法。但他的内部进行锁定效率不会很高。
for(Iterator<Client> it = clients.Iterator(); it.hasNext(); ) {
Client c = it.hasNext();
c.send(str);
}
*/
}
} catch(EOFException e) {
System.out.println("client closed!"); // 在抛出异常后,给一些比较友好的提示.
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(dis != null) dis.close();
if(dos != null) dos.close();
if(s != null) s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}