项目:一个公共聊天室功能的实现,实现了登录聊天,保存聊天记录等功能。

一、实现代码

1.客户端

ChatClient.java

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;
import java.util.StringTokenizer;

public class ChatClient {
	// 创建一个Socket
	private static Socket client = null;
	// 定义输入流变量
	private static DataInputStream di = null;
	// 定义输出流变量
	private static DataOutputStream dos = null;
	// 创建scanner对象接收数据
	Scanner sc = new Scanner(System.in);
	// 定义一个String变量保存用户名
	static String name;
	// 定义一个布尔值来判断是否循环接受用户名
	boolean falg = true;

	public static void main(String args[]) {
		// 创建对象
		ChatClient cc = new ChatClient();
		// 创建客户端的socket对象
		client = new Socket();
		// 调用方法
		cc.connect();
		// 调用方法
		cc.send();
	}

	// 连接
	public void connect() {

		// 创建客户端的socket对象
		client = new Socket();
		// 定义一个String接收IP地址
		String IP = null;
		// 定义一个int端口号
		int port = 0;
		System.out.println("***!!Welcome!!***");
		try {
			System.out.println("输入服务器的默认地址是0或127.0.0.1");
			// 接收一个IP保存到string对象中
			IP = sc.nextLine();
			// 匹配接收到的IP,接收到后将IP赋值为默认
			if (IP.equalsIgnoreCase("0")) {
				IP = "";
			}
			// 设置端口号
			port = 8000;
		} catch (Exception e) {
		}
		try {
			// 从给定的主机名得到ip存入inetaddress对象中
			InetAddress address = InetAddress.getByName(IP);
			// 根据得到的ip和端口号创建套接字地址
			InetSocketAddress socketaddress = new InetSocketAddress(address,
					port);
			// 将客户端的套接字链接到服务器
			try {
				// 连接服务器与客户端
				client.connect(socketaddress);
				// 判断是否有连接
				if (client.isConnected()) {
					// 调用方法
					runn();
				} else {
					client.connect(socketaddress);
					// 调用方法
					connect();
				}
			} catch (SocketException e) {
				System.out.println("不能连接到服务器,请重新输入");
				// 调用connect()重新连接
				connect();
			}
		} catch (Exception e) {
			System.out.println("不能连接到服务器,请重新输入");
			// 调用connect()重新连接
			connect();
		}
	}

	public void runn() {
		try {
			// 定义read对象
			ClientThread read = null;
			// 创建read对象
			read = new ClientThread();
			// 创建readdata线程对象
			Thread readData = new Thread(read);
			// 封装一个DataInputStream对象得到输入流
			di = new DataInputStream(client.getInputStream());
			// 封装一个DataOutputStream对象得到输出流
			dos = new DataOutputStream(client.getOutputStream());
			// 接受用户名
			while (falg) {
				System.out.println("请输入用户名:");
				name = sc.next();
				System.out.println(name + "上线了");
				System.out.println("欢迎进入聊天室,需要帮助请输入/A");
				dos.writeUTF(name);
				dos.flush();
				read.setDataInputStream(di);
				// 启动线程
				readData.start();
				// 改变flag中断循环
				falg = false;
			}
		} catch (IOException e) {

		}
	}

	// 写入信息
	@SuppressWarnings("deprecation")
	public void send() {
		// 循环接收发送的消息
		System.out.println("请输入内容:");
		while (sc.hasNext()) {
			String mess;
			mess = sc.nextLine();
			if (mess.equalsIgnoreCase("/0")) {
				System.exit(0);
			} else if (mess.equalsIgnoreCase("/D")) {
				try {
					File file = new File("qq.txt");
					di = new DataInputStream(new FileInputStream(file));
					String info = null;
					System.out.println("1 微笑;2 哭泣;3 疑问;4 加油;5 喔耶~;");
					System.out.println("6 幸福;7 好冷;8 刺眼;9 昏倒;10 幸福;");
					System.out.println("请选择表情:");
					String age = sc.next();
					while ((info = di.readLine()) != null) {
						StringTokenizer stri = new StringTokenizer(info, ".");
						String inn = stri.nextToken();
						String in = stri.nextToken();
						// System.out.println(in);

						if (age.equals(inn)) {
							dos.writeUTF(in);
							break;
						}
					}
				} catch (Exception e) {

				}
			}
			if (mess.equals("/D")) {
				System.out.println("请选择表情:");
			} else {
				select(mess);
			}
		}
	}

	// 将消息发送给服务器
	public void select(String mess) {
		// 判断输入的信息
		if (mess.equalsIgnoreCase("/A")) {
			// 匹配上调用helpList()方法
			helpList();
		} else if (mess.equalsIgnoreCase("/C")) {
			try {
				System.out.println("请输入你要查看的聊天记录的名字");
				String str1 = sc.next();
				File file = new File(str1 + ".txt");
				BufferedReader bf = new BufferedReader(new FileReader(file));
				String str = null;
				while ((str = bf.readLine()) != null) {
					System.out.println(str);
				}
				bf.close();
			} catch (IOException e) {

			}

		} else {
			try {
				// 将消息发送给服务器
				dos.writeUTF(mess);
				// 清空输出流
				dos.flush();
			} catch (IOException e) {

			}
		}
	}

	public void helpList() {
		System.out.println("提示:进入聊天室,默认公聊!!");
		System.out.println("/B 用户在线列表,用户/信息 私聊,/C 查看聊天记录,/D 发送表情,/0 退出系统");
	}
}

ClientThread.java

import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class ClientThread implements Runnable {
	// 定义一个DataInputStream变量
	DataInputStream di;
	// 定义一个string对象接收服务器发送的消息
	String str;
	boolean ff=true;

	public void setDataInputStream(DataInputStream di) {
		this.di = di;
	}

	public void run() {
		while (true) {
			try {
				// 从流中读取信息
				str = di.readUTF();
				// 调用writeFile将信息写入文件中
				writeFile(str);
				// 显示信息
				System.out.println(str);
			} catch (IOException e) {
				// 服务器断开后客户端显示提示信息
				System.out.println("服务断开~~~~~~~");
				// 终止客户端
				System.exit(0);
			}
		}
	}

	public void writeFile(String str2) {
		try {
			// 创建一个文件
			File file = new File(ChatClient.name + ".txt");
			FileWriter fw = new FileWriter(file, true);
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write(str2);
			// 写入一个行分隔符
			bw.newLine();
			// 关闭缓冲流
			bw.close();
			// 关闭字符输出流
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
 

2.服务端

ChatServer.java

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

public class ChatServer {
	// 定义一个serversocket对象
	private static ServerSocket server = null;
	// 定义一个socket对象
	private static Socket client = null;
	// 定义一个变量用来保存客户端的用户名
	private static String name;
	// 定义一个布尔值变量
	private static boolean falg = true;
	// 定义输入流变量
	private static DataInputStream di = null;
	// 定义输出流变量
	private static DataOutputStream dos = null;

	public static void main(String args[]) {
	// 创建ServerThread对象
	ServerThread st = new ServerThread();
	while (true) {
		try {
			// 创建serversocket对象指定端口号为8000
			server = new ServerSocket(8000);
		} catch (IOException e) {
			System.out.println("正在监听!!");
		}
		try {
			System.out.println("等待客户端连接....");
			// 将客户端的套接字与服务器的套接字连接起来
			client = server.accept();
			System.out.println("连接成功!!");
			// 将服务器的输入流封装到DataInputStream中
			di = new DataInputStream(client.getInputStream());
			// 将服务器的输出流封装到DataInputStream中
			dos = new DataOutputStream(client.getOutputStream());
			// 从流中读取用户名
			while (falg) {
				name = di.readUTF();
				if (st.checkp(name)) {
					System.out.println("客户的地址:" + client.getInetAddress()+ "\t" + name + ":进入聊天室");
					// 将falg变为false
					falg = false;
				} else {
					// 清空输出流
					dos.flush();
				}
			}
		} catch (IOException e) {
			System.out.println("正在等待客户端呼叫......");
		}

		// 判断是否有客户端连接到服务器
		if (client.isConnected()) {
			// 创建服务器端的收发信息线程对象
			ServerThread sth = new ServerThread(client, name);
			Thread th = new Thread(sth);
			// 启动线程
			th.start();
			falg = true;
			}
		}
	}
}

ServerThread.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;

public class ServerThread implements Runnable {
	// 定义一个socket变量
	Socket client = null;
	// 定义一个Datainputstream变量
	DataInputStream di = null;
	// 定义一个Dataoutputstream变量
	DataOutputStream dos = null;
	// 定义一个变量保存连接当前线程的用户名
	String name = null;
	// 创建一个hashtable对象用来保存所有的为客户端开辟的线程对象
	static Hashtable<String, ServerThread> clientlist = new Hashtable<String, ServerThread>();

	public ServerThread() {

	}

	public ServerThread(Socket client, String name) {
		try {
			// 将传入的client赋值给成员变量的client
			this.client = client;
			// 将传入的name赋值给成员变量的name
			this.name = name;
			// 将服务器的输出流封装到DataInputStream中
			di = new DataInputStream(client.getInputStream());
			// 将服务器的输出流封装到DataOutputStream中
			dos = new DataOutputStream(client.getOutputStream());
		} catch (IOException e) {

		}
	}

	public void run() {
		try {
			// 添加当前对象到hashtable
			clientlist.put(name, this);
			// 发送新用户进入的消息给所有客户端
			sendallClient(name + "进入聊天室");

			while (true) {

				// 定义一个string对象接受从流中读取到的信息
				String mess = di.readUTF();
				// 创建一个stringtokenizer对象分析接收到的消息
				StringTokenizer str = new StringTokenizer(name, "/");
				// 判断截取到的信息有没分隔符
				// 如果有分隔符则判断为私聊发送信息
				if (str.countTokens() == 2) {
					// 得到要发送私聊信息用户的姓名
					String nameid = str.nextToken();
					// 得到要发送的私聊信息
					String message = str.nextToken();
					// 调用发sendclient送私聊信息
					sendClient(nameid, message);

					// 没有分隔符或者有多个分隔符是信息默认为公聊发送
				} else if (mess.equalsIgnoreCase("/B")) {
					// 匹配到调用getlist方法
					getList();
					// 判断信息是否与-change匹配
				} else if (mess.equalsIgnoreCase("/0")) {
					System.out.println("name" + "退出聊天室");
					break;
				} else if (str.countTokens() == 1 || str.countTokens() >= 3) {
					// 调用sendallclient发送公聊信息
					sendallClient(name + "说:" + mess);
				}
			}
			client.close();
		} catch (Exception e) {

		} finally {
			// 清除客户端信息
			clientlist.remove(name);
			File file = new File(name + ".txt");
			// 文件删除
			file.delete();
			// 有人退出时,给所有人发送退出信息
			sendallClient(name + "退出聊天室");
			System.out.println(getDate() + " " + name + "退出聊天室");
		}
	}

	// 公聊
	public void sendallClient(String mess) {
		// 获得clientlist中值得ServerThread放入枚举集合中
		Enumeration<ServerThread> allclients = clientlist.elements();
		// 遍历所有客户
		while (allclients.hasMoreElements()) {
			// 枚举中还有元素是,返回此举的下一个元素
			ServerThread st = (ServerThread) allclients.nextElement();
			try {
				// 将信息写入流中
				st.dos.writeUTF(getDate() + "\t" + mess);
				// 刷新流
				st.dos.flush();
			} catch (IOException e) {
				Thread th = new Thread(st);
				// 产生异常中断此线程
				th.interrupt();
			}
		}
	}

	// 私聊
	public void sendClient(String name1, String mess) {
		ServerThread st = clientlist.get(name1);
		ServerThread st1 = clientlist.get(name);
		try {
			// 将要发送的信息保存到流中
			st.dos.writeUTF(getDate() + "\t" + name + "对你说:\t" + mess);
			// 刷新流
			st.dos.flush();

			// 把信息发给原客户端
			st1.dos.writeUTF(getDate() + "\t你对" + name1 + "说:\t" + mess);
			// 刷新流
			st1.dos.flush();
		} catch (IOException e) {

			System.out.println("你发送的信息有误,请重新发送!");
			//sendClient(name1, mess);
		}
	}

	public boolean checkp(String str) {
		boolean flag = true;
		// 得到所有的用户名
		Enumeration<String> checkname = clientlist.keys();
		// 循环检查用户名
		while (checkname.hasMoreElements()) {
			// 枚举中还有元素是,返回此举的下一个元素
			String sn = (String) checkname.nextElement();
			// 判断用户名是否重复,重复的话返回false
			if (str.equalsIgnoreCase(sn)) {
				// 返回false
				flag = false;
				// 判断用户名是否为空
			} else if (str.equalsIgnoreCase("")) {
				// 返回false
				flag = false;
			}
		}
		return flag;
	}

	// 关闭服务器
	public void guanBi() {
		if (!(client == null)) {
			try {
				client.close();
				System.out.println("客户端退出此系统");
			} catch (IOException e) {

			}
		}
	}

	// 显示用户在线
	public void getList() {
		// 获得所有键的枚举
		Enumeration<String> checkname = clientlist.keys();
		// 通过用户名获得线程对象
		ServerThread st = clientlist.get(name);
		try {
			// 写入信息
			st.dos.writeUTF("在线用户列表");
			// 写入用户在线人数
			st.dos.writeUTF(clientlist.size() + ":人在线");
		} catch (IOException e1) {

			e1.printStackTrace();
		}
		// 便利枚举
		while (checkname.hasMoreElements()) {
			// 枚举中还有元素是,返回此举的下一个元素
			String sn = (String) checkname.nextElement();
			try {
				// 将用户信息写入流中
				st.dos.writeUTF("用户名称:" + sn);
				// 清空输出流
				st.dos.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	// 时间
	public String getDate() {
		// 获得data对象
		Date nowTime = new Date();
		// 创建格式化参数
		String pattern = "HH:mm:ss";
		// 创建SimpleDateFormat对象
		SimpleDateFormat sdf = new SimpleDateFormat(pattern);
		// 定义一个变量接收时间
		String timePattern = sdf.format(nowTime);
		// 返回当前时间
		return timePattern;
	}
}

二、补充

1.制作.bat执行文件

创建client.bat和server.bat,编辑.bat文件为 java -jar client.jar 和 java -jar server.jar

java基于socket公共聊天室的实现的更多相关文章

  1. Android 基于Socket的聊天室(一)

    Socket是TCP/IP协议上的一种通信,在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路.一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信. Client A ...

  2. Android 基于Socket的聊天应用(二)

    很久没写BLOG了,之前在写Android聊天室的时候答应过要写一个客户(好友)之间的聊天demo,Android 基于Socket的聊天室已经实现了通过Socket广播形式的通信功能. 以下是我写的 ...

  3. 使用socket.io打造公共聊天室

    最近的计算机网络课上老师开始讲socket,tcp相关的知识,当时脑袋里就蹦出一个想法,那就是打造一个聊天室.实现方式也挺多的,常见的可以用C++或者Java进行socket编程来构建这么一个聊天室. ...

  4. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  5. Socket.IO聊天室~简单实用

    小编心语:大家过完圣诞准备迎元旦吧~小编在这里预祝大家元旦快乐!!这一次要分享的东西小编也不是很懂啊,总之小编把它拿出来是觉地比较稀奇,而且程序也没有那么难,是一个比较简单的程序,大家可以多多试试~ ...

  6. Java网络编程案例---聊天室

    网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来. java.net包中JavaSE的API包含有类和接口,它们提供低层次的通信细节.你可以直接使用这些类和接口,来专注于解决 ...

  7. 基于WebSocket实现聊天室(Node)

    基于WebSocket实现聊天室(Node) WebSocket是基于TCP的长连接通信协议,服务端可以主动向前端传递数据,相比比AJAX轮询服务器,WebSocket采用监听的方式,减轻了服务器压力 ...

  8. Java 网络编程 -- 基于TCP 实现聊天室 群聊 私聊

    分析: 聊天室需要多个客户端和一个服务端. 服务端负责转发消息. 客户端可以发送消息.接收消息. 消息分类: 群聊消息:发送除自己外所有人 私聊消息:只发送@的人 系统消息:根据情况分只发送个人和其他 ...

  9. Java Socket 多线程聊天室

    本来这次作业我是想搞个图形界面的,然而现实情况是我把题意理解错了,于是乎失去了最初的兴致,还是把程序变成了功能正确但是“UI”不友好的console了,但是不管怎么样,前期的图形界面的开发还是很有收获 ...

随机推荐

  1. 关于GPL的一些知识

    1.什么是GPL GPL许可协议(GNU General Public License):只要软件中包含有其他GPL协议的产品或代码,那么该软件就必须也采用GPL许可协议且开源及免费.具有以下特点: ...

  2. jsp中两种include的区别【转】

    引用文章:http://www.ibm.com/developerworks/cn/java/j-jsp04293/ http://www.cnblogs.com/lazycoding/archive ...

  3. 关于.net Microsoft.Office.Interop.Word组建操作word的问题,如何控制word表格单元格内部段落的样式。

    控制word表格单元格内部文字样式.我要将数据导出到word当中,对于word表格一个单元格中的一段文字,要设置不同的样式,比如第一行文字作为标题要居中,加粗,第二行为正常的正文. 代码如下 publ ...

  4. docker在ubuntu14.04下的安装笔记

    本文主要是参考官网教程进行ubuntu14.04的安装. 下面是我的安装笔记. 笔记原件完整下载: 链接: https://pan.baidu.com/s/1dEPQ8mP 密码: gq2p

  5. HttpClient接口测试之会话保持

    HttpClient接口测试之会话保持     HttpClient4.X自带会话保持功能,使用同一个HttpClient未关闭的连接即可保持登陆会话,如果多个HttpClient想要使用一个登陆会话 ...

  6. sql join用法学习

    为了在两个或更多的表中获取结果,我们常常会用到join inner join(又叫join) out join包括left join,right join和full join(也就是left+righ ...

  7. EntityFrameWork 使用时碰到的小问题

    EntityFrameWork 使用时碰到的小问题 1,在使用orm访问数据库的相目里,也要引用EntityFrameWork.dll,否则无法使用orm 否则,编译错误 错误 5 "Sys ...

  8. Sql Server中不常用的表运算符之PIVOT

    PIVOT是SQL Server2005新添加的一个表运算符,作用在于将行转为列. 先来看看他的基本语法: 来自http://technet.microsoft.com/zh-cn/library/m ...

  9. 如何在cluster上跑R脚本

    R 是一个比较不错但是有时候操蛋的语言,不错是因为用着爽的时候真的很爽,操蛋是因为这种爽不是什么时候都可以的,比如说在cluster上批处理跑R脚本. 当然说这话有些在上面跑过的各种不服气,你丫傻逼吧 ...

  10. CentOS7安装问题及解决方案记录

    CentOS7系统已安装好: 一.我想要实现系统桌面化. 需要运行 yum 命令. 出现第一个error: 1.提示错误:can not find a valid baseurl 86_X64.... ...