文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

【JavaEE初阶】 TCP服务器与客户端的搭建

2023-12-22 19:40

关注

🌲前言

TCP服务器与客户端的搭建需要借助以下API

TCP之间通信通过流进行传输,无论是服务器还是客户端:读取内容用输入流,写入内容用输出流

🌴ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。

ServerSocket 构造方法

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

ServerSocket 方法

方法签名方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待
void close()关闭此套接字

🎄Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

Socket 构造方法

方法签名方法说明
Socket(String host, intport)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

Socket 方法

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

🍀TCP中的长短连接

博主在前面的博文里面说到,TCP是面向连接的通信方式,TCP发送数据时,需要先建立连接,而这个连接又分为长短连接:

对比以上长短连接,两者区别如下:

拓展

博主下面实现的TCP服务器与客户端属于长连接

🎍建立TCP回显客户端与服务器

什么叫回显客户端与与服务器呢?

其实就是:客户端向服务端发送请求,一般来说我们的服务端会对我们发送的请求进行处理,我们这里为了简单,就省略里面的处理过程,只实现将请求重新发回客户端,不做任何处理。

🚩TCP搭建服务器

我们分为以下几步来实现:

  1. 创建TcpEchoServer类来表示我们的服务器,并创建ServerSocket对象,初始值为null

  2. 在TcpEchoServer的构造方法里进行ServerSocket对象的实例化

  3. 用一个start()方法表示启动程序

  4. 在该方法内我们首先要使用accept()进行连接,并用Socket对象进行接收

  5. 我们再用一个processConnection(Socket clientSocket)方法处理我们的连接

由于我们的TCP传输是以流的形式传播的,所以我们这里用到了读写数据流的方法来进行书写,不会这一部分的小伙伴,可以去看看博主所写《【JavaEE初阶】 文件内容的读写 —— 数据流》进行查看学习

接下来我们书写这个processConnection(Socket clientSocket)方法

  1. 读取请求,构造输入流的Scanner,并判断后面如果没有数据就关闭连接

  2. 然后我们将读取的数据交给我们的 response()构造响应

  3. 响应后的数据写入该套接字的输出流中,最后flush(),进行刷新,确保写入

为了释放资源,我们每一次交互完毕都需要对我们的套接字进行关闭,这里我们使用fially来进行处理

代码实现如下:

import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.util.Scanner;public class TcpEchoServer {    private ServerSocket serverSocket = null;    public TcpEchoServer(int port) throws IOException {        serverSocket = new ServerSocket(port);    }    public void start() throws IOException {        System.out.println("启动服务器");        Socket socket = serverSocket.accept();        processConnection(socket);    }    // 使用这个方法来处理一个连接.    // 这一个连接对应到一个客户端. 但是这里可能会涉及到多次交互.    private void processConnection(Socket clientSocket) {        System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());        // 基于上述 socket 对象和客户端进行通信        try (InputStream inputStream = clientSocket.getInputStream();             OutputStream outputStream = clientSocket.getOutputStream()) {            // 由于要处理多个请求和响应, 也是使用循环来进行.            while (true) {                // 1. 读取请求                Scanner scanner = new Scanner(inputStream);                if (!scanner.hasNext()) {                    // 没有下个数据, 说明读完了. (客户端关闭了连接)                    System.out.printf("[%s:%d] 客户端下线! \n", clientSocket.getInetAddress().toString(), clientSocket.getPort());                    break;                }                // 注意!! 此处使用 next 是一直读取到换行符/空格/其他空白符结束, 但是最终返回结果里不包含上述 空白符 .                String request = scanner.next();                // 2. 根据请求构造响应                String response = process(request);                // 3. 返回响应结果.                //    OutputStream 没有 write String 这样的功能. 可以把 String 里的字节数组拿出来, 进行写入;                //    也可以用字符流来转换一下.                PrintWriter printWriter = new PrintWriter(outputStream);                // 此处使用 println 来写入. 让结果中带有一个 \n 换行. 方便对端来接收解析.                printWriter.println(response);                // flush 用来刷新缓冲区, 保证当前写入的数据, 确实是发送出去了.                printWriter.flush();                System.out.printf("[%s:%d] req: %s; resp: %s \n", clientSocket.getInetAddress().toString(), clientSocket.getPort(),                        request, response);            }        } catch (IOException e) {            e.printStackTrace();        } finally {            // 更合适的做法, 是把 close 放到 finally 里面, 保证一定能够执行到!!            try {            clientSocket.close();                clientSocket.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    public String process(String request) {        return request;    }    public static void main(String[] args) throws IOException {        TcpEchoServer server = new TcpEchoServer(9090);        server.start();    }}

服务端启动展示
在这里插入图片描述

🚩TCP搭建客户端

搭建客户端我们也可以分为以下几步:

  1. 创建TcpEchoClient类表示我们的客户端,创建Soket对象用于与客户端通信·

  2. 再TcpEchoClient构造方法里进行实例化Socket的对象

  3. 创建start()方法用于我们的操作

  4. 读取键盘所要输入的数据

  5. 将所读的数据通过输出流进行写入

  6. 读取响应的输入流,进行打印

  7. main函数中进行启动

代码实现如下:

import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;import java.util.Scanner;public class TcpEchoClient {    private Socket socket = null;    public TcpEchoClient(String serverIp, int serverPort) throws IOException {        // Socket 构造方法, 能够识别 点分十进制格式的 IP 地址. 比 DatagramPacket 更方便.        // new 这个对象的同时, 就会进行 TCP 连接操作.        socket = new Socket(serverIp, serverPort);    }    public void start() {        System.out.println("客户端启动!");        Scanner scanner = new Scanner(System.in);        try (InputStream inputStream = socket.getInputStream();             OutputStream outputStream = socket.getOutputStream()) {            while (true) {                // 1. 先从键盘上读取用户输入的内容                System.out.print("> ");                String request = scanner.next();                if (request.equals("exit")) {                    System.out.println("goodbye");                    break;                }                // 2. 把读到的内容构造成请求, 发送给服务器.                PrintWriter printWriter = new PrintWriter(outputStream);                printWriter.println(request);                // 此处加上 flush 保证数据确实发送出去了.                printWriter.flush();                // 3. 读取服务器的响应                Scanner respScanner = new Scanner(inputStream);                String response = respScanner.next();                // 4. 把响应内容显示到界面上                System.out.println(response);            }        } catch (IOException e) {            e.printStackTrace();        }    }    public static void main(String[] args) throws IOException {        TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);        client.start();    }}

客户端启动展示:
在这里插入图片描述

🚩通信过程展示:

在这里插入图片描述
在这里插入图片描述

🌳多个客户端对一个服务器

在博主写的【JavaEE初阶】 UDP服务器与客户端的搭建中多个客户端面对一个服务器的时候,我们只需要进行设置以下即可,但是了,在我们上述写的TCP代码中是不可行的,就算可以启动,但是后续的客户端会出现阻塞,并不会执行相应响应

这是因为这里是可连接的,当一个客户端与服务器进行建立后,后面的线程就只能等待。

就相当于你正在和你的女朋友打电话,这时候你兄弟打电话来了,你就只能让你兄弟先等着,等你和你女朋友打完电话后,才可以接你兄弟的电话

那我们的解决方法是什么呢?

这里用的解决方法是对服务器用多线程进行处理,使得服务器可以和多台客户端进行通信。由于我们在实际开发环境中客户端非常的多,而我们频繁创建销毁线程会增加开销,所以我们这里有用了一个线程池来实现

对上述服务器修改的代码如下:

    public void start() throws IOException {        System.out.println("启动服务器");        // 此处使用 CachedThreadPool, 使用 FixedThreadPool 不太合适 (线程数不太应该是有固定的....)        ExecutorService threadPool = Executors.newCachedThreadPool();        while (true) {            // 使用这个 clientSocket 和具体的客户端进行交流.            Socket clientSocket = serverSocket.accept();            // 此处使用多线程来处理.            // 这里的多线程版本的程序, 最大的问题就是可能会涉及到频繁申请释放线程.//            Thread t = new Thread(() -> {//                processConnection(clientSocket);//            });//            t.start();            // 使用线程池.            threadPool.submit(() -> {                processConnection(clientSocket);            });        }    }

🚩拓展(IO多路复用/IO多路转接)

上述代码解决了多个客户端与一个服务器通信的问题,但是呢,在实际应用中,要是客户端太多,且客户端都不退出通信,这时候服务器被占满了,后续就无法处理个更多客户端

这时候呢,有两种解决方法:

  1. 增加服务器的数量

但是呢,这种方法虽然好,但是缺点是需要钱来购买服务器

  1. 所以我们一般采用另一种更省钱的做法,这时候操作系统就提供了一种API,也就是IO多路复用/IO多路转接

简单理解就是客户端在进行IO操作时可能有很多的空闲时间,这时候该线程处于空闲的状态,这时候就可以让该线程去做一些其他的操作,等空闲时间一过,又立即回来继续执行

这样即省钱,又可以容纳更多的客户端进行通信。这里博主只是扩展一下哎,所以就不做过多赘述了。

⭕总结

关于《【JavaEE初阶】 TCP服务器与客户端的搭建》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

来源地址:https://blog.csdn.net/m0_71731682/article/details/134246343

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-服务器
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯