关键词:
需求:模拟聊天室群聊
客户端要先登录,登录成功之后才能发送和接收消息
分析:
- 服务器端,需要为每个客户端开启一个线程通信,这样才能实现多个客户端“同时”与服务器通信
- 客户端,需要把收消息功能与发消息功能分开两个线程,这样才能“同时收发”,即可以发消息,也可以接收其他客户端的聊天消息
- 服务器端要分别处理客户端的“登录”、“退出”、“聊天”的消息,所以这里设计了Code常量类,用状态值区分“登录”、“退出”、“聊天”
- 这里设计Message类,包含code属性,区别是“登录”、“退出”、“聊天”,username属性表示用户名,表明消息是谁发的,content属性,存储消息内容,如果是登录,就用来存储密码。
- 这里消息是Message对象,因此在客户端与服务器之间传输的是对象,所以选择ObjectOutputStream和ObjectInputStream。
- 这里的Message类与Code类是服务器端和客户端“共享”的,要保持一致。特别注意包名和序列化版本ID。
Message 类与 Code 类的示例代码:
1 //Code
2 public class Code
3 public static final int LOGIN = 1;
4 public static final int CHAT = 2;
5 public static final int LOGOUT = 3;
6
7 public static final int SUCCESS = 1;
8 public static final int FAIL = 2;
9
10
11
12 // Message
13 public class Message implements Serializable
14 private static final long serialVersionUID = 1L;
15 private int code;
16 private String username;
17 private String content;
18 public Message(int code, String username, String content)
19 super();
20 this.code = code;
21 this.username = username;
22 this.content = content;
23
24
25 public Message()
26 super();
27
28
29 public int getCode()
30 return code;
31
32 public void setCode(int code)
33 this.code = code;
34
35 public String getUsername()
36 return username;
37
38 public void setUsername(String username)
39 this.username = username;
40
41 public String getContent()
42 return content;
43
44 public void setContent(String content)
45 this.content = content;
46
47
服务器端用户管理类代码:
1 import java.util.HashMap;
2
3 public class UserManager
4 public static HashMap<String,String> allUsers = new HashMap<String,String>();
5 static
6 allUsers.put("gangge", "123");
7 allUsers.put("xiaobai", "456");
8 allUsers.put("gujie", "789");
9
10
11 public static boolean login(String username, String password)
12 if(allUsers.get(username)!=null && allUsers.get(username).equals(password))
13 return true;
14 else
15 return false;
16
17
18
服务器端实例代码:
1 import java.net.ServerSocket;
2 import java.net.Socket;
3
4 public class Server
5 public static void main(String[] args)throws Exception
6 @SuppressWarnings("resource")
7 ServerSocket server = new ServerSocket(9999);
8
9 while(true)
10 Socket socket = server.accept();
11
12 ClientHandlerThread ct = new ClientHandlerThread(socket);
13 ct.start();
14
15
16
服务器端处理消息的线程类代码:
1 import java.io.IOException;
2 import java.io.ObjectInputStream;
3 import java.io.ObjectOutputStream;
4 import java.net.Socket;
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.HashSet;
8 import java.util.Set;
9
10 import com.tcp.chat.bean.Code;
11 import com.tcp.chat.bean.Message;
12
13 public class ClientHandlerThread extends Thread
14 public static Set<ObjectOutputStream> online = Collections.synchronizedSet(new HashSet<ObjectOutputStream>());
15
16 private Socket socket;
17 private String username;
18 private ObjectInputStream ois;
19 private ObjectOutputStream oos;
20
21 public ClientHandlerThread(Socket socket)
22 super();
23 this.socket = socket;
24
25
26 public void run()
27 Message message = null;
28 try
29 ois = new ObjectInputStream(socket.getInputStream());
30 oos = new ObjectOutputStream(socket.getOutputStream());
31
32 //接收数据
33 while (true)
34 message = (Message) ois.readObject();
35
36 if(message.getCode() == Code.LOGIN)
37 //如果是登录,则验证用户名密码
38 username = message.getUsername();
39 String password = message.getContent();
40 if(UserManager.login(username, password))
41 message.setCode(Code.SUCCESS);
42 oos.writeObject(message);
43
44 //并将该用户添加到在线人员名单中
45 online.add(oos);
46
47 message.setCode(Code.CHAT);
48 message.setContent("上线了");
49 //通知其他人,xx上线了
50 sendToOther(message);
51 else
52 message.setCode(Code.FAIL);
53 oos.writeObject(message);
54
55 else if(message.getCode() == Code.CHAT)
56 //如果是聊天信息,把消息转发给其他在线客户端
57 sendToOther(message);
58 else if(message.getCode() == Code.LOGOUT)
59 //通知其他人,xx下线了
60 message.setContent("下线了");
61 sendToOther(message);
62 break;
63
64
65 catch(Exception e)
66 //通知其他人,xx掉线了
67 if(message!=null && username!=null)
68 message.setCode(Code.LOGOUT);
69 message.setContent("掉线了");
70 sendToOther(message);
71
72 finally
73 //从在线人员中移除并断开当前客户端
74 try
75 online.remove(oos);
76 socket.close();
77 catch (IOException e)
78 e.printStackTrace();
79
80
81
82
83 private void sendToOther(Message message)
84 ArrayList<ObjectOutputStream> offline = new ArrayList<ObjectOutputStream>();
85 for (ObjectOutputStream on : online)
86 if(!on.equals(oos))
87 try
88 on.writeObject(message);
89 catch (IOException e)
90 offline.add(on);
91
92
93
94
95 for (ObjectOutputStream off : offline)
96 online.remove(off);
97
98
99
客户端示例代码:
1 import java.io.ObjectInputStream;
2 import java.io.ObjectOutputStream;
3 import java.net.Socket;
4 import java.util.Scanner;
5
6 import com.tcp.chat.bean.Code;
7 import com.tcp.chat.bean.Message;
8
9 public class Client
10 public static void main(String[] args) throws Exception
11 Socket socket = new Socket("192.168.1.107", 9999);
12
13 ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
14 ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
15
16 //先登录
17 Scanner scanner = new Scanner(System.in);
18 String username;
19 while(true)
20 //输入登录信息
21 System.out.println("用户名:");
22 username = scanner.nextLine();
23 System.out.println("密码:");
24 String password = scanner.nextLine();
25
26 Message msg = new Message(Code.LOGIN, username, password);
27 //发送登录数据
28 oos.writeObject(msg);
29 // 接收登录结果
30 msg = (Message) ois.readObject();
31 if(msg.getCode() == Code.SUCCESS)
32 System.out.println("登录成功!");
33 break;
34 else if(msg.getCode() == Code.FAIL)
35 System.out.println("用户名或密码错误,登录失败,重新输入");
36
37
38
39 //启动收消息和发消息线程
40 SendThread s = new SendThread(oos,username);
41 ReceiveThread r = new ReceiveThread(ois);
42 s.start();
43 r.start();
44
45 s.join();//不发了,就结束
46 r.setFlag(false);
47 r.join();
48
49 scanner.close();
50 socket.close();
51
52
客户端发消息线程类代码:
1 import java.io.IOException;
2 import java.io.ObjectOutputStream;
3 import java.util.Scanner;
4
5 import com.tcp.chat.bean.Code;
6 import com.tcp.chat.bean.Message;
7
8 public class SendThread extends Thread
9 private ObjectOutputStream oos;
10 private String username;
11
12 public SendThread(ObjectOutputStream oos,String username)
13 super();
14 this.oos = oos;
15 this.username = username;
16
17
18 public void run()
19 try
20 Scanner scanner = new Scanner(System.in);
21 while(true)
22 System.out.println("请输入消息内容:");
23 String content = scanner.nextLine();
24 Message msg;
25 if("bye".equals(content))
26 msg = new Message(Code.LOGOUT, username, content);
27 oos.writeObject(msg);
28 scanner.close();
29 break;
30 else
31 msg = new Message(Code.CHAT, username, content);
32 oos.writeObject(msg);
33
34
35 catch (IOException e)
36 e.printStackTrace();
37
38
39
客户端接受消息线程类代码:
1 import java.io.ObjectInputStream;
2
3 import com.tcp.chat.bean.Message;
4
5 public class ReceiveThread extends Thread
6 private ObjectInputStream ois;
7 private volatile boolean flag = true;
8
9 public ReceiveThread(ObjectInputStream ois)
10 super();
11 this.ois = ois;
12
13 public void run()
14 try
15 while(flag)
16 Message msg = (Message) ois.readObject();
17 System.out.println(msg.getUsername() + ":" + msg.getContent());
18
19 catch (Exception e)
20 System.out.println("请重新登录");
21
22
23 public void setFlag(boolean flag)
24 this.flag = flag;
25
26
27
注意:
以上案例的网络通信程序是基于阻塞式API的,所以服务器必须为每个客户端都提供一条独立线程进行处理,当服务器需要同时处理大量客户端时,这种做法会导致性能下降。如果要开发高性能网络服务器,那么需要使用Java提供的NIO API,可以让服务器使用一个或有限几个线程来同时处理连接到服务器上的所有客户端。
java网络编程案例三:多个客户端上传文件(代码片段)
需求:每一个客户端启动后都可以给服务器上传一个文件,服务器接收到文件后保存到一个upload目录中。可以同时接收多个客户端的文件上传。 分析: (1)服务器端要“同时”处理多个客户端的请求,那么必... 查看详情
第97题java高级技术-网络编程16(简易聊天室11:实现客户端群聊)(代码片段)
回城传送–》《JAVA筑基100例》文章目录零、前言一、题目描述二、解题思路三、代码详解四、推荐专栏五、示例源码下载零、前言今天是学习JAVA语言打卡的第97天,每天我会提供一篇文章供群成员阅读(不需要订阅付... 查看详情
java基础增强:网络编程tcpudp案例
一入编程深似海,从此妹子是路人。1、Udp的客户端,服务端-----DatagramSocket通过Udp协议编写一个群聊室,具体实现代码如下:packageday.by.day.udp.charroom;//客户端代码importjava.net.DatagramPacket;importjava.net.DatagramSocket;importjava.net.InetAddress;i... 查看详情
javasocket编程实现群聊(超详细)(代码片段)
...效果文末有完整代码,创作不易,点个赞再走吧~客户端之间的交流有人退出群聊时,减少在线人数实现流程1、项目结构即原理分析功能实现多客户之间聊天实时统计在线人数图形化界面创建昵称下线通知项目结构原... 查看详情
第97题java高级技术-网络编程16(简易聊天室11:实现客户端群聊)
回城传送–》《JAVA筑基100例》文章目录零、前言一、题目描述二、解题思路三、代码详解四、推荐专栏五、示例源码下载零、前言今天是学习JAVA语言打卡的第97天,每天我会提供一篇文章供群成员阅读(不需要订阅付钱),... 查看详情
十.netty入门到超神系列-基于netty群聊系统(代码片段)
...加强Netty的熟练度,案例的效果是服务端可以广播某客户端的消息给所有客户端。每个客户端监听键盘输入来获取消息,然后发送给服务端。服务端服务端一样的需要创建BossGroup和WorkGroup,然后使用ServerBootStrap来配置Netty和... 查看详情
java编程实例-tcp聊天室代码实现(代码片段)
Tcp多人聊天室实现客户端:功能:数据发送数据接收技术:socket输入流和输出流多线程,客户端功能模块有两个线程聊天方式:群聊私聊私聊方法:@服务器用户id号:msg服务器:功能:数据转发用户注册技术:ServerSocket每一个用户对应的Sock... 查看详情
第96题java高级技术-网络编程15(简易聊天室10:一个服务器与多个客户端通信)(代码片段)
回城传送–》《JAVA筑基100例》文章目录零、前言一、题目描述二、解题思路三、代码详解四、推荐专栏五、示例源码下载零、前言今天是学习JAVA语言打卡的第96天,每天我会提供一篇文章供群成员阅读(不需要订阅付... 查看详情
java网络多线程专题(代码片段)
...络通信编程应用案例(使用字节流)服务端代码客户端代码:TCP网络通信编程应用案例2服务端代码:客户端代码 查看详情
linux下基于tcp协议的群聊系统设计(多线程+select)(代码片段)
...一个简单的群聊天程序。这个例子可以学习、巩固Linux下网络编程相关知识点练习Linux下socket、TCP编程练习Linux下pthread、线程编程练习Linux下多路IO检测、select函数使用练习C语言链表使用练习线程间同步与互斥、互斥锁mutex的使用... 查看详情
12.7网络编程案例(代码片段)
12.7网络编程案例 12.7.1TCP多线程时间服务器主要功能: 子类化方式继承QTcpServer与QThread,创建TCP服务器,等待客户端连接,连上新的客户端之后,服务器获取本地时间,将时间发送给客户端,再断开连接,销毁线程。(配套... 查看详情
java中socket网络通信,实现客户端上传文件案例(代码片段)
服务端代码/*文件上传案例服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写"上传成功"明确:数据源:客户端上传的文件目的地:服务器的硬盘d:\\\\upload\\\\1.jpg实现步骤:1.创建一个服务器ServerSocket对象,和系... 查看详情
java---网络编程(代码片段)
文章目录网络编程1.网络编程入门1.1软件结构1.2网络通信协议1.3协议分类1.4网络编程三要素2.TCP通信程序2.1概述2.2Socket类2.3ServerSocket类2.4简单的TCP网络程序3.综合案例3.1文件上传案例网络编程1.网络编程入门1.1软件结构C/S结构:... 查看详情
网络i/o编程模型16netty框架实现的群聊系统(代码片段)
一背景描述1.编写一下群聊系统:实现服务器端和客户端之间数据通讯(非阻塞模式)服务端:可以检测用户上线,离线,并实现消息转发功能。客户端:通过channel可以无阻塞发送消息给其他所用用户&... 查看详情
网络--tcp概述三次握手四次挥手粘包分包数据分段(代码片段)
...网络传输本身就是不可靠的;可能丢包或者数据重复.案例:客户端发送了3个数据包,经过传输到达服务器端,但是只收到了3和1的包;数据包2丢包了发送和接收的次序出现问题,客户端先发的数据包1,但服务器先收到的是数据包3;网络中... 查看详情
io流中「线程」模型总结(代码片段)
...数据-业务执行-应答写数据」的形式;Java提供「三种」IO网络编程模型,即:「BIO同步阻塞」、「NIO同步非阻塞」、「AIO异步非阻塞」;二、同步阻塞1、模型图解BIO即同步阻塞,服务端收到客户端的请求时,会启动一个线程处理... 查看详情
tcp案例代码(代码片段)
文章目录TCP网络编程的案例代码普通服务端/客户端代码多线程方式代码TCP网络编程的案例代码描述客户端与服务端的通信普通服务端/客户端代码服务端importjava.io.*;importjava.net.ServerSocket;importjava.net.Socket;/***服务端*/publicclassServerSo... 查看详情
java网络编程案例二:多态客户端与服务器之间的多次通信
通常情况下,服务器不应该只接受一个客户端请求,而应该不断地接受来自客户端的所有请求,所以Java程序通常会通过循环,不断地调用ServerSocket的accept()方法。 如果服务器端要“同时”处理多个客户端的请求,... 查看详情