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();
			}
		}
	}
}
												
											Java利用TCP编程实现简单聊天室的更多相关文章
- java web利用mvc结构实现简单聊天室功能
		
简单聊天室采用各种内部对象不适用数据库实现. 一个聊天室要实现的基本功能是: 1.用户登录进入聊天室, 2.用户发言 3.用户可以看见别人发言 刚才算是简单的需求分析了,现在就应该是进 ...
 - JAVA基础知识之网络编程——-基于TCP通信的简单聊天室
		
下面将基于TCP协议用JAVA写一个非常简单的聊天室程序, 聊天室具有以下功能, 在服务器端,可以接受客户端注册(用户名),可以显示注册成功的账户 在客户端,可以注册一个账号,并用这个账号发送信息 发 ...
 - (ASP.net)利用Application对象制作简单聊天室
		
1.共四个页面,Default.aspx默认主页,Default2.aspx聊天室 default3.aspx显示用户列表,default4.aspx显示聊天内容,添加一个Global.asax全局程 ...
 - Java 多线程Socket编程通讯--实现聊天室代码
		
1.创建服务器类 import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import ja ...
 - 利用socket.io+nodejs打造简单聊天室
		
代码地址如下:http://www.demodashi.com/demo/11579.html 界面展示: 首先展示demo的结果界面,只是简单消息的发送和接收,包括发送文字和发送图片. ws说明: ...
 - 如何利用WebSocket实现网页版聊天室
		
花了将近一周的时间终于完成了利用WebSocket完成网页版聊天室这个小demo,期间还走过了一段"看似弯曲"的道路,但是我想其实也不算是弯路吧,因为你走过的路必将留下你的足迹.这 ...
 - SpringBoot 搭建简单聊天室
		
SpringBoot 搭建简单聊天室(queue 点对点) 1.引用 SpringBoot 搭建 WebSocket 链接 https://www.cnblogs.com/yi1036943655/p ...
 - TCP/IP以及Socket聊天室带类库源码分享
		
TCP/IP以及Socket聊天室带类库源码分享 最近遇到个设备,需要去和客户的软件做一个网络通信交互,一般的我们的上位机都是作为客户端来和设备通信的,这次要作为服务端来监听客户端,在这个背景下,我查 ...
 - 基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍。最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室。
		
基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍.最后我们将会实现一个基于S ...
 
随机推荐
- XLSReadWriteII5导入excel数据
			
procedure TForm1.Button1Click(Sender: TObject); var xls: TXLSReadWriteII5; openFile: TOpenDialog; Ro ...
 - [js]练习绘制拓扑图
 - vi检索
			
/ :检索 n :向下执行上一步的检索 N :向上执行上一步的检索
 - 做一个有产品思维的研发:Scrapy安装
			
每天10分钟,解决一个研发问题. 如果你想了解我在做什么,请看<做一个有产品思维的研发:课程大纲>传送门:https://www.cnblogs.com/hunttown/p/104909 ...
 - office word memo
			
显示左侧目录树 office 和 wps 的差异 wps 的版本:视窗 ->文档结构图 office 的版本: 视图 ->导航窗格
 - cc.Mask. 纯代码拉伸遮罩
			
var imgBoxInner = new cc.Node('imgBoxInner');var mask = imgBoxInner.addComponent(cc.Mask);mask.alpha ...
 - java学习之成员内部类
			
//成员内部类:直接在类中定义 /*成员内部类的通常用法: * 通常是提供给外部类使用不进行内部类的实例化 * 因此一般把他设为私有的类用private限定 * * */ /*Demo*/ class ...
 - jquery datetimepicker 日期时间控件的使用及参数说明
			
首先下载 jquery.datetimepicker.css jquery.datetimepicker.main.js 1. 引入css和js (注:该控件要依赖于jquery) <link ...
 - linux 中的 vim 设置粘贴板
			
https://blog.csdn.net/zhangxiao93/article/details/53677764 亲测有效
 - sudo:无法解析主机
			
原因:替换了hosts文件(之前用了google hosts) 或者更改了主机名称 解决办法:$ pkexec nano /etc/hosts 加入 127.0.1.1 xxxx XXX为主机名字即 ...