用TCP实现聊天室,可多人共同聊天,也可与别人私聊。
一、建立客户端和服务端
二、创建客户端发送线程和接收线程,实现同时读和写
三、服务端创建死循环,以实现循环接收和发送信息
四、创建客户端与服务端的连接通道,不同客户端对应不同连接通道,并将不同通道放到集合list中
五、在连接通道MyChannel中实现向多人聊天,比如:A客户端发送消息,A通道获取该消息msg,其他通道将此msg发送到各自的客户端
六、在连接通道MyChannel中实现私聊,约定私聊格式为@name:content,遍历集合,使用此用户名的连接通道就会向自己客户端发送msg,实现私聊
服务器端
package com.mipo.tcp.demo4;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* 服务器端
* @author Administrator
*
*/
public class Server {
//创建集合
private List<MyChannel> all = new ArrayList<MyChannel>();
public static void main (String[] args) throws IOException {
new Server().start();
}
public void start() throws IOException {
//创建服务器,指定端口
ServerSocket server = new ServerSocket(7777);
while (true) {
//接收客户端连接,阻塞式(若客户端连接成功,则执行下面的程序,否则不执行)
Socket client = server.accept();
//创建一条通道
MyChannel channel = new MyChannel(client);
//将不同通道添加到集合中,方便统一管理
all.add(channel);
//一条通道是一个独立的线程
new Thread(channel).start();
}
}
//内部类
//MyChannel代表的是客户端与服务器之间的连接通道,不同的客户端有不同的连接通道
private class MyChannel implements Runnable{
//输入流
private DataInputStream dis;
//输出流
private DataOutputStream dos;
//判断程序是否正常运行
private boolean isRunning = true;
//聊天室用户名
private String name;
//构造器
public MyChannel(Socket client) {
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
//当前对象的名称=控制台输入的用户名
this.name = dis.readUTF();
//向自己发送消息
this.send("歡迎您進入聊天室");
//发送系统信息
sendOthers(this.name+"進入了聊天室", true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
//出现异常,关闭输入输出流
CloseUtil.closeAll(dis,dos);
//设置程序运行状态
isRunning = false;
}
}
//读取数据
private String receive() {
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
CloseUtil.closeAll(dis);
isRunning = false;
//移除自身
all.remove(this);
}
return msg;
}
//发送数据
private void send(String msg) {
if(null == msg || msg.equals("")) {
return;
}
try {
//发送信息
dos.writeUTF(msg);
//刷新
dos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
CloseUtil.closeAll(dos);
isRunning = false;
all.remove(this);
}
}
//发送给其他客户端
private void sendOthers(String msg,boolean sys) {
//是否为私聊,约定格式为: @姓名:
if(msg.startsWith("@") && msg.indexOf(":")>-1) {
//获取子串,左包由不包
String name = msg.substring(1, msg.indexOf(":"));
//获取content
String content = msg.substring(msg.indexOf(":")-1);
//foreach(元素类型 变量:数组或集合表达式)
for(MyChannel other:all) {
if(other.name.equals(name)) {
other.send(this.name+"对您悄悄说:"+content);
}
}
}else{
//遍历容器
for(MyChannel other:all) {
if(other==this) {
continue;//跳过本次循环,执行下一次循环
}
if(sys){//系统消息
other.send("系统消息:"+msg);
}else{
//发送其他客户端
other.send(this.name+"对所有人说:"+msg);
}
}
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(isRunning) {
sendOthers(receive(),false);
}
}
}
}
package com.mipo.tcp.demo4;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 客户端
* @author Administrator
*
*/
public class Client {
public static void main(String[] args) throws IOException, IOException {
System.out.println("请输入您的名称");
//获取控制台输入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//读取控制台输入的文本
String name = br.readLine();
if(name.equals("")) {
return;//如果输入的姓名为"",程序执行到此终止
}
//建立客户端,必须指定服务器+服务器端口,此时就在连接
Socket client = new Socket("localhost",7777);
//发送线程
Thread t1 = new Thread(new Send(client, name));
t1.start();
//接收线程
Thread t2 = new Thread(new Receive(client));
t2.start();
}
}
发送线程
package com.mipo.tcp.demo4;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
* 客户端发送数据线程
* @author Administrator
*
*/
public class Send implements Runnable{
//控制台输入流
private BufferedReader console;
//管道输出流
private DataOutputStream dos;
//控制线程
private boolean isRunning = true;
//聊天室用户名
private String name;
//构造器
public Send() {
console = new BufferedReader(new InputStreamReader(System.in));
}
public Send(Socket client,String name) {
this();//指无参构造器创建的对象
try {
dos = new DataOutputStream(client.getOutputStream());
//当前调用send的方法的客户端的用户名
this.name = name;
//将用户名发送到服务器
send(name);
} catch (IOException e) {
//e.printStackTrace();
//关闭输出流
CloseUtil.closeAll(dos,console);
//当遇到问题,线程不再运行
isRunning = false;
}
}
//从控制台接收数据
private String getMsgFromConsole () {
try {
return console.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
//1、从控制台接收数据
//2、再发送数据
public void send(String msg) {
if (null != msg && !msg.equals("")) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
CloseUtil.closeAll(dos,console);
isRunning = false;
}
}
}
@Override
public void run() {
// TODO Auto-generated method stub
//循环发送,当程序出现异常,isRunning=false
while (isRunning) {
send(getMsgFromConsole());
}
}
}
接收线程
package com.mipo.tcp.demo4;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**
* 客户端接收线程
* @author Administrator
*
*/
public class Receive implements Runnable {
//输入流
private DataInputStream dis;
//线程标识
private boolean isRunning = true;
//构造器
public Receive() {
}
public Receive(Socket client) {
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
isRunning = false;
CloseUtil.closeAll(dis);
}
}
//接收数据
public String receive() {
String msg = "";
try {
//该方法读取使用 UTF-8 修改版格式编码的 Unicode 字符串的表示形式;然后以 String 的形式返回此字符串。
msg = dis.readUTF();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
isRunning = false;
CloseUtil.closeAll(dis);
}
return msg;
}
@Override
public void run() {
// TODO Auto-generated method stub
while (isRunning) {
System.out.println(receive());
}
}
}
关闭流工具类
package com.mipo.tcp.demo4;
import java.io.Closeable;
import java.io.IOException;
/**
* 关闭流的方法
* @author Administrator
*
*/
public class CloseUtil {
public static void closeAll(Closeable...io) {
for (Closeable temp:io) {//foreach
if (null != temp) {
try {
temp.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}