互联网编程之多线程/线程池TCP服务器端程序设计

目录

需求

多线程TCP服务器

线程池TCP服务器

测试

日志模块


需求

多线程TCP服务器(30分):

设计编写一个TCP服务器端程序,需使用多线程处理客户端的连接请求。客户端与服务器端之间的通信内容,以及服务器端的处理功能等可自由设计拓展,无特别限制和要求。

线程池TCP服务器(30分):

设计编写一个TCP服务器端程序,需使用线程池处理客户端的连接请求。客户端与服务器端之间的通信内容,以及服务器端的处理功能等可自由设计拓展,无特别限制和要求,但应与第1项要求中的服务器功能一致,便于对比分析。

比较分析不同编程技术对服务器性能的影响(20分):

自由编写客户端程序和设计测试方式,对1和2中的服务器端程序进行测试,分析比较两个服务器的并发处理能力。

设计编写可重用的服务器日志程序模块,日志记录的内容和日志存储方式可自定(比如可以记录客户端的连接时间、客户端IP等,日志存储为.TXT或.log文件等),分别在1和2的服务器程序中调用该日志程序模块,使多线程TCP服务器和线程池TCP服务器都具备日志功能,注意线程之间的同步操作处理。(20分)

多线程TCP服务器

这段代码是一个基于Java的多线程服务器实现,用于接收客户端的连接并处理其发送的消息。

  1. 首先,在MultithreadingServer类的main方法中:

    • 创建了一个ServerSocket对象,并指定它监听的端口号为8888,同时设置最大连接数量为10000。
    • 进入一个无限循环,用于持续接受客户端的连接请求。
    • 每次循环,当有客户端连接时,创建一个新的MultiThread实例,并传入对应的Socket对象。
    • 同时,创建一个Logger实例,记录连接的相关信息,包括客户端的IP地址、连接时间和日志文件名。
  2. MultiThread类中:

    • 继承了Thread类,并重写了run方法。
    • run方法中,通过BufferedReaderSocket的输入流获取一个字符输入流,并通过InputStreamReader将其转换为字符流,然后读取客户端发送的数据。
    • 使用一个循环来连续读取,直到达到输入流的末尾(客户端关闭连接)为止。
    • 在每次循环中,打印接收到的消息到标准输出。
    • 最后,关闭输入流和Socket连接。

整体而言,这段代码实现了一个简单的多线程服务器,能够接收并处理客户端的连接请求,以及读取和输出客户端发送的消息。日志记录部分使用了自定义的Logger类,但其具体实现不在这段代码中显示。需要注意的是,异常处理方面可能需要根据实际需求进行补充和调整。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;public class MultithreadingServer {public static void main(String[] args) {try {ServerSocket serverSocket = new ServerSocket(8888, 10000);while (true) {Socket client = serverSocket.accept();new MultiThread(client).start();new Logger(client.getInetAddress().getHostAddress(), new Date(), "LogMultithreadingServer.txt");}} catch (IOException e) {e.printStackTrace();throw new RuntimeException(e);}}
}class MultiThread extends Thread {private Socket socket = null;public MultiThread(Socket socket) {this.socket = socket;}public void run() {try {BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));String message = null;while ((message = input.readLine()) != null) {System.out.println(message);}input.close();socket.close();} catch (IOException error) {error.printStackTrace();}}
}

线程池TCP服务器

这段代码是一个使用线程池的多线程服务器实现,与前面的代码相比,在并发处理客户端连接方面进行了改进。

  1. ThreadPoolServer类的main方法中:

    • 创建了一个具有200个线程的固定大小线程池ExecutorService
    • 创建了一个ServerSocket对象,并指定它监听的端口号为9999,同时设置最大连接数量为10000。
    • 进入一个无限循环,用于持续接受客户端的连接请求。
    • 每次循环,当有客户端连接时,将一个新的TheadPoolTask任务提交给线程池进行执行。
    • 同时,创建一个Logger实例,记录连接的相关信息,包括客户端的IP地址、连接时间和日志文件名。
  2. TheadPoolTask类中:

    • 实现了Runnable接口,并重写了run方法。
    • run方法中,通过BufferedReaderSocket的输入流获取一个字符输入流,并通过InputStreamReader将其转换为字符流,然后读取客户端发送的数据。
    • 使用一个循环来连续读取,直到达到输入流的末尾(客户端关闭连接)为止。
    • 在每次循环中,打印接收到的消息到标准输出。
    • 最后,关闭输入流和Socket连接。

整体而言,这段代码与前一段代码类似,不同之处在于使用了线程池来管理线程资源,提高了并发处理能力。通过将任务提交给线程池执行,可以控制并发线程数,并重复利用线程,避免频繁创建和销毁线程带来的开销。需要注意的是,异常处理方面可能需要根据实际需求进行补充和调整。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolServer {public static void main(String[] args) {ExecutorService executorService= Executors.newFixedThreadPool(200);try{ServerSocket serverSocket=new ServerSocket(9999,10000);while(true){Socket client=serverSocket.accept();executorService.execute(new TheadPoolTask(client));new Logger(client.getInetAddress().getHostAddress(),new Date(),"LogThreadPoolServer.txt");}} catch (IOException e) {e.printStackTrace();throw new RuntimeException(e);}finally {executorService.shutdown();}}
}
class TheadPoolTask implements Runnable{private Socket socket=null;public TheadPoolTask(Socket socket){this.socket=socket;}public void run(){try{BufferedReader input=new BufferedReader(new InputStreamReader(socket.getInputStream()));String message=null;while((message=input.readLine())!=null){System.out.println(message);}input.close();socket.close();}catch (IOException error){error.printStackTrace();}}
}

测试

编写一个压力测试程序,测试多线程服务器和线程池服务器在高并发时的表现,实现run()方法,发起连接请求,为了保证多线程并发访问long类型变量时的线程安全性,使用线程安全的AtomicLong类来记录服务器响应的时间,如图7所示,附件已含源代码。

图7

主程序同时向两个服务器发起多个连接,连接规模从1000个连接请求开始一直增加到10000个,通过在短时间内发起大量连接请求来对服务器进行压力测试,如图8所示。

图8

测试过程数据如图9所示。

图9

分析两个服务器的表现情况,如图10所示,可见在处理大量短任务(如处理网络请求)的情况下,使用线程池可以避免频繁地创建、销毁线程所带来的开销,因此会更快一些。

图10

 

这段代码是一个简单的测试服务器和客户端的程序。

  1. TestServerClient类的main方法中:

    • 通过循环来控制不同规模(power)的测试。
    • 在每个测试规模下,通过嵌套循环启动一定数量(2000)的测试任务。
    • 每个测试任务使用TestTask类创建一个线程,构造函数传入不同的端口号(9999或8888),然后调用run方法运行测试任务。
    • 在每次测试任务完成后,将消耗的时间输出到控制台。
  2. TestTask类中:

    • 定义了一个port变量,表示客户端连接的目标端口。
    • 声明了两个静态的AtomicLong对象timePooltimeMulti,用于记录线程池和多线程方式的测试消耗时间。
    • 在构造函数中接收一个端口号,并将其赋值给port变量。
    • run方法实现了客户端的测试逻辑:
      • 创建一个空的Socket对象。
      • 构建InetSocketAddress对象,指定本地主机地址和目标端口。
      • 记录当前时间为起始时间。
      • 调用socket.connect方法与服务器建立连接,等待连接完成。
      • 关闭socket对象。
      • 记录当前时间为结束时间。
      • 根据不同的端口号,将测试消耗时间累加到相应的AtomicLong对象中。

该程序的主要目的是通过多次连接服务器的测试来比较线程池和多线程方式的性能消耗。它会启动一定数量的测试任务,并分别记录两种方式的测试消耗时间。在每次测试任务完成后,将消耗时间输出到控制台。通过对不同规模测试结果的比较,可以初步评估线程池和多线程方式的性能表现。

import java.io.IOException;
import java.net.*;
import java.util.concurrent.atomic.AtomicLong;public class TestServerClient {public static void main(String[] args) {for (int power = 1; power <= 10; power++) {int scale = 2000*power ;for (int i = 0; i < 2000; i++) {if(i%2==0)new TestTask(9999).run();else new TestTask(8888).run();}System.out.printf("线程池:规模为%d消耗时间%d毫秒\n", scale/2, TestTask.timePool.get());System.out.printf("多线程:规模为%d消耗时间%d毫秒\n", scale/2, TestTask.timeMulti.get());}}
}class TestTask{private final int port;public static AtomicLong timePool =new AtomicLong();public static AtomicLong timeMulti=new AtomicLong();public TestTask(int port) {this.port = port;}public void run() {try {Socket socket = new Socket();SocketAddress socketAddress=new InetSocketAddress(InetAddress.getLocalHost(), port);long start=System.currentTimeMillis();socket.connect(socketAddress);while(!socket.isConnected()){}socket.close();long end=System.currentTimeMillis();if(port==9999)timePool.addAndGet(end-start);else timeMulti.addAndGet(end-start);} catch (IOException e) {e.printStackTrace();System.exit(-1);}}
}

日志模块

这段代码是一个简单的日志记录器类。它具有以下功能:

  1. 构造函数:接受一个IP地址、日期和文件路径作为参数,并生成一条以IP地址和日期为内容的日志信息。

  2. keep() 方法:该方法使用了 synchronized 关键字,以确保在多线程环境下只有一个线程可以访问该方法。在方法内部,它创建一个 BufferedWriter 对象,并将日志内容写入指定的文件中。

总体来说,这个代码实现了一个基本的日志记录功能,将用户登录的 IP 地址和日期写入指定的文件中。

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;public class Logger {private final String log;private final String filePath;public Logger(String IP, Date date,String filePath){log=IP+" login at "+date.toString()+'\n';this.filePath=filePath;keep();}private synchronized void keep(){try {BufferedWriter bufferedWriter=new BufferedWriter(new FileWriter(filePath,true));bufferedWriter.write(log);bufferedWriter.close();} catch (IOException e) {e.printStackTrace();}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux中创建sftp用户并限制目录权限

注意两点&#xff1a; 一是禁止该用户通过ssh登录&#xff0c;二是不需要创建家目录。家目录简单来说&#xff0c;就是在/home下的用户命令&#xff0c;默认每个用户在/home中都是有与用户名一样的文件夹。 1.创建组 groupadd sftp 2. 创建用户 useradd -g sftp -s /sbin/…

API 网关 vs 负载均衡:选择适合你的网络流量管理组件

由于互联网技术的发展&#xff0c;网络数据的请求数节节攀升&#xff0c;这使得服务器承受的压力越来越大。在早期的系统架构中&#xff0c;通常使用负载均衡来将网络流量平摊到多个服务器中&#xff0c;以此减轻单台服务器的压力。但是现如今&#xff0c;后端服务的种类在不断…

vue进阶-elementPlus

Element Plus官网 Element Plus 基于 Vue 3&#xff0c;面向设计师和开发者的组件库。减少开发者关注css&#xff0c;重心关注业务逻辑。 1. 入门 1.1 安装 npm install element-plus --save1.2 快速开始 1、main.js 引入并 use element-plus import { createApp } from …

Java开发 - Canal的基本用法

前言 今天给大家带来的是Canal的基本用法&#xff0c;Canal在Java中常被我们用来做数据的同步&#xff0c;当然不是MySQL与MySQL&#xff0c;Redis与Redis之间了&#xff0c;如果是他们&#xff0c;那就好办了&#xff0c;我们可以直接通过配置来完成他们之间的主从、主主&…

Java官方笔记13集合

Storing Data The Collections Framework is the most widely used API of the JDK. 集合不是数据类型&#xff0c;它是JDK的API&#xff0c;可以用来存储数据等&#xff0c;相当于数据结构。 the Collections Framework is a set of interfaces that models different way of …

DYnamics 365如何隐藏实体列表页面home page页面上的PowerBI按钮和EXCEL template按钮

如何隐藏以上两个按钮&#xff0c;用ribbon工具根本找不到这2个按钮。 解决方案&#xff1a;添加一个没用的按钮&#xff0c;通过调用enable方法来隐藏。 // JavaScript source code function HiddenButton() { HiddePowerBIButton(); HiddeDocumentTemplateButton(); return…

大数据周会-本周学习内容总结018

开会时间&#xff1a;2023.06.18 15:00 线下会议 目录 01【调研-数据分析&#xff08;质量、ETL、可视化&#xff09;】 02【fhzn项目】 2.1【es多维度检索接口】 2.2【AI算法库资料整理】 03【专利】 04【学习内容】 4.1【flink】 01【调研-数据分析&#xff08;质量、…

svn commit 用法

转载   原文&#xff1a;https://blog.csdn.net/qq_39790633/article/details/103700391 使用svn进行代码的提交有两种方法&#xff1a;一种是通过TortoiseSVN客户端界面进行提交&#xff0c;另一种是通过svn commit指令提交。 方法一&#xff1a;通过TortoiseSVN客户端界面提…

【Linux 驱动篇(二)】LED 驱动开发

文章目录 一、Linux 下 LED 灯驱动原理1. 地址映射1.1 ioremap 函数1.2 iounmap 函数 2. I/O 内存访问函数2.1 读操作函数2.2 写操作函数 二、实验程序编写1. LED 灯驱动程序编写2. 编写测试 APP 三、运行测试1. 编译驱动程序和测试 APP1.1 编译驱动程序1.2 编译测试 APP 2. 运…

如何评估大型语言模型(LLM)?

编者按&#xff1a;近期几乎每隔一段时间&#xff0c;就有新的大语言模型发布&#xff0c;但是当下仍然没有一个通用的标准来评估这些大型语言模型的质量&#xff0c;我们急需一个可靠的、综合的LLM评估框架。 本文说明了为什么我们需要一个全面的大模型评估框架&#xff0c;并…

HBase(14):HBase架构

1 系统架构 1.1 Client 客户端,例如:发出HBase操作的请求。例如:之前我们编写的Java API代码、以及HBase shell,都是CLient 1.2 Master Server 在HBase的Web UI中,可以查看到Master的位置。 监控RegionServer处理RegionServer故障转移处理元数据的变更处理region的分配或…

选读SQL经典实例笔记01_检索和排序

1. 在WHERE子句中引用别名列 1.1. 当表里的某些列没有被恰当命名的时候&#xff0c;这个技巧尤其有用 1.2. sql select sal as salary, comm as commissionfrom empwhere salary &#xff1c; 5000 1.3. 内嵌视图 1.3.1. sql select *from (select sal as salary, comm …