Java实现Socket聊天室

一、网络编程是什么?

在网络通信协议下,不同计算机上运行的程序,进行数据传输。

  • 应用场景:即时通讯、网游对战、金融证券、国际贸易、邮件、等等。

不管是什么场景,都是计算机与计算机之间通过网络进行数据传输。

  • Java中可以使用java.net包下的技术轻松开发出常见的网络应用程序。

二、常见的软件架构?

  1. 常见的软件架构有哪些?

    CS/BS。 即Client/Server 和 Browser/Server模式

  2. 通信的软件架构CS/BS的各有什么优缺点和区别?

    CS:客户端服务端模式都需要开发客户端

    BS:浏览器服务器模式不需要开发客户端

    CS:适合定制专业化的办公类软件:IDEA、网游

    BS:适合移动互联网应用,可以在任何地方随时访问系统

三、网络编程三要素?

  • IP:设备在网络中的地址,是唯一的标识
  • 端口号:应用程序在设备中唯一标识
  • 协议:数据在网络中传输的规则,常见的协议游UDP、TCP、HTTP、HTTPS、FTP

四、Socket编程(Java)

Socket(套接字)使用TCP提供了两台计算机之间的通信机制。客户端程序创建一个套接字,并尝试连接服务器的套接字。当连接建立的时候,服务器会创建一个Socket对象。客户端和服务器可以通过对Socket对象写入和读取来进行通信。

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

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

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

在这里插入图片描述

  1. 连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。
  2. TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送。

五、ServerSocket类的方法

服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求。

ServerSocket 类有四个构造方法:

序号方法描述
1public ServerSocket(int port) throws IOException 创建监听特定端口的服务器套接字。
2public ServerSocket(int port, int backlog) throws IOException 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。
3public ServerSocket(int port, int backlog, InetAddress address) throws IOException 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
4public ServerSocket() throws IOException 创建非绑定服务器套接字。

创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。

这里有一些 ServerSocket 类的常用方法:

序号方法描述
1public int getLocalPort() 返回此套接字在其上侦听的端口。
2public Socket accept() throws IOException 侦听并接受到此套接字的连接。
3public void setSoTimeout(int timeout) 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。
4public void bind(SocketAddress host, int backlog) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。

六、Socket 类的方法

java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。

Socket 类有五个构造方法.

序号方法描述
1public Socket(String host, int port) throws UnknownHostException, IOException. 创建一个流套接字并将其连接到指定主机上的指定端口号。
2public Socket(InetAddress host, int port) throws IOException 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
3public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException. 创建一个套接字并将其连接到指定远程主机上的指定远程端口。
4public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException. 创建一个套接字并将其连接到指定远程地址上的指定远程端口。
5public Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字

当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。

下面列出了一些感兴趣的方法,注意客户端和服务器端都有一个 Socket 对象,所以无论客户端还是服务端都能够调用这些方法。

序号方法描述
1public void connect(SocketAddress host, int timeout) throws IOException 将此套接字连接到服务器,并指定一个超时值。
2public InetAddress getInetAddress() 返回套接字连接的地址。
3public int getPort() 返回此套接字连接到的远程端口。
4public int getLocalPort() 返回此套接字绑定到的本地端口。
5public SocketAddress getRemoteSocketAddress() 返回此套接字连接的端点的地址,如果未连接则返回 null。
6public InputStream getInputStream() throws IOException 返回此套接字的输入流。
7public OutputStream getOutputStream() throws IOException 返回此套接字的输出流。
8public void close() throws IOException 关闭此套接字。

例子(一):Socket 实例

1. 服务端

如下的 MySocketServer 是一个服务端程序,该程序通过 socket 连接到服务器并发送一个请求,然后等待一个响应。

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class MySocketServer extends Thread{
    private ServerSocket serverSocket;
    
    public MySocketServer(int port)throws IOException {
        //创建ServerSocket监听端口port
        serverSocket = new ServerSocket(port);
        //设置等待时间:10000毫秒没有监听到Socket连接该端口就报错
        serverSocket.setSoTimeout(10000);
    }
    
    public void run(){
        try{
            //ServerSocket等待连接,链接成功就创建一个与客户端对等通信的socket
            Socket server = serverSocket.accept();
            System.out.println("客户"+server.getLocalAddress()+"连接成功");
            //获取Socket的输入流输出流
            DataInputStream inputStream = new DataInputStream(server.getInputStream());
            DataOutputStream outputStream = new DataOutputStream(server.getOutputStream());
            while(true){
                //从流中获取信息
                String msg = inputStream.readUTF();
                //当客户端传递的信息为ends时,结束。
                if(msg.equals("ends")) break;
                //服务端显示流中的信息
                System.out.println(msg);
            }
            server.close();
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }

    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        try{
            System.out.println("请输入服务端绑定端口:");
            //运行线程让服务端异步运行这样可以让主程序继续干自己的事
            Thread t = new MySocketServer(scanner.nextInt());
            t.run();
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
}

2. 客户端

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class MySocketClient{
    public static void main(String[] args){
        try{
            Scanner keyboard = new Scanner(System.in);
            System.out.println("请输入连接主机的IP地址:");
            String host = keyboard.nextLine();
            System.out.println("输入主机"+host+" 的端口号:");
            int port = keyboard.nextInt();
            //创建Socket并尝试连接 IP=host && 端口=port的服务端
            Socket client = new Socket(host,port);
            InputStream in = client.getInputStream();
            OutputStream out = client.getOutputStream();
            DataInputStream inputStream = new DataInputStream(in);
            DataOutputStream outputStream = new DataOutputStream(out);
            while(true){
                String msg = keyboard.nextLine();
                outputStream.writeUTF(msg);
                //当输入了ends时,客户端关闭socket,服务端也关闭socket,二者结束通信。
                if(msg.equals("ends"))break;
            }
            client.close();
        }catch(Exception ex){
            ex.printStackTrace();
        };
    }
}

3. 测试结果

在这里插入图片描述

例子(二):聊天室

1. 服务端

  • 服务端使用while循环的添加Socket用户
  • 每个用户有自己独立的线程(异步性:使各个用户可以同时输出输入的同时服务端能够继续监听端口)

在这里插入图片描述

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class ChatRoomServer{
    private static ServerSocket serverSocket;
    private static ArrayList<Socket> clientList = new ArrayList<Socket>();

    public static void main(String[] args){
        try{
            serverSocket = new ServerSocket(8888);
            while(true){
                Socket client = serverSocket.accept();
                clientList.add(client);
                //TODO:开启客户端线程,进行异步聊天
                ClientThread ct = new ClientThread(client,clientList);
                ct.start();
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }finally {
            try{
                if(serverSocket != null)serverSocket.close();
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }
}

class ClientThread extends Thread{
    private Socket client = null;
    private ArrayList<Socket> clientList;
    public ClientThread(Socket s,ArrayList<Socket>ss){
        client = s;
        clientList = ss;
    }
    public void run(){
        DataInputStream input = null;
        DataOutputStream output =  null;
        try{
            input = new DataInputStream(client.getInputStream());
            String rec = null;
            String send = null;
            while(true){
                if(!client.isClosed()){
                    rec = input.readUTF();
                    System.out.println("服务端接收到数据:"+rec);
                    clientList.trimToSize();
                    String[] param = rec.split("&");
                    //将输入进行一些封装
                    if("$start$".equals(param[1])){
                        send = param[0] + "进入聊天室";
                    }else{
                        send = param[0] + "说:  " + param[1];
                    }
                    //将非取消信号的数据发送出去
                    if(!("$ends$".equals(param[1]))){
                        for(Socket socket : clientList){
                            if(!socket.isClosed()){
                                output = new DataOutputStream(socket.getOutputStream());
                                output.writeUTF(send);
                            }
                        }
                    }else{
                        for(Socket socket : clientList){
                            if(socket!= client && !socket.isClosed()){
                                output = new DataOutputStream(socket.getOutputStream());
                                output.writeUTF(param[0]+"已退出聊天室");
                            }
                        }
                        output = new DataOutputStream(client.getOutputStream());
                        output.writeUTF("$ends$");
                        client.close();
                        input.close();
                        output.close();
                    }
                }
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
}

2. 客户端

在这里插入图片描述

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;

public class ChatRoomClient {
    public static final String ip = "127.0.0.1";
    public static final int port = 8888;
    public Socket socket = null;
    public DataInputStream input = null;
    public DataOutputStream output = null;
    public Scanner keyboard = new Scanner(System.in);
    public String send;
    public String name;
    public void start(){
        try{
            System.out.println(" ################ 欢迎进入Socket聊天室 ################ ");
            System.out.println("输入您在聊天室的昵称: ");
            name = keyboard.nextLine();
            socket = new Socket(ip,port);
            input = new DataInputStream(socket.getInputStream());
            output = new DataOutputStream(socket.getOutputStream());
            send = name + "&$start$";
            System.out.println(" ################ 进入聊天室成功 ################ ");
            System.out.println("如需退出聊天室,输入'$ends$'即可....");
            output.writeUTF(send);
            //TODO: 编写聊天的线程
            MsgThread mt = new MsgThread(output,name,input);
            mt.start();

            while(true){
                String rec = input.readUTF();
                if("$ends$".equals(rec)){
                    System.out.println(" ################ 退出聊天室成功 ################ ");
                    input.close();output.close();socket.close();
                    System.exit(0);
                }else{
                    System.out.println(rec);
                }
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }finally {
            try{
                if(socket!= null){
                    socket.close();input.close();output.close();
                }
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
    }
    public static void main(String[] args){
        ChatRoomClient client = new ChatRoomClient();
        client.start();
    }
}
class MsgThread extends Thread{
    private DataInputStream input;
    private DataOutputStream output;
    private Scanner keyboard = new Scanner(System.in);
    public static String name;
    public MsgThread(DataOutputStream o,String n,DataInputStream i){
        output = o;input = i;name = n;
    }
    public void run(){
        ChatRoomClient client = new ChatRoomClient();
        try{
            while(true){
                String send = name+"&" + keyboard.nextLine();
                output.writeUTF(send);
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }finally {
            System.out.println("sfef");
        }
    }
}

3. 测试结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/228202.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker网络原理及Cgroup硬件资源占用控制

docker的网络模式 获取容器的进程号 docker inspect -f {{.State.Pid}} 容器id/容器名 docker初始状态下有三种默认的网络模式 &#xff0c;bridg&#xff08;桥接&#xff09;&#xff0c;host&#xff08;主机&#xff09;&#xff0c;none&#xff08;无网络设置&#xff…

Linux C语言 39-进程间通信IPC之管道

Linux C语言 39-进程间通信IPC之管道 本节关键字&#xff1a;C语言 进程间通信 管道 FIFO 相关库函数&#xff1a;pipe、mkfifo、mknod、write、read 什么是管道&#xff1f; 管道通常指“无名管道”&#xff0c;是Unix系统中最古老的IPC通信方式。 管道的分类 管道&#…

L1-027:出租

题目描述 下面是新浪微博上曾经很火的一张图&#xff1a; 一时间网上一片求救声&#xff0c;急问这个怎么破。其实这段代码很简单&#xff0c;index数组就是arr数组的下标&#xff0c;index[0]2 对应 arr[2]1&#xff0c;index[1]0 对应 arr[0]8&#xff0c;index[2]3 对应 arr…

绝地求生:【PC】12月网页活动和特殊空投

亲爱的玩家朋友们&#xff0c;大家好&#xff01; 将为2023年画上圆满句号的大型活动和丰厚奖励已经准备就绪。参与活动即可获得武器皮肤&#xff0c;服装&#xff0c;喷漆&#xff0c;黑货票券在内的奖品&#xff0c;赶快来参与活动获得奖励吧&#xff01; 荣都上线纪念活动 …

JavaSE基础50题:19. 递归求斐波那契数列的第N项。

概述 用递归求斐波那契数列的第N项。 斐波那契数列&#xff1a; 1 1 2 3 5 8 …… f(n) f(n-1) f(n-2) 代码 public class P19 {public static int fibnacio(int n) {if (n 1 || n 2) {return 1;}int tmp fibnacio(n-1) fibnacio(n-2);return tmp;}public static void…

LLM之Agent(三):HuggingGPT根据用户需求自动调用Huggingface合适的模型

​ 浙大和微软亚洲研究院开源的HuggingGPT&#xff0c;又名JARVIS&#xff0c;它可以根据用户的自然语言描述的需求就可以自动分析需要哪些AI模型&#xff0c;然后去Huggingface上直接调用对应的模型&#xff0c;最终给出用户的解决方案。 一、HuggingGPT的工作流程 它的…

六个自媒体写作方法,提升自媒体创作收益

在自媒体时代&#xff0c;写作成为了一个不可或缺的技能。特别是对于新手来说&#xff0c;掌握一些有效的写作方法&#xff0c;可以事半功倍&#xff0c;更好地展现个人创意和观点。在这里&#xff0c;我将分享六个适合新手的自媒体写作方法&#xff0c;希望能够为你在写作之路…

外汇天眼:什么时段做外汇交易最好,有所谓的“最佳时间点”吗?

在外汇交易的时候&#xff0c;很多手动交易的投资者不知道到底什么时间段操作交易最适合自己。 我们在进行选择最佳交易时间的时候&#xff0c;一定要明白各时间段的全球各个市场的交易状况&#xff0c;这样你才能分配好自己的时间。 当然在通过技术分析与基本分析选择好币种后…

刚刚,深兰科技在国际顶级会议EMNLP竞赛中再夺两冠

12月6日至10日&#xff0c;自然语言处理领域的国际顶级会议EMNLP(自然语言处理中的经验方法会议)在新加坡召开&#xff0c;研究人员、学者和业界专业人士齐聚一堂&#xff0c;展示和讨论该领域的最新研究成果、进展和创新。 会上公布了各项任务竞赛的获奖名单&#xff0c;深兰科…

十分钟带你搞懂Python那啥

Python爬虫的用处就不需要我多说了吧&#xff0c;今天就来带大家十分钟快速学会Python是如何爬取网页信息的&#xff0c;当然大家在爬取目标网页内容之前一定要遵守该网页的爬虫规则&#xff0c;以免带来不必要的麻烦&#xff0c;因而本次的示例所爬取的网页也是自己的本地网站…

【Vulnhub 靶场】【Funbox: GaoKao】【简单】【20210606】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/funbox-gaokao,707/ 靶场下载&#xff1a;https://download.vulnhub.com/funbox/FunboxGaoKao.ova 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年06月06日 文件大小&#xff1a;1.3 GB 靶场作者&#…

专业130+总分400+云南大学通信847专业基础综考研经验(原专业课827)

今年专业130总分400云南大学通信上岸&#xff0c;整体考研感觉还是比较满意&#xff0c;期间也付出了很多心血&#xff0c;走过弯路&#xff0c;下面分享一下这一年考研得失&#xff0c;希望大家可以从中有所借鉴。 先说明我在考研报名前更换成云南大学的理由&#xff1a;&…

通过项目管理软件监管新员工入职流程的方法与策略

项目管理软件是什么&#xff1f;项目管理软件都能做什么&#xff1f;是不是只有项目团队需要啊&#xff1f;NO&#xff01;项目管理软件乍听其名不免让人觉得这不过是个项目领域的专用工具。 那什么是项目呢&#xff1f;项目是为创造独特的产品、服务或成果而进行的体系化的工…

Qt内存管理、UI编辑器、客制化组件、弹出对话框、常用部件类

头文件的小技巧 #include <QtWidgets> // 在自动生成的 .h 里面加上此句 适用条件&#xff1a; QT 的内存管理 当父窗体被关闭时&#xff0c;子部件的内存会自动释放。 对象树是一种管理对象生命周期的机制。当一个对象被添加到另一个对象的子对象列表中时&#xff0…

机器学习应用 | 使用 MATLAB 进行异常检测(下)

在使用MATLAB 进行异常检测&#xff08;上&#xff09;中&#xff0c;我们探讨了什么是异常值&#xff0c;简单的一维数据异常检测问题&#xff0c;针对高维数据的有监督异常检测方法。 在&#xff08;下&#xff09;篇中&#xff0c;我们将和大家一起探讨无监督异常检测。 没…

“身份证信息批量核验:高效解决管理难题,轻松提升工作效率“

尊敬的读者们&#xff0c;您是否曾经因为身份证信息的核验而感到烦恼&#xff1f;是否曾经因为手动核验而感到繁琐和耗时&#xff1f;现在&#xff0c;我们向您介绍一款全新的工具——身份证信息批量核验器&#xff0c;它将帮助您一键解决管理难题&#xff0c;让工作事半功倍&a…

netty源码:(9)ChannelId

ChannelId代表了一个Channel的全局标识符。它提供了asShortText和asLongText两个方法来返回对应的字符串

C语言printf的输出格式大全及颜色字体打印

不同类型的数据有不同形式的打印格式&#xff0c;熟知了printf的不同输出格式会让后边的学习事半功倍。 文章目录 %d%i打印十进制整形数据标志&#xff08;flags&#xff09;%u打印无符号十进制%o 打印无符号八进制%x %X打印大写或小写十六进制%f打印浮点数%e %E科学计数法打印…

Java几种深拷贝方式比较

Java的深度拷贝分为克隆&#xff08;实现Java的Clone接口&#xff09;和序列化&#xff08;实现Java的Serializable接口&#xff09;两种&#xff0c;由于序列化有不同的方式&#xff0c;下面分析一下每种的注意事项和性能对比。 一、继承Cloneable接口 可以使用Java提供的Clo…

Java基础50题: 21.实现一个方法printArray, 以数组为参数,循环访问数组中的每个元素,打印每个元素的值.

概述 实现一个方法printArray, 以数组为参数,循环访问数组中的每个元素,打印每个元素的值. 代码 public static void printArray(int[] array) {for (int i 0; i < array.length; i) {System.out.println(array[i] " ");}System.out.println();}public static…