热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:Java利用TCP编程实现简单聊天室

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java利用TCP编程实现简单聊天室相关的知识,希望对你有一定的参考价值。前言:

篇首语:本文由编程笔记#小编为大家整理,主要介绍了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 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();
}

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 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();
}
}
}
}

推荐阅读
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
author-avatar
chenyanni1030_430
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有