利用面向對象的思想,對在線聊天的模塊進行封裝;
- 一個服務器,可以有多個客戶端進行連接;每個客戶端是個單獨的線程,互不影響;(因爲每跟服務端建立一次連接,所對應的Socket是不同的,一個ServerSocket可以對應多個Socket);
- 對於客戶端來說,既可以是發送者,也可以是接收者;所以發送者和接收者需要使用線程來處理;
- 底層數據傳輸還是藉助於io流進行處理,BufferedReader處理控制檯的輸入內容,數據流進行對接進行;
public class Client {
/**
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws UnknownHostException, IOException {
System.out.println("請輸入名稱:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String name = br.readLine();
if(name.equals("")){
return;
}
Socket client = new Socket("localhost",9999);
new Thread(new Send(client,name)).start(); //一條路徑
new Thread(new Receive(client)).start(); //一條路徑
}
}
/**
* 發送數據 線程
* @author Administrator
*
*/
public class Send implements Runnable{
//控制檯輸入流
private BufferedReader console;
//管道輸出流
private DataOutputStream dos;
//控制線程
private boolean isRunning =true;
//名稱
private String name;
public Send() {
console =new BufferedReader(new InputStreamReader(System.in));
}
public Send(Socket client,String name){
this();
try {
dos =new DataOutputStream(client.getOutputStream());
this.name =name;
send(this.name);
} catch (IOException e) {
//e.printStackTrace();
isRunning =false;
CloseUtil.closeAll(dos,console);
}
}
//1、從控制檯接收數據
private String getMsgFromConsole(){
try {
return console.readLine();
} catch (IOException e) {
//e.printStackTrace();
}
return "";
}
/**
* 1、從控制檯接收數據
* 2、發送數據
*/
public void send(String msg){
try {
if(null!=msg&& !msg.equals("")){
dos.writeUTF(msg);
dos.flush(); //強制刷新
}
} catch (IOException e) {
//e.printStackTrace();
isRunning =false;
CloseUtil.closeAll(dos,console);
}
}
@Override
public void run() {
//線程體
while(isRunning){
send(getMsgFromConsole());
}
}
}
public class Receive implements Runnable {
//輸入流
private DataInputStream dis ;
//線程標識
private boolean isRunning = true;
public Receive() {
}
public Receive(Socket client){
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
e.printStackTrace();
isRunning =false;
CloseUtil.closeAll(dis);
}
}
/**
* 接收數據
* @return
*/
public String receive(){
String msg ="";
try {
msg=dis.readUTF();
} catch (IOException e) {
e.printStackTrace();
isRunning =false;
CloseUtil.closeAll(dis);
}
return msg;
}
@Override
public void run() {
//線程體
while(isRunning){
System.out.println(receive());
}
}
}
3.服務器是對數據請求的分發,可以全部發送,也可以單獨給某個客戶端發送,所謂私聊;(服務器每次accept()獲取Socket後,會創建一個線程去對接(接收消息和回傳消息),也會統一管理這些線程)
/**
* 創建服務器
* 寫出數據:輸出流
* 讀取數據:輸入流
* @author Administrator
*
*/
public class Server {
private List<MyChannel> all = new ArrayList<MyChannel>();
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
new Server().start();
}
public void start() throws IOException{
ServerSocket server =new ServerSocket(9999);
while(true){
Socket client =server.accept();
MyChannel channel = new MyChannel(client);
all.add(channel);//統一管理
new Thread(channel).start(); //一條道路
}
}
/**
* 一個客戶端 一條道路
* 1、輸入流
* 2、輸出流
* 3、接收數據
* 4、發送數據
* @author Administrator
*
*/
private class MyChannel implements Runnable{
private DataInputStream dis ;
private DataOutputStream dos ;
private boolean isRunning =true;
private String name;
public MyChannel(Socket client ) {
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
this.name =dis.readUTF();
this.send("歡迎您進入聊天室");
sendOthers(this.name+"進入了聊天室",true);
} catch (IOException e) {
//e.printStackTrace();
CloseUtil.closeAll(dis,dos);
isRunning =false;
}
}
/**
* 讀取數據
* @return
*/
private String receive(){
String msg ="";
try {
msg=dis.readUTF();
} catch (IOException e) {
//e.printStackTrace();
CloseUtil.closeAll(dis);
isRunning =false;
all.remove(this); //移除自身
}
return msg;
}
/**
* 發送數據
*/
private void send(String msg){
if(null==msg ||msg.equals("")){
return ;
}
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
//e.printStackTrace();
CloseUtil.closeAll(dos);
isRunning =false;
all.remove(this); //移除自身
}
}
/**
* 發送給其他客戶端
*/
private void sendOthers(String msg,boolean sys){
//是否爲私聊 約定
if(msg.startsWith("@")&& msg.indexOf(":")>-1 ){ //私聊
//獲取name
String name =msg.substring(1,msg.indexOf(":"));
String content = msg.substring(msg.indexOf(":")+1);
for(MyChannel other:all){
if(other.name.equals(name)){
other.send(this.name+"對您悄悄地說:"+content);
}
}
}else{
//遍歷容器
for(MyChannel other:all){
if(other ==this){
continue;
}
if(sys){ //系統信息
other.send("系統信息:"+msg);
}else{
//發送其他客戶端
other.send(this.name+"對所有人說:"+msg);
}
}
}
}
@Override
public void run() {
while(isRunning){
sendOthers(receive(),false);
}
}
}
}
4.關閉
public class CloseUtil {
public static void closeAll(Closeable... io){
for(Closeable temp:io){
try {
if (null != temp) {
temp.close();
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
}