网络编程之 使用Socket创建聊天室

使用基于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聊天室就完成啦!

你可能感兴趣的:(javaio,java基础,线程,网络编程)