Java网络编程,模拟QQ群聊功能
一、网络编程知识点简介:
1.C/S架构:Client客户端/Server服务器:
- 涉及到的应用:桌面的应用软件,QQ,王者荣耀
- 涉及到的技术:Socket网络编程
2.网络:
各个孤立的工作站或主机相连在一起,从而达到资源共享和通信的目的
2.1 网络分为局域网和广域网:
局域网:
- 在局域网里可以实现数据的共享
- 优点:规模小,容易搭建,传输速度快
- 比如:公司的局域网,学校宿舍里的网络
广域网:
- 因特网就是世界范围内最大的广域网(WWW)
3.网络协议:
- ****TCP(Transmis Control Protocol,传输控制协议)—>(面向连接)
- ****UDP(User Datagram Protocol,用户数据报协议)—>(面向非连接)
4.端口和套接字:
- 端口(port):可以认为是设备与外界通信交流的出口,范围是0-65535之间的整数
- Java提供了IP地址的封装类InetAddress,它位于java.net包中,主要封装ip地址
二、题目要求:
实现QQ群聊功能:
- 客户端之间的聊天,本质是客户端发送一条消息后,由服务器转发到另外的所有客户端
三、简述步骤:
- 1.在服务器TCPServer类中定义List集合用来存放所有连接进来的客户端
- 2.ClientThread的run方法中,当构建好PrintWriter对象后,将对象加入到List集合中
- 3.在接收客户端消息的地方,调用sendMsg方法,将消息发送给除了自己以外的所有的客户端
- 4.完成sendMsg方法
四、代码实现:
1.创建客户端类:
1.1 封装打开客户端的方法
public class TCPClient01 {
public void open() {
try {
//创建客户端,并连接服务器
Socket client = new Socket("localhost",9999);
System.out.println("连接服务器成功!");
//获取客户端输入流的对象
InputStream is = client.getInputStream();
//创建并启动读取服务器发送消息的线程
new ReadFromServer(is).start();
//获取客户端输出流对象
OutputStream os = client.getOutputStream();
//创建并启动向服务器发送消息的线程
new WriteToServer(os).start();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TCPClient01 client = new TCPClient01();
client.open();
}
}
1.2 封装读取服务器发送消息的线程:
class ReadFromServer extends Thread{
//封装服务器发送过来消息的输入流
InputStream is;
public ReadFromServer(InputStream is) {
this.is = is;
}
//重写run方法
@Override
public void run() {
//使用BufferedReader读取服务器的消息
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = null;
try {
//开始读取
while((line=br.readLine())!=null){
System.out.println(line);
}
//关流
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.3 封装客户端向服务器发送消息的线程:
class WriteToServer extends Thread{
OutputStream os;
//客户端向服务器发送消息的输出流
public WriteToServer(OutputStream os) {
super();
this.os=os;
}
//重写run方法
@Override
public void run() {
//创建键盘扫描器
Scanner key = new Scanner(System.in);
//写入消息
PrintWriter pw = new PrintWriter(os);
while(true) {
//接收用户输入的内容
String line = key.nextLine();
//写入
pw.println(line);
//强制刷新
pw.flush();
}
}
}
2.创建服务器类:
2.1创建并启动服务器的方法:
public class TCPServer01 {
//以客户端的昵称作为key值,以发送的消息作为value
//定义一个变量
Map<String,PrintWriter> allout;
public void start() {
//初始化变量
allout = new HashMap<String,PrintWriter>();
try {
//创建服务器
ServerSocket server = new ServerSocket(9999);
System.out.println("服务器启动成功,等待用户接入...");
//创建while循环,保证服务器一直开启并且一直等待客户端连接
while(true) {
//调用accept方法
Socket client = server.accept();
//得到连接的客户端的IP地址
System.out.println("有客户接入,客户的IP为:"+client.getInetAddress());
//启动线程
new ClientThread(client,allout).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TCPServer01 server = new TCPServer01();
server.start();
}
}
2.2 向客户端发送消息,接收客户端发送过来的消息
class ClientThread extends Thread{
//连接到服务器端的客户端
Socket client;
Map<String,PrintWriter> allout;
public ClientThread(Socket client, Map<String, PrintWriter> allout) {
super();
this.client = client;
this.allout = allout;
}
@Override
public void run() {
try {
//向客户端发送数据
//获取输出流对象
OutputStream out = client.getOutputStream();
PrintWriter pw = new PrintWriter(out);
//发消息
pw.println("~~~欢迎来到云轩棋牌聊天室~~~~");
pw.println("请先输入您的聊天昵称:");
//强制刷新
pw.flush();
//接收客户端发送的消息
InputStream in = client.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
//获取昵称
String name = br.readLine();
//将客户端输出流对象加入到集合中
allout.put(name, pw);
String line = null;
while((line=br.readLine())!=null){
//向其他所有的客户端发送消息,pw代表当前自己
sendMsg(name+"说:"+line,pw);
}
//关流
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2.3 向其他所有的客户端发送消息的方法:
private void sendMsg(String line, PrintWriter pw) {
//遍历map集合
Set<String> names = allout.keySet();
for(String name:names){
//根据key获取到对应的value值
PrintWriter p = allout.get(name);
if(p != pw){
p.println(line);
//刷新
p.flush();
}
}
}
}
五、运行结果展示:
*温馨提示:运行时,首先启动服务器,然后启动客户端哟
结语:技术无他,唯有熟尔。知其然,也知其所以然;不要着急,你想要的,岁月都会给你。