基于Java Socket写一个多线程的聊天室(附源码)

这篇具有很好参考价值的文章主要介绍了基于Java Socket写一个多线程的聊天室(附源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是socket编程

Socket编程是在TCP/IP上的网络编程,但是Socket在上述模型的什么位置呢。这个位置被一个天才的理论家或者是抽象的计算机大神提出并且安排出来

​ 我们可以发现Socket就在应用程序的传输层和应用层之间,设计了一个Socket抽象层,传输层的底一层的服务提供给Socket抽象层,Socket抽象层再提供给应用层,问题又来了,应用层和Socket抽象层之间和传输层,网络层之间如何通讯的呢,了解这个之前,我们还是回到原点

​ 要想理解Socket编程怎么通过Socket关键词实现服务器和客户端通讯,必须得先了解TCP/IP是怎么通讯的,在这个的基础上再去理解Socket的握手通讯

套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。

​ 当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。

java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。

以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:

  • 服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。
  • 服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
  • 服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。
  • Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。
  • 在服务器端,accept() 方法返回服务器上一个新的 Socket 引用,该 Socket 连接到客户端的 Socket。

多线程的聊天室

  1. 封装 常量类 用于保存所用到的常量
public class Constant {
    //所有成功的常量
    public static final String SUCCESS="SUCCESS";
    //所有失败的常量
    public static final String FAIL="fail";
    //密码
    public static final String DEFAULT_PASSWORD="123";
    //服务器名字
    public static final String SERVER_NAME="server";
    public static final String OK="ok";
    public static final String NO="no";
    //保存在綫的用戶
    public static final Map<String, Socket> USERS = new ConcurrentHashMap<>(8);
}

//选择的功能 用静态常量表示
public class MessageType {
    public static final int TO_SERVER=1;
    public static final int TO_CLIENT=2;
    public static final int TO_ALL=3;
    public static final int LOGIN=4;
    public static final int FROM_SERVER=5;
    public static final int RECEIVE=6;

}
  1. 封装工具类 减少代码冗余
public class MsgUtils {

    //从流 中读取消息
    public static Optional<Message> readMsg(InputStream inputStream) {
        ObjectInputStream ois ;
        try {
            ois = new ObjectInputStream(inputStream);
            //封装成optional 将来外界访问 可以避免 空指针
            return Optional.ofNullable((Message) ois.readObject());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return Optional.empty();
    }
    //从流 中写入消息
    public static void writeMsg(OutputStream outputStream, Message message) {
        ObjectOutputStream oos;
        try {
            oos = new ObjectOutputStream(outputStream);
            oos.writeObject(message);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

//输入
public class ScannerUtil {
    private static final Scanner scanner = new Scanner(System.in);

    public static String input() {
        return scanner.next();
    }


}
  1. 客户端代码(client)
public class Client {

    @SuppressWarnings("all")
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket();
        //连接服务器
        socket.connect(new InetSocketAddress(8848));
        //发消息
        OutputStream outputStream = socket.getOutputStream();
        InputStream inputStream = socket.getInputStream();

        String username = null;

        while (true) {
            //登录
            if (username == null) {
                username = login(outputStream, inputStream);
            } else {
                printOrder();
                String input = ScannerUtil.input();
                switch (Integer.parseInt(input)) {
                    case MessageType.TO_SERVER:
                        sendToServer(username, outputStream, inputStream);
                        break;
                    case MessageType.TO_CLIENT:
                        sendToFriend(username, outputStream, inputStream);
                        break;
                    case MessageType.TO_ALL:
                        sendToAll(username, outputStream, inputStream);
                        break;
                    case MessageType.RECEIVE:
                        receiveMsg(inputStream);
                        break;
                }
            }


        }
    }

    private static void receiveMsg(InputStream inputStream) {
        while (true) {
            Optional<Message> msg = MsgUtils.readMsg(inputStream);
            msg.ifPresent(m -> System.out.println(m.getUsername() + ":" + m.getContent()));
        }
    }

    private static void sendToAll(String username, OutputStream outputStream, InputStream inputStream) {
        while (true) {
            System.out.println(username + ":");
            String msg = ScannerUtil.input();
            MsgUtils.writeMsg(outputStream,
                    new Message(MessageType.TO_ALL, msg, username));
        }

    }

    private static void sendToFriend(String username, OutputStream outputStream, InputStream inputStream) {
        System.out.println("請輸入好友名字");
        String friend = ScannerUtil.input();
        boolean flag = true;
        while (flag) {
            System.out.println(username + ":");
            String msg = ScannerUtil.input();
            if ("bye".equals(msg)) {
                flag = false;
            }
            MsgUtils.writeMsg(outputStream,
                    new Message(MessageType.TO_CLIENT, msg, username, friend));
        }
    }

    //给服务器发消息
    private static void sendToServer(String username, OutputStream outputStream, InputStream inputStream) {
        System.out.println(username + ":");
        String msg = ScannerUtil.input();

        MsgUtils.writeMsg(outputStream,
                new Message(MessageType.TO_SERVER, msg, username));
        Optional<Message> message = MsgUtils.readMsg(inputStream);
        message.ifPresent(m -> System.out.println(m.getUsername() + ":" + m.getContent()));
    }

    private static void printOrder() {
        System.out.println("请选择功能:"
                + MessageType.TO_SERVER + ",给服务器发消息" +
                +MessageType.TO_CLIENT + ",给好友发消息 " +
                MessageType.TO_ALL + ",给群聊发消息 " +
                MessageType.RECEIVE + ",接受消息 ");
    }

    //登录的方法
    private static String login(OutputStream outputStream, InputStream inputStream) {
        System.out.println("请您输入用户名");
        String name = ScannerUtil.input();
        System.out.println("请您输入密码");
        String pwd = ScannerUtil.input();
        Message message = new Message();
        message.setType(MessageType.LOGIN);
        message.setUsername(name);
        message.setPassword(pwd);
        //发给服务器
        MsgUtils.writeMsg(outputStream, message);
        //接受来自服务端的消息
        Optional<Message> msg = MsgUtils.readMsg(inputStream);
        if (msg.isPresent() && Constant.SUCCESS.equals(msg.get().getContent())) {
            return name;
        }
        return null;
    }
}

  1. 服务端代码(server)
public class Server {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        try (
                ServerSocket serverSocket = new ServerSocket();
        ) { //绑定
            serverSocket.bind(new InetSocketAddress(8848));
            System.out.println("服务器开启----8848 port");
            while (true) {
                //监听
                Socket socket = serverSocket.accept();
                new ServerThread(socket).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  1. 保存一个消息对象实体类 实现了 Serializable 可以进行序列化
public class Message  implements Serializable {

//序列化ID
    private static final long serialVersionUID = -9016687226866473553L;
    private Integer type;

    private String content;

    //用户名和密码
    private String username;
    private String password;


    private String friendUserName;



    public Message() {
    }

    public Message( Integer type,String content, String username) {
        this.type = type;
        this.content = content;
        this.username = username;
    }
    public Message( String username, String password,Integer type) {
        this.type = type;
        this.username = username;
        this.password = password;
    }

    public Message(Integer type, String content, String username,  String friendUserName) {
        this.type = type;
        this.content = content;
        this.username = username;
        this.friendUserName = friendUserName;
    }

    public Integer getType() {
        return type;
    }

    public void setType(Integer type) {
        this.type = type;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getFriendUserName() {
        return friendUserName;
    }

    public void setFriendUserName(String friendUserName) {
        this.friendUserName = friendUserName;
    }

    @Override
    public String toString() {
        return "Message{" +
                "type=" + type +
                ", content='" + content + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", friendUserName='" + friendUserName + '\'' +
                '}';
    }
}

  1. 多线程的server端 用于多个用户
public class ServerThread extends Thread {

    private Socket socket;
    public ServerThread(){}
    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {

        try (
                InputStream inputStream = socket.getInputStream();
                OutputStream outputStream = socket.getOutputStream();
        ) {
            while (true) {
                Optional<Message> message = MsgUtils.readMsg(inputStream);
                if (message.isPresent()) {
                    Message msg = message.get();
                    switch (msg.getType()) {
                        case MessageType.TO_SERVER:
                            sendToClient(inputStream, outputStream, msg);
                            break;
                        case MessageType.TO_CLIENT:
                            SendToTarget(msg);
                            break;
                        case MessageType.TO_ALL:
                            SendToAll(msg);
                            break;
                        case MessageType.LOGIN:
                            loginHandler( outputStream, msg, socket);
                            break;
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    private  void sendToClient(InputStream inputStream, OutputStream outputStream, Message message) {
        System.out.println(message.getUsername() + ":" + message.getContent());
        MsgUtils.writeMsg(outputStream, new Message(MessageType.TO_SERVER, Constant.OK, Constant.SERVER_NAME));
    }

    //給目標用戶發消息
    private  void SendToTarget( Message message) {
      //找到對應的socket
        Socket socket = Constant.USERS.get(message.getFriendUserName());
        try {
            MsgUtils.writeMsg(socket.getOutputStream(),message);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private  void SendToAll( Message message) {
        //遍歷所有在綫用戶 拿到他們的socket
    for (Map.Entry<String,Socket>entry:Constant.USERS.entrySet()){
        try {
            MsgUtils.writeMsg(entry.getValue().getOutputStream(),message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    }

    private static void loginHandler(OutputStream outputStream, Message message, Socket socket) {

        if (Constant.USERS.containsKey(message.getUsername())){
            MsgUtils.writeMsg(outputStream, new Message(MessageType.FROM_SERVER, Constant.FAIL, Constant.SERVER_NAME));
            return;
        }
        //判断登陆的逻辑
        if (message.getUsername() == null || !Constant.DEFAULT_PASSWORD.equals(message.getPassword())) {
            MsgUtils.writeMsg(outputStream, new Message(MessageType.FROM_SERVER, Constant.FAIL, Constant.SERVER_NAME));
        } else {

            //登陆成功 放入在线用户map中
            Constant.USERS.put(message.getUsername(), socket);
            System.out.println(message.getUsername() + "--> 登陆成功");
            MsgUtils.writeMsg(outputStream, new Message(MessageType.FROM_SERVER, Constant.SUCCESS, Constant.SERVER_NAME));

        }
    }
}

实现效果

基于Java Socket写一个多线程的聊天室(附源码)

基于Java Socket写一个多线程的聊天室(附源码)文章来源地址https://www.toymoban.com/news/detail-495431.html

到了这里,关于基于Java Socket写一个多线程的聊天室(附源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【你的第一个socket应用】Vue3+Node实现一个WebSocket即时通讯聊天室

    这篇文章主要是用WebSocket技术实现一个 即时通讯聊天室 ,首先先要了解为什么使用WebSocket而不是普通的HTTP协议,如果使用HTTP协议它是下面这种情况: 我发送一条消息,发送一个发送消息的请求;* 一直轮询接收别人发送的消息,不管有没有发送都要定时去调用接口。这里明

    2023年04月20日
    浏览(46)
  • .NET编程——利用C#实现基于Socket类的聊天室(WinForm)

    在学习C#和MySQL实现注册登录和TCP协议的Socket通信后,本文将介绍如何利用Socket类中的异步通信函数来实现本地聊天室功能, Socket通信限制了客户端与客户端之间的通信,客户端只能接收来自服务器的消息而不能接收到客户端发送的消息,因此服务器最佳的选择是起到一个中

    2023年04月21日
    浏览(46)
  • 【Unity 3D】利用C#、Unity和Socket实现简单的在线聊天室工具(附源码 简单易懂)

    需要源码请点赞关注收藏后评论区留言并且私信~~~ 下面利用Unity和C#语言做一个简单的聊天室程序,主要用到的技术就是Socket通信连接,需要一个客户端和一个服务器端,服务器端就使用C#语言的控制台完成 下面就开始搭建C#语言服务器端 1:新建一个C#语言控制台程序 2:命名

    2024年02月05日
    浏览(44)
  • 计算机网络技术与JAVA网络编程手写Socket聊天室-----JAVA入门基础教程-----计算机网络经典

    import java.io.*; import java.net.Socket; import java.util.Scanner; public class ChatClient { public static void main(String[] args) { try { Socket socket = new Socket(\\\"127.0.0.1\\\",9090); new Thread(new Runnable() { @Override public void run() { InputStream inputStream = null; while(true) { try { inputStream = socket.getInputStream(); } catch (IOException e)

    2024年02月15日
    浏览(44)
  • Linux socket聊天室

    目录 一、运行效果 1、分别编译客户端和服务端代码 2、运行 3、使用效果  二、代码 chat.h 服务端代码  客户端代码 gcc client.c -o C -lpthread gcc server.c -o S -lpthread 先运行服务器端,8888为端口号 ./S 8888  再运行客户端,这里创建两个客户端,端口号要和服务端的一样 ./C 127.0.0.1

    2024年01月22日
    浏览(28)
  • 基于 SpringBoot+WebSocket 无DB实现在线聊天室(附源码)

    0.1 样例展示 0.2 源码地址 GitHub:https://github.com/ShiJieCloud/web-chat Gitee:https://gitee.com/suitbaby/web-chat GitCode:I’m Jie / web-chat · GitCode 1.1 HTTP 常用的 HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出

    2024年02月05日
    浏览(33)
  • 【网络编程】利用套接字实现一个简单的网络通信(UDP实现聊天室 附上源码)

    源IP地址(Source IP Address): 源IP地址是数据包发送方(或数据流出发点)的唯一标识符。它用于在互联网或本地网络中定位发送数据包的设备或主机。源IP地址是数据包的出发点,即数据从这个地址开始传送,向目的IP地址指示的设备发送。 在TCP/IP协议中,源IP地址通常由发

    2024年02月14日
    浏览(67)
  • 基于java的聊天室系统设计与实现

    基于java的聊天室系统设计与实现 研究背景: 随着互联网技术的迅速发展和普及,在线聊天和实时通信成为人们日常交流的重要方式。聊天室系统作为实时通信的一种重要形式,具有广泛的应用场景,如远程教育、在线游戏、即时通讯、网络直播等。因此,设计和实现一个基

    2024年02月05日
    浏览(31)
  • 利用Java EE相关技术实现一个简单的Web聊天室系统

    利用Java EE相关技术实现一个简单的Web聊天室系统 (1)编写一个登录页面,登录信息中有用户名和密码,分别用两个按钮来提交和重置登录信息。 (2)通过请求指派来处理用户提交的登录信息,如果用户名为本小组成员的名字且密码为对应的学号时,跳转到LoginSuccess显示聊

    2024年02月07日
    浏览(26)
  • C语言Socket编程TCP简单聊天室

    这是一个使用C语言进行套接字编程实现的简单聊天室, 使用Pthread库进行多线程执行 服务端: svr.c 客户端: cli.c Makefile: Makefile 执行编译 启动服务器 启动客户端 聊天 退出

    2024年02月03日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包