使用基于tcp协议的socket创建聊天室,
昨天我写的博客已经介绍了怎么使用 ServerSocket 创建服务端,以及服务端怎么返回数据给客户端
昨天写的博客有个缺陷,就是 客户端只能 先发送,后读取,对于正常情况来说应该是,客户端读取和发送是互相不相干涉的两个步骤,所以这里就得使用到多线程了,为 客户端发送数据,和接收数据各开辟一条道路。
首先来看看客户端的发送消息:
客户端发送消息,得跟 Socket 关联起来,里面则有 输入输出流,因为客户端要想发送则必须有输出流,输入流则是因为我们在控制台使用,使用输入流输入数据
那么我们来看看 ClientSend 类
/**
* 发送数据线程
* @author snow
*
*/
public class ClientSend implements Runnable {
private BufferedInputStream bis;
private BufferedOutputStream bos;
private Socket client;
private boolean isRuning = true ;
/**
* 构造方法,初始化输入输出
* @param client
*/
public ClientSend(Socket client) {
this.bis = new BufferedInputStream( System.in );
this.client = client;
try {
this.bos = new BufferedOutputStream( this.client.getOutputStream() );
} catch (IOException e) {
e.printStackTrace();
isRuning = false;
CloseUtil.closeAll(bos,bis,client);
}
}
/**
* 获取控制台输入的数据
* @return
*/
private String getMsg() {
byte [] data = new byte[1024];
int len = 0;
try {
len = bis.read(data);
} catch (IOException e) {
e.printStackTrace();
isRuning = false;
CloseUtil.closeAll(bos,bis,client);
}
return new String(data , 0 ,len);
}
/**
* 发送消息
*/
public void send() {
String msg = getMsg();
try {
this.bos.write(msg.getBytes());
this.bos.flush();
} catch (IOException e) {
e.printStackTrace();
isRuning = false;
CloseUtil.closeAll(bos,bis,client);
}
}
@Override
public void run() {
while(isRuning) {
send();
}
}
}
接下来就是接收类:
接收类也和读取类一样,需要和Socket 进行关联
看看代码吧:
/**
* 接收消息
* @author snow
*
*/
public class ClientReceive implements Runnable {
private Socket client;
private BufferedInputStream bis;
private boolean isRuning = true;
public ClientReceive(Socket client) {
this.client = client;
try {
this.bis = new BufferedInputStream(client.getInputStream()); // 获取输入流
} catch (IOException e) {
isRuning = false;
CloseUtil.closeAll(bis,client);
e.printStackTrace();
}
}
// 接收数据
public String receiveMsg() {
byte [] data = new byte[1024];
int len = 0;
try {
len = this.bis.read(data);
return new String(data,0,len);
} catch (IOException e) {
isRuning = false;
CloseUtil.closeAll(bis,this.client);
e.printStackTrace();
}
return "";
}
@Override
public void run() {
while (isRuning) {
// 输出接收到的数据
System.out.println( receiveMsg().replaceAll("\r\n", "") );
}
}
}
则,客户端,创建一个 Socket连接,然后new 两个线程跑起来就可以实现 接收发送互不干扰了
public class MyClient {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket client = new Socket("127.0.0.1", 9999);
// 发送消息
new Thread(new ClientSend(client)).start();
// 接收消息
new Thread(new ClientReceive(client)).start();
}
}
服务端这边,如果使用 while(true)里面按照原来得样子不使用线程写的话,则一个客户端连接上了,则其他的客户端得等待这个客户端聊完了才能连上,很显然这是不合常理得。
为了避免这种问题,我们服务端也得使用多线程。
通常多个客户端一起聊称之为群聊,自己发送得消息别人都收得到。
所以我们这里创建一个 Server 类实现 runable,同时使用一个 list来装 每一个Server
看看代码吧:
/**
* 服务端的 server
* @author snow
*
*/
public class Server implements Runnable {
private BufferedInputStream bis ;
private BufferedOutputStream bos;
private Socket server;
private boolean isRuning = true;
private ArrayList clients;
public Server(Socket server,ArrayList clients) {
try {
this.bis = new BufferedInputStream( server.getInputStream() );
this.bos = new BufferedOutputStream( server.getOutputStream() );
this.server = server;
this.clients = clients;
} catch (IOException e) {
e.printStackTrace();
isRuning = false;
CloseUtil.closeAll(server,bos,bis);
}
}
/**
* 接收数据
* @return
*/
private String receviceMsg() {
byte [] data = new byte[1024];
int len = 0;
try {
len = this.bis.read(data);
} catch (IOException e) {
e.printStackTrace();
isRuning = false;
CloseUtil.closeAll(server,bos,bis);
}
return new String(data, 0, len);
}
/**
* 发送消息
*/
private void sendMsg(String data) {
try {
this.bos.write(data.getBytes());
this.bos.flush();
} catch (IOException e) {
e.printStackTrace();
isRuning = false;
CloseUtil.closeAll(server,bos,bis);
}
}
/**
* 发送给另外的客户端消息
*/
private void sendMsgToAllClient() {
String msg = receviceMsg();
for (Server server : clients) {
if( server == this ) {
continue;
}
server.sendMsg(msg);
}
}
@Override
public void run() {
while(isRuning) {
sendMsgToAllClient();
}
}
}
MyServer 服务端类这样写:
public class MyServer {
// 装每一个连接上得 客户端,发送给其他客户端消息得时候使用
private ArrayList clients = new ArrayList<>();
public static void main(String[] args) throws IOException {
new MyServer().server();
}
public void server() throws IOException {
// 1,创建服务端
ServerSocket server = new ServerSocket(9999);
while (true) {
// 2,接收客户端连接
Socket socket = server.accept();
Server s = new Server(socket, clients);
clients.add(s);
new Thread(s).start();
}
}
}
这样,一个 基于TCP得socket聊天室就完成啦!