C/S聊天室分为服务器端和客户端,均需要采用多线程来实现。
服务器端主线程需要不断地监听端口,一旦有客户端的请求时,产生相应的Socket,将其加入到队列中并启动子线程,子线程负责接收客户端消息(使用Socket的getInputStream()函数来处理),再将消息发送到所有的客户端(使用Socket的getOutputStream()函数)。
客户端主线程负责获取键盘输入(相当于侦听键盘输入),并传递给Socket的输出流(使用Socket的getOutputStream(),为什么这里是getOutputStream(),因为对Socket而言是输出流),而子线程负责取得服务器端发过来的消息(使用Socket的getInputStream())。
代码如下,代码没有实现界面:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class Server {
private final int PORT=30000;
public static ArrayList<Socket> socketList=new ArrayList<Socket>();
public void init()
{
try (
ServerSocket serverSocket=new ServerSocket(PORT);
)
{
while(true)
{
Socket socket=serverSocket.accept();
socketList.add(socket);
new Thread(new ServerThread(socket)).start();
}
}catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("服务器启动失败,是否"+PORT+"端口已被占用?");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Server s=new Server();
s.init();
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class ServerThread implements Runnable
{
private Socket s=null;
private BufferedReader br=null;
public ServerThread(Socket s)
{
this.s=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Socket对应的输入流获取失败!");
}
String content=null;
while((content=readFromClient())!=null)
{
for(Socket s:Server.socketList)
{
try (
PrintStream ps=new PrintStream(s.getOutputStream()); // 让该程序通过该输出流向socket中输出数据
)
{
ps.println(content);
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private String readFromClient() {
// TODO Auto-generated method stub
try {
return br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
Server.socketList.remove(s);
}
return null;
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
private final static int PORT=30000;
private final static String host="127.0.0.1";
public static void main(String[] args) throws UnknownHostException, IOException {
// TODO Auto-generated method stub
Socket s=new Socket(host,PORT);
new Thread(new ClientThread(s)).start(); // 子线程完成与服务器通信
PrintStream ps=new PrintStream(s.getOutputStream()); // 让程序通过该输出流向Socket中输出数据,得到了socket的输出流
String line=null;
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
// br.readline()不断读取键盘输入内容
while((line=br.readLine())!=null)
{
ps.println(line);
}
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ClientThread implements Runnable {
private Socket s=null;
private BufferedReader br=null;
public ClientThread(Socket s) throws IOException
{
this.s=s;
br=new BufferedReader(new InputStreamReader(s.getInputStream())); // 该句只是返回了这样一个对象,里面并不包含屏幕中的语句
} // 其用readline()的时候才得到输入语句
@Override
public void run() {
// TODO Auto-generated method stub
String content=null;
try {
while((content=br.readLine())!=null) // readline()遇到输入末尾的时候才会返回null
{
System.out.println(content);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
参考资料:疯狂JAVA讲义