Java基于TCP协议实现多线程聊天室

这篇文章实现了丐版的聊天室。用到的主要技术是多线程和TCP,一共有三各类:服务器Server,服务器线程类Worker,客户端Client。话不多说,直接看代码。

Server.java

Server类用于启动监听服务和管理Worker类。接收到客户端的连接请求后会新建一个线程运行Worker类并放到threadMap中。

import java.net.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.io.*;


public class Server {
	//创建了一个线程安全的键值对集合
	public static Map<String, Worker> threadMap = new ConcurrentHashMap<String, Worker>();
	
	public static void main(String[] args) {
		try {
			//在22222端口上运行服务
			ServerSocket ss = new ServerSocket(22222);
			System.out.println("服务器启动...");
			while(true) {
				Socket s = ss.accept();		
				//启动线程	
				new Thread(new Worker(s)).start();
				System.out.println("连接到一个用户" );
			}
		}
		catch(Exception ex){
			ex.printStackTrace();
		}
	}

}

Worker.java

Worker类负责文本的输入输出。Worker接受到客户端发送来的消息后会进行遍历,判定是否是发给自己的消息。

import java.io.*;
import java.net.*;


 class Worker implements Runnable{
	 Socket s;
	 InputStream ips;
	 OutputStream ops;
	 BufferedReader br;
	 DataOutputStream dos;
	 
	 public Worker(Socket s) {
			this.s = s;
		}
	 
	@Override
	public void run() {
		try {
			ips = s.getInputStream();
			ops = s.getOutputStream();
			//输入流
			br = new BufferedReader(new InputStreamReader(ips));
			//输出流
			dos = new DataOutputStream(ops);
			while (true) {
				String strWord = br.readLine();
				//获取用户名
				String name = strWord.split(":")[0];
				//将客户端添加到threadmap
				Server.threadMap.put(name, this);
				
				System.out.println(strWord +"---" + (strWord.length()-name.length()-1));			
				//quit正常关闭
				if (strWord.equalsIgnoreCase("quit"))
					break;
				//遍历map集合,给不是自己的线程发送消息
				for(Worker w : Server.threadMap.values()) {
					if(w != this){
						w.dos.writeBytes(strWord + System.getProperty("line.separator"));
					}			
				}
			}
			br.close();
			// 关闭包装类,会自动关闭包装类中所包装的底层类。所以不用调用ips.close()
			dos.close();
			s.close();
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
}

Client.java

客户端实现的功能是在本机发送和接受文本,打开连接后会启动一个while循环监听服务器的消息,如果接受到消息就会打印出来。发送消息时用输入输出流向服务端进行发送。

import java.io.*;
import java.net.*;
import java.util.Scanner;

public class Client {
	public static void main(String[] args) {
		try {
			//此处换成本机的ip地址,只在本地测试可以直接getHostAddress()
			Socket s = new Socket(InetAddress.getByName("192.168.56.1"), 22222);
			//打开输入输出流
			InputStream ips = s.getInputStream();
			BufferedReader brNet = new BufferedReader(new InputStreamReader(ips));
			
			OutputStream ops = s.getOutputStream();
			DataOutputStream dos = new DataOutputStream(ops);
			
			BufferedReader brKey = new BufferedReader(new InputStreamReader(System.in));
			
			System.out.println("Input your name:");
			Scanner sc = new Scanner(System.in);
			String name = sc.nextLine();
			while(true) {
				String strWord = brKey.readLine();
				if(strWord.equalsIgnoreCase("quit")) {
					break;
				}
				else {
					//System.out.println("i sent:" + strWord);
					//后面这个System.getProperty("line.separator")是换行的意思
					dos.writeBytes(name +":" + strWord + System.getProperty("line.separator"));
					
					System.out.println(brNet.readLine());
				}
			}			
			dos.close();
			brNet.close();
			brKey.close();
			s.close();
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}
}

其中Server类和Worker类是一个工程,Client是另一个工程。运行的时候要先运行服务器,再运行客户端,不然会产生异常。运行效果就不放了。
这里需要强调的是Server类的

public static Map<String, Worker> threadMap = new ConcurrentHashMap<String, Worker>();

其中的ConcurrentHashMap是线程安全的hasmap。由于换成不安全的Map时,由于Worker类会对threadMap进行访问,会出现消息和目标用户不对应的情况。关于线程、并行串行、同步异步方面的知识目前还在学习中,以后会分享出来。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值