网络编程之TCP
TCP协议时安全的, 可靠的, 但性能相对UDP要低
TCP实现聊天室
最终实现效果
思路:
客户端-->控制台录入数据
-->写出数据到服务端
服务端-->读取数据
-->写出数据
客户端-->读取服务端数据
-->打印到控制台
客户端录入数据加入循环实现多条信息发送.
服务端加入多线程, 每连接一个客户端就启用一个线程来响应数据.
聊天室, 一个客户端说话, 所有人都可以看到, 需要借助容器, 获取每一个客户端, 然后在输出时, 把数据发送给每一个人(除了自己), 其他客户带接收到数据后会打印到控制台, 实现聊天室
客户端姓名, 在每一个客户端建立连接之前, 从控制台接收一个姓名
初始化时, 将这个姓名说:发送给每一个客户端, 后面接上要发送的数据.
加入聊天室, 退出聊天室分别在初始化时, 和客户端退出时进行打印即可
私聊消息加入数据判断, 分析数据, 然后发送给指定的客户端, 而不是发送给所有客户端
代码:
/**
* 释放资源工具类
* @author ASUS
*
*/
public class CloseUtil {
/** 释放资源方法
* @param closeables
*/
public static void close(Closeable...closeables ) {
for (Closeable closeable : closeables) {
try {
if(null != closeable)
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
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 com.net.chat03.CloseUtil;
/**
* TCP聊天室客户端, 加入私聊功能
* @author ASUS
*
*/
public class Client {
public static void main(String[] args) throws Exception {
System.out.println("-----client------");
// 在每一个客户端连接之前, 从控制台接收客户端姓名
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入用户名:");
String name = br.readLine();
Socket client = new Socket("localhost",8888);
new Thread(new Send(client, name)).start();
new Thread(new Receive(client)).start();
}
// 发送数据
static class Send implements Runnable{
private DataOutputStream dos;
private Socket client;
private BufferedReader console;
private boolean isRunning;
private String name; //客户端姓名
public Send(Socket client, String name) {
this.client = client;
this.name = name;
console = new BufferedReader(new InputStreamReader(System.in));
try {
dos = new DataOutputStream(client.getOutputStream());
send(this.name); //发送姓名
isRunning = true;
} catch (IOException e) {
e.printStackTrace();
release();
}
}
@Override
public void run() {
while(isRunning) {
String msg = getMsg();
if(!"".equals(msg)) {
send(msg);
}
}
}
// 发送控制台录入的数据
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
// 获取控制台的数据
private String getMsg() {
try {
return console.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
// 释放资源
private void release() {
isRunning = false;
CloseUtil.close(dos,client);
}
}
// 接收数据
static class Receive implements Runnable{
private DataInputStream dis;
private Socket client;
private boolean isRunning;
public Receive(Socket client) {
this.client = client;
try {
dis = new DataInputStream(client.getInputStream());
isRunning = true;
} catch (IOException e) {
e.printStackTrace();
release();
}
}
@Override
public void run() {
while(isRunning) {
String msg = receive();
System.out.println(msg);
}
}
// 接收数据
private String receive() {
try {
return dis.readUTF();
} catch (IOException e) {
e.printStackTrace();
release();
}
return "";
}
// 释放资源
private void release() {
isRunning = false;
CloseUtil.close(dis,client);
}
}
}
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;
import com.net.chat03.CloseUtil;
/**
* TCP聊天室服务端, 加入私聊功能
* @author ASUS
*
*/
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("-----server-----");
ServerSocket server = new ServerSocket(8888);
while(true) {
Socket client = server.accept();
System.out.println("一个客户端建立了连接");
Channel c = new Channel(client);
Channel.users.add(c);
new Thread(c).start();;
}
}
static class Channel implements Runnable{
// CopyOnWriteArrayList用于存储客户端, 用于并发修改
public static CopyOnWriteArrayList<Channel> users = new CopyOnWriteArrayList<>();
private Socket client;
private DataOutputStream dos;
private DataInputStream dis;
private boolean isRunning;
private String name; //客户端名字
public Channel(Socket client) {
this.client = client;
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
this.name = receive(); //初始化时先接收这个名字
this.send(name + " 欢迎你的到来"); //接收到名字后就打印出来
this.sendOthers(name + "来到了聊天室", true); //在聊天室对所有人说我来了
isRunning = true;
} catch (IOException e) {
e.printStackTrace();
release();
}
}
/**
* 给其他人发送消息
* 在容器中遍历所有客户端, 并向每一个客户端发送消息(除了自己)
* 私聊
* 格式: @XXX:xxxx
* @param msg 数据
* @param isSys 系统消息(是系统消息就为true, 不是系统消息为false)
*/
private void sendOthers(String msg, boolean isSys) {
boolean isPrivate = msg.startsWith("@"); //数据@开头代表私聊
if(isPrivate) {
int index = msg.indexOf(":"); //判断:索引位置
String name = msg.substring(1,index); //通过索引位置, 确定name
msg = msg.substring(index + 1); //截取索引+1位置及后面的内容, 确定msg
for(Channel c : users) {
if(c.name.equals(name)) { //容器中的姓名和私聊姓名相同
c.send(this.name + "悄悄地对你说:" + msg);
}
}
}else {
for(Channel c : users) {
if(c == this) { //容器中的对象是自己的时候不打印
continue;
}
if(isSys) { //系统消息, 直接打印
c.send(msg);
}else { //不是系统消息, 某某说:什么什么
c.send(name + "说:" + msg);
}
}
}
}
// 发送数据
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
release();
}
}
// 接收数据
private String receive() {
String msg = "";
try {
msg = dis.readUTF();
} catch (IOException e) {
System.out.println("客户端退出");
release();
}
return msg;
}
// 释放资源
private void release() {
isRunning = false;
CloseUtil.close(dos,dis,client);
users.remove(this); //退出后删除容器中对象
sendOthers(name + "离开了聊天室...", true);
}
@Override
public void run() {
while(isRunning) {
String msg = receive();
if(!msg.equals("")) {
//send(msg); 不再向给自己这个客户端发送消息
sendOthers(msg,false); //给所有其他的客户端发送消息
}
}
}
}
}