Java利用TCP編程實現簡單聊天室


前言:

本文是我在學習尚學堂JAVA300集第二季網絡編程部分仿照視頻內容實現而成
具體可以去尚學堂官網觀看視頻學習

一、實現思路

   實現聊天室的最核心部分就是JAVA的TCP網絡編程。
  TCP 傳輸控制協議是一種面向連接的、可靠的、基於字節流的傳輸層通信協議 ,在Java中我們利用ServerSocket類來建立服務端,利用Socket類來建立客戶端。這里要注意,在TCP中,Socket實際上是指
Server端與Client端建立的一個雙向的流通道,我們利用這個流通道實現數據的傳輸。
  我們將聊天室分為兩部分,客戶端和服務端.
  對於客戶端,主要有兩個功能,信息的收與信息的發。因為這兩個功能需要並行進行,並且要不停的進行收和發,所以將這兩個功能抽象成兩個實現Runnable接口的(Send,Recevice)類,每次客戶端Client啟動,建立一個Socket,並利用這個Socket建立一個收線程(Recevice類)和發線程(Send)類

  對於服務器端,因為我們要不停的監聽是否有新的連接進來,所有要通過一個循環不停的接收,這里的接收函數是阻塞式的。因為我們要對所有的連接進行同時處理,所有我們將新得到連接抽象成一個實現Runnable接口的User類,利用多線程進程對每一個連接並行處理。為了方便多個連接之間的交互,我們將User類作為Server類的一個內部類使用。

二、實現過程

1.客戶端

Client類:

package top.dlkkill.tcp.chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Client {
	
	public static void main(String[] args) throws IOException {
		//從控制台獲得輸入
		BufferedReader console=new BufferedReader(new InputStreamReader(System.in));
		System.out.println("請輸入您的名字:");
		String name=console.readLine();
		if(name.equals(""))
			return;
		Socket client=null;
		try {
			//建立新連接,注意這里創建好就已經連接上了,要保證服務端已經開啟
			client=new Socket("localhost", 8888);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			System.err.println(name+"連接失敗");
		}
		//兩條線路,一條負責發,一條負責收
		new Thread(new Send(client,name)).start();
		new Thread(new Recevice(client)).start();
	}
	
}

Send類

package top.dlkkill.tcp.chat;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class Send implements Runnable{
	
	//負責寫出,將信息傳輸到服務端
	private DataOutputStream os;
	//負責讀取控制台輸入
	private BufferedReader console;
	//線程標識
	private boolean isRun=true;
	
	//通過死循環保證線程一直進行
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(isRun) {
			send(getMsgFromConsole());
		}
	}
	
	//構造方法,利用Socket類獲得流
	public Send(Socket client,String name) {
		// TODO Auto-generated constructor stub
		try {
			os=new DataOutputStream(client.getOutputStream());
			console=new BufferedReader(new InputStreamReader(System.in));
			send(name);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			isRun=false;
			CloseUtil.closeAll(os,console);
		}
	}
	
	//發送函數
	public void send(String msg) {
		
		try {
			if(msg!=null&&!msg.equals("")) {
				os.writeUTF(msg);
				os.flush();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			isRun=false;
			CloseUtil.closeAll(os,console);
		}
	}
	
	//從控制台不斷讀取信息
	public String getMsgFromConsole() {
		String msg=null;
		try {
			msg=console.readLine();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			isRun=false;
			CloseUtil.closeAll(os,console);
		}
		return msg;
	}
}

Recevice類

package top.dlkkill.tcp.chat;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

public class Recevice implements Runnable{
	
	    //負責讀取服務端發送過來的信息
		private DataInputStream is;
		//線程標識
		private boolean isRun=true;
		
		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(isRun){
				recevice();
			}
		}
		
		public Recevice(Socket client) {
			// TODO Auto-generated constructor stub
			try {
				is=new DataInputStream(client.getInputStream());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				CloseUtil.closeAll(is);
				isRun=false;
			}
		}
		
		public void recevice() {
			String msg=null;
			try {
				msg=is.readUTF();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				CloseUtil.closeAll(is);
				isRun=false;
			}
			System.out.println(msg);
		}
		
}

2.服務端

Server類(內部有一個User類)

package top.dlkkill.tcp.chat;


import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;

public class Server {
	
	//保存所有的連接
	private HashSet<User> users;
	//線程標識
	private boolean run=true;
	
	public static void main(String[] args) {

		//創建一個Server類
		Server server=new Server();
		try {
			//啟動服務器
			server.start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public Server() {
		run=true;
		users=new HashSet<User>();
	}
	
	public void start() throws IOException {
		//創建一個服務器端
		ServerSocket server=new ServerSocket(8888);
		while(run) {
			//不斷接收一個新的連接,利用新連接創建一個User線程進行處理
			Socket client= server.accept();
			User user=new User(client);
			users.add(user);
			new Thread(user).start();
		}
	}
	
	public void stop() {
		run=false;
	}
	
	
	//代表一個連接,負責信息的接收與轉發
	private class User implements Runnable{
		
		//記錄連接用戶的名字
		private String name;
		
		public String getName() {
			return name;
		}
		//負責接收
		private DataInputStream is;
		//負責發送
		private DataOutputStream os;
		//線程標識
		private boolean isRun=true;
		
		public User(Socket client) {

			try {
				is=new DataInputStream(client.getInputStream());
				os=new DataOutputStream(client.getOutputStream());
				isRun=true;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				isRun=false;
				CloseUtil.closeAll(is,os);
			}
			try {
				 name=is.readUTF();
				 this.sendOther(new String("歡迎"+name+"進入聊天室"),true);
				 this.send(new String("系統:您已經進入了聊天室"));
			}catch (Exception e) {
				// TODO: handle exception
			}
		}
		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(isRun) {
				this.sendOther(this.revice(),false);
			}
		}
		
		//接收信息
		public String revice() {
			String msg = null;
			try {
				msg=is.readUTF();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return msg;
		}
		
		//發送信息
		public void send(String msg) {
			try {
				os.writeUTF(msg);
				os.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		//將信息轉發給其他用戶,同時實現了私聊功能和系統信息功能
		//因為是內部類,所以可以訪問Server類中的private HashSet<User> users
		//@XX:代表向XX發送私聊信息
		public void sendOther(String msg,boolean admin) {
			if(msg.startsWith("@")&&msg.contains(":")) {
				String toname=msg.substring(1, msg.indexOf(":"));
				String newmsg=msg.substring(msg.indexOf(":")+1);
				for (User user : users) {
					if(user.getName().equals(toname)) {
						user.send(this.name+"悄悄的對你說:"+newmsg);
					}
				}
			}else {
				for (User client : users) {
					if(client!=this) {
						if(admin)
							client.send("系統:"+":"+msg);
						else
							client.send(this.name+":"+msg);
					}
				}
			}
		}
	}
}

3.工具類

CloseUtil類(負責關閉流)

package top.dlkkill.tcp.chat;

import java.io.Closeable;

public class CloseUtil {
	public static void closeAll(Closeable ...io) {
		for (Closeable closeable : io) {
			try {
				if(closeable!=null)
					closeable.close();
			}catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM