如何使用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天

手把手教你用Java语言在IdeaAndroid中分别建立服务端客户端实现局域网聊天

目录

基本实现

  • 实现客户端和服务端之间的通信

  • 实现服务端转接客户端消息,并发送给其他局域网在线成员

  • 实现服务端接收客户端消息,并判断相应类型,做出对应应答

  • 实现客户端消息发送者 发送时间 当前在线用户基本可视化


问题分析

  1. 服务端开发

    • 在IntelliJ IDEA中创建一个Java项目。
    • 实现一个简单的TCP服务器,能够接收客户端消息并回显(或广播)消息给所有已连接的客户端。
  2. 客户端开发

    • 在Android Studio中创建一个Android项目。
    • 实现一个简单的TCP客户端,能够发送消息到服务端并显示从服务端接收到的消息。
  3. 网络通信

    • 确保服务端和客户端在同一局域网内,并且客户端可以正确连接到服务端。
    • 处理多线程问题,确保服务端可以同时处理多个客户端连接。

服务端

Idea:

结构预览

在这里插入图片描述

在Idea中创建一个名为Server的类

Server类
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class Server {
   
   
    // 定义一个集合容器存储所有登陆进来的客户端,以便群发消息给他们
    // 定义一个Map集合,键是存储客户端的管道,值是这个管道的名称
    public static final Map<Socket, String> onLineSockets = new HashMap<>();

    public static void main(String[] args) throws Exception {
   
   
        System.out.println("服务端启动");
        // 1. 创建服务端ServerSocket对象,绑定端口号,监听客户端连接
        ServerSocket serverSocket = new ServerSocket(9999);

        while (true) {
   
   
            System.out.println("等待客户端连接....");

            Socket socket = serverSocket.accept();
            new ServerReader(socket).start();

            System.out.println("一个客户端上线了....  IP:" + socket.getInetAddress().getHostAddress());
        }
    }
}
代码解读
  • 定义一个Map集合(所有局域网用户共享集合)存储所有登陆进来的客户端,以便群发消息给他们,是存储客户端的管道,是这个管道的名称(说白了就是前一个是主键,后一个)
public static final Map<Socket, String> onLineSockets = new HashMap<>();
  • 创建服务端ServerSocket对象,绑定端口号,监听客户端连接
ServerSocket serverSocket = new ServerSocket(9999);
  • 端口号选择建议范围**(1024~65535)**,其中绝大多数没有被使用
while (true) {
   
   
            System.out.println("等待客户端连接....");

            Socket socket = serverSocket.accept();
            new ServerReader(socket).start();

            System.out.println("一个客户端上线了....  IP:" + socket.getInetAddress().getHostAddress());
        }
  • 使用无限循环,持续监听新的连接

  • **serverSocket.accept()**会堵塞线程,等待连接请求,直到收到一个新的请求,并与客户端建立新的通信管道用来传输数据

  • **new ServerReader(socket).start()**在建立新的管道后,会建立一个新线程用来与管道对应的客户端通信,这样就能实现多客户端之间通信

  • **socket.getInetAddress().getHostAddress()**用于获取新建连接的客户端Ip,并在Server类终端打印,便于服务端查看连接信息


在Idea中创建一个名为ServerReader的类

注:本文所有读取发送都使用特殊流DataInputStreamDataOutputStream

ServerReader类
import java.io.*;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;

public class ServerReader extends Thread {
   
   

    private Socket socket;

    public ServerReader(Socket socket) {
   
   
        this.socket = socket;
    }

    @Override
    public void run() {
   
   
        try {
   
   
            DataInputStream dis = new DataInputStream(socket.getInputStream());
            while (true) {
   
   
                int type = dis.readInt(); // 1 2
                switch (type) {
   
   
                    case 1:
                        String nickname = dis.readUTF();
                        // 登陆成功,将客户端socket存入在线集合
                        Server.onLineSockets.put(socket, nickname);
                        // 更新全部客户端的在线人数列表
                        updateClientOnLineUserList();
                        break;
                    case 2:
                        String msg = dis.readUTF();
                        sendMsgToAll(msg);
                        break;
                    default:
                        System.out.println("未知的消息类型: " + type);
                        break;
                }
            }
        } catch (Exception e) {
   
   
            System.out.println("客户端断开连接  IP:" + socket.getInetAddress().getHostAddress() + " 时间: " + LocalDateTime.now());
            Server.onLineSockets.remove(socket); // 把下线的客户端socket从在线集合中移除
            updateClientOnLineUserList();
        }
    }

    // 给全部在线socket推送当前客户端发来的消息
    private void sendMsgToAll(String msg) {
   
   
        StringBuilder sb = new StringBuilder();
        String name = Server.onLineSockets.get(socket);
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter dft = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EEE a");
        String nowStr = dft.format(now);

        String msgResult = sb.append(name).append(" ").append(nowStr).append("\r\n").append(msg).append("\r\n").toString();

        // 推送给全部客户端
        for (Socket clientSocket : Server.onLineSockets.keySet()) {
   
   
            try {
   
   
                DataOutputStream dos = new DataOutputStream(clientSocket.getOutputStream());
                dos.writeInt(2); // 1代表告诉客户端接下来是在线人数列表信息 2代表发的是群聊消息
                dos.writeUTF(msgResult);
                dos.flush();
            } catch (IOException e) {
   
   
                e.printStackTrace();
            }
        }
    }
    // 更新全部客户端的在线人数列表
    private void updateClientOnLineUserList() {
   
   
        // 拿到全部在线客户端的用户名称,把这些名称转发给全部在线socket管道
        Collection<String> onLineUsers = Server.onLineSockets.values();
        for (Socket clientSocket : Server.onLineSockets.keySet()) {
   
   
            try {
   
   
                DataOutputStream dos = new DataOutputStream(clientSocket.getOutputStream());
                dos.writeInt(1); // 1代表告诉客户端接下来是在线人数列表信息 2代表发的是群聊消息
                dos.writeInt(onLineUsers.size());
                for (String onLineUser : onLineUsers) {
   
   
                    dos.writeUTF(onLineUser);
                }
                dos.flush
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值