您现在的位置是:网站首页 > 代码编程 > JAVA开发JAVA开发
【原】Java Socket编写基于TCP协议的简易聊天室
不忘初心 2019-03-27 围观() 评论() 点赞() 【JAVA开发】
简介:Java提供了Socket套接字来实现网络编程,对TCP和UDP协议都有很好的的支持,在学习的时候,写的最多的可能就是聊天室了,很简单,但是能很好的将Socke
Java提供了Socket套接字来实现网络编程,对TCP和UDP协议都有很好的的支持,在学习的时候,写的最多的可能就是聊天室了,很简单,但是能很好的将Socket和多线程结合起来。
好久没写了,这几天在复习Socket知识点的时候,顺手也写了一个基于TCP协议的简易聊天室:
服务端:负责消息转发和广播;
客户端:发送消息,接收消息;
服务器代码:
package com.wolffy.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* Created by SongFei on 2019/3/24.
*/
public class Server {
/**
* 客户端集合
*/
private static List<Socket> clients = new ArrayList<>();
private void start() {
try {
// 开启服务,设置指定端口
ServerSocket server = new ServerSocket(5555);
System.out.println("服务开启,等待客户端连接中...");
// 循环监听
while (true) {
// 等待客户端进行连接
Socket client = server.accept();
System.out.println("客户端[" + client.getRemoteSocketAddress() + "]连接成功,当前在线用户" + clients.size() + "个");
// 将客户端添加到集合
clients.add(client);
// 每一个客户端开启一个线程处理消息
new MessageListener(client).start();
}
} catch (IOException e) {
// log
}
}
/**
* 消息处理线程,负责转发消息到聊天室里的人
*/
class MessageListener extends Thread {
// 将每个连接上的客户端传递进来,收消息和发消息
private Socket client;
// 将这几个变量抽出来公用,避免频繁new对象
private OutputStream os;
private PrintWriter pw;
private InputStream is;
private InputStreamReader isr;
private BufferedReader br;
public MessageListener(Socket socket) {
this.client = socket;
}
@Override
public void run() {
try {
// 每个用户连接上了,就发送一条系统消息(类似于广播)
sendMsg(0, "[系统消息]:欢迎" + client.getRemoteSocketAddress() + "来到聊天室,当前共有" + clients.size() + "人在聊天");
// 循环监听消息
while (true) {
sendMsg(1, "[" + client.getRemoteSocketAddress() + "]:" + receiveMsg());
}
} catch (IOException e) {
// log
}
}
/**
* 发送消息
*
* @param type 消息类型(0、系统消息;1、用户消息)
* @param msg 消息内容
* @throws IOException
*/
private void sendMsg(int type, String msg) throws IOException {
if (type != 0) {
System.out.println("处理消息:" + msg);
}
for (Socket socket : clients) {
if (type != 0 && socket == client) {
continue;
}
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.println(msg);// 这里需要特别注意,对方用readLine获取消息,就必须用print而不能用write,否则会导致消息获取不了
pw.flush();
}
}
/**
* 接收消息
*
* @return 消息内容
* @throws IOException
*/
private String receiveMsg() throws IOException {
is = client.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
return br.readLine();
}
}
public static void main(String[] args) {
new Server().start();
}
}
服务器需要不停的监听客户端连接,并且负责消息的转发,本来还需要负责心跳,但是我这里偷懒就没有写。
客户端代码:
package com.wolffy.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;
/**
* Created by SongFei on 2019/3/24.
*/
public class Client {
private Socket server = null;
private OutputStream os;
private PrintWriter pw;
private InputStream is;
private InputStreamReader isr;
private BufferedReader br;
private void start() {
try {
// 连接服务器
server = new Socket("127.0.0.1", 5555);
System.out.println("连接服务器成功,身份证:" + server.getLocalSocketAddress());
// 启动接受消息的线程
new ReceiveMessageListener().start();
// 启动发送消息的线程
new SendMessageListener().start();
} catch (SocketException e) {
System.out.println("服务器" + server.getRemoteSocketAddress() + "嗝屁了");
} catch (IOException e) {
// log
}
}
/**
* 发送消息的线程
*/
class SendMessageListener extends Thread {
@Override
public void run() {
try {
// 监听idea的console输入
Scanner scanner = new Scanner(System.in);
// 循环处理,只要有输入内容就立即发送
while (true) {
sendMsg(scanner.next());
}
} catch (SocketException e) {
System.out.println("服务器" + server.getRemoteSocketAddress() + "嗝屁了");
} catch (IOException e) {
// log
}
}
}
/**
* 接受消息的线程
*/
class ReceiveMessageListener extends Thread {
@Override
public void run() {
try {
// 循环监听,除非掉线,或者服务器宕机了
while (true) {
System.out.println(receiveMsg());
}
} catch (SocketException e) {
System.out.println("服务器" + server.getRemoteSocketAddress() + "嗝屁了");
} catch (IOException e) {
// log
}
}
}
/**
* 发送消息
*
* @param msg 消息内容
* @throws IOException
*/
private void sendMsg(String msg) throws IOException {
os = server.getOutputStream();
pw = new PrintWriter(os);
pw.println(msg);// 这里需要特别注意,对方用readLine获取消息,就必须用print而不能用write,否则会导致消息获取不了
pw.flush();
}
/**
* 接受消息
*
* @return 消息内容
* @throws IOException
*/
private String receiveMsg() throws IOException {
is = server.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
return br.readLine();
}
public static void main(String[] args) {
new Client().start();
}
}
客户端就简单许多,它不需要消息的转发,只仅仅是消息的发送和接受即可。
服务器代码运行效果图如下:
客户端代码运行效果图如下:
如果服务器关闭或宕机了,需要通知到客户端:
监听客户端下线的实现方式有两种:
1、允许输入指定消息退出,比如:输入exit下线;
2、利用TCP的keepAliave和timeOut来判定是否下限(心跳机制在服务器端);
3、启动单独的 线程来监听心跳,如果client在指定的时间没有心跳,就直接将其剔除下线(心跳机制在客户端);
上述三种方式中,第二种需要谨慎使用,因为服务器要负责消息的转发,压力本来就很大了,如果再将发送频率很高的心跳也放到服务器上,用户量上来之后,服务器搞不好会扛不住这个压力,所以最好还是使用一和三,这种由客户端发起请求,服务器的压力会小很多。
看完文章,有任何疑问,请加入群聊一起交流!!!
很赞哦! ()
相关文章
- Java Socket实现心跳机制的代码案例详解
- Java Socket编写基于UDP协议的简易聊天室
- log4j使用之SocketAppender教程
- IntelliJ IDEA 2024.2发布之后强推新UI,如何恢复老的经典UI界面
- Uninstall hasn't detected folder of intelli] lDEA installation. Probablyuninstall.exe was moved from the installation folder.
- 公众号CPS变现新宠:微赚淘客查券返利机器人,开启智能省钱赚钱新时代
- 高返利优惠券公众号推荐
- 返利公众号可信吗安全吗?返利机器人哪个佣金高?
- 唯品会购物返利公众号大揭秘:哪些真正好用的返利公众号?
- 饿了么优惠券免费领取:哪些公众号值得推荐?
标签云
猜你喜欢
- IntelliJ IDEA 2019.2已经可以利用补丁永久破解激活了
- IntelliJ IDEA 2019.3利用补丁永久破解激活教程
- IntelliJ IDEA高版本最灵活的永久破解激活方法(含插件激活,时长你说了算)
- Jetbrains全家桶基于ja-netfilter的最新破解激活详细图文教程
- IntelliJ IDEA 2022.1永久破解激活教程(亲测可用,持续更新)
- 分享几个正版 IntelliJ IDEA 激活码(破解码、注册码),亲测可用,持续更新
- ja-netfilter到底需不需要mymap,2021.3.2版本激活失效?
- 如何激活idea2022.1及以上版本中的插件(亲测可用)
- 【史上最全】IntelliJ IDEA最新2022.1版本安装和激活视频教学(含插件)
- IntelliJ IDEA 2022.2 版本最新2099年永久激活方法,亲测可用,也可以开启新UI了。
站点信息
- 网站程序:spring + freemarker
- 主题模板:《今夕何夕》
- 文章统计:篇文章
- 标签管理:标签云
- 微信公众号:扫描二维码,关注我们