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

2023-11-17

目录

需求

多线程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();
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

互联网编程之多线程/线程池TCP服务器端程序设计 的相关文章

  • Kali Linux 安全渗透核心总结,444页核心知识点

    就像IT人离不开Linux系统一样 网安人也离不开Kali Linux 作为攻击性防御和渗透测试的代名词 越来越多的人开始学习Kali 如果你也对kali感兴趣 又想深入了解这方面内容 不妨收藏一下这份Kali Linux安全渗透教程 共4
  • 如何使用内网穿透实现iStoreOS软路由公网远程访问局域网电脑桌面

    文章目录 简介 一 配置远程桌面公网地址 二 家中使用永久固定地址 访问公司电脑 具体操作方法是 简介 软路由 是PC的硬件加上路由系统来实现路由器
  • 虚拟主机操作系统 Windows、Linux

    操作系统将直接影响服务器的性能 安全性和可用性 因此确保选择合适的操作系统对于成功运行您的网站或应用程序至关重要 以下是一些考虑因素 可帮助您选择适合您需求的虚拟主机操作系统 1 熟悉度和技术支持 如何选择操作系统应该考虑您的经验水平和熟悉
  • 服务器超线程的好处

    服务器超线程的好处 1 提高性能 超线程通过提高整体系统吞吐量显着提高服务器性能 通过允许多个线程在单个物理内核上同时执行 超线程减少了空闲时间并最大限度地利用了可用资源 这会加快任务执行速度并缩短应用程序的响应时间 尤其是在多线程工作负载
  • Linux中如何查看开启了哪些端口?

    在Linux中 端口是设备与外界通讯交流的出口 常用于指TCP IP协议中的端口 其按照端口号可以分为三类 分别是 公认端口 注册端口 动态端口 那么Linux中如何查看开启了哪些端口 以下是常用命令介绍 1 使用netstat命令 net
  • Linux 系统日志及其归档

    主要记录Linux 系统需要关注的日志文件 以及日志归档服务 rsyslogd 系统日志服务 rsyslogd 日志服务 rsyslogd reliable and extended syslogd 可靠 可扩展的系统日志服务 Rsyslo
  • 服务器集群是如何提高计算性能的?

    服务器集群是一种将多台服务器连接起来协同工作的技术 通过集群配置 可以提高计算性能 可靠性和可扩展性 以下是服务器集群如何提高计算性能的详细解释 一 并行处理能力 服务器集群的核心优势在于其并行处理能力 通过将多个服务器组成一个集群 可以将
  • 自定义编写zabbix_agent脚本

    vi usr lib systemd system zabbix agent servicce Unit Description Zabbix Agent After syslog target After network target S
  • 5个步骤,教你瞬间明白线程和线程安全

    记得今年3月份刚来杭州面试的时候 有一家公司的技术总监问了我这样一个问题 你来说说有哪些线程安全的类 我心里一想 这我早都背好了 稀里哗啦说了一大堆 他又接着问 那你再来说说什么是线程安全 然后我就GG了 说真的 我们整天说线程安全 但是对
  • 线程安全(中)--彻底搞懂synchronized(从偏向锁到重量级锁)

    接触过线程安全的同学想必都使用过synchronized这个关键字 在java同步代码快中 synchronized的使用方式无非有两个 通过对一个对象进行加锁来实现同步 如下面代码 synchronized lockObject 代码 对
  • WEB前端常见受攻击方式及解决办法总结

    一个网址建立后 如果不注意安全问题 就很容易被人攻击 下面讨论一下集中漏洞情况和放置攻击的方法 一 SQL注入 所谓的SQL注入 就是通过把SQL命令插入到web表单提交或输入域名或页面请求的查询字符串 最终达到欺骗服务器执行恶意的SQL命
  • 用户数据中的幸存者偏差

    幸存者偏差 Survivorship bias 是一种常见的逻辑谬误 意思是没有考虑到筛选的过程 忽略了被筛选掉的关键信息 只看到经过筛选后而产生的结果 先讲个故事 二战时 无奈德国空防强大 盟军战机损毁严重 于是军方便找来科学家统计飞机受
  • Python爬虫实战:IP代理池助你突破限制,高效采集数据

    当今互联网环境中 为了应对反爬虫 匿名访问或绕过某些地域限制等需求 IP代理池成为了一种常用的解决方案 IP代理池是一个包含多个可用代理IP地址的集合 可以通过该代理池随机选择可用IP地址来进行网络请求 IP代理池是一组可用的代理IP地址
  • HPE Aruba Networking:五大网络现代化策略助力实现校园数字化转型

    作者 Aruba中国区技术销售总监 俞世丹 全球数字化进程日益加深 科技已成为加速教育行业发展的重要驱动力 人工智能 大数据 云计算 物联网 虚拟现实等新兴技术的快速发展 正在深刻改变着教育的形态和模式 为了更好地满足学校师生个性化教育教学
  • 一台java服务器可以跑多少个线程?

    一台java服务器可以跑多少个线程 一台java服务器能跑多少个线程 这个问题来自一次线上报警如下图 超过了我们的配置阈值 打出jstack文件 通过IBM Thread and Monitor Dump Analyzer for Java
  • 国外拨号VPS指南:开启你的全球网络之旅

    在当今数字化时代 互联网已经成为了我们生活的一部分 而要在全球范围内畅通无阻地访问互联网 拥有一个可靠的国外拨号VPS是非常重要的 无论您是为了工作 学习还是娱乐 国外拨号VPS都可以为您提供更广泛的网络体验 本文将为您提供国外拨号VPS的
  • 内网安全:隧道技术详解

    目录 隧道技术 反向连接技术 反向连接实验所用网络拓扑图及说明 网络说明 防火墙限制说明 实验前提说明 实战一 CS反向连接上线 拿下Win2008 一 使用转发代理上线创建监听器 二 上传后门执行上线 隧道技术 SMB协议 SMB协议介绍
  • 静态综合实验

    1 IP地址划分 192 168 1 0 27 用于主干拆分 192 168 1 32 27 用于用户拆分 192 168 1 64 27 用于用户拆分 192 168 1 96 27 用于用户拆分 192 168 1 128 27 用于用
  • DSCA190V 57310001-PK

    DSCA190V 57310001 PK DSCA190V 57310001 PK 具有两个可编程继电器功能 并安装在坚固的 XP 外壳中 DSCA190V 57310001 PK 即可使用 只需最少的最终用户校准 DSCA190V 573
  • 服务器中E5和I9的区别是什么,如何选择合适的配置

    随着科技的进步 服务器处理器的性能在不断攀升 其中 Intel的E5和I9系列处理器在业界具有广泛的影响力 而当我们在选择服务器的时候会有各种各样的配置让我们眼花缭乱不知道该怎么去选择 下面我跟大家分享一下E5跟I9有什么区别 方便我们在选

随机推荐

  • 详解@Override注解

    目录 1 是什么 2 为什么用 3 举例说明 1 示例一 2 示例二 3 示例三 1 是什么 Override注解是伪代码 用于表示被标注的方法是一个重写方法 Override注解 只能用于标记方法 并且它只在编译期生效 不会保留在clas
  • QT中添加Q_OBJECT出现的问题

    Multiple Inheritance Requires QObject to Be First 多重继承QObject一定要放在前面 我在用class My Node public QGraphicsItem public QObjec
  • 产业互联网-构建智能+时代数字生态新图景

    在2019腾讯全球数字生态大会新闻发布会上 腾讯云联合腾讯研究院 共同发布了行业重磅报告 产业互联网 构建智能 时代数字生态新图景 报告首次阐述了产业互联网的战略框架和实践方法论 报告指出 产业互联网的实现 需要跨界共建数字生态共同体 形成
  • linux安装telnet工具下载,Linux下安装telnet的方法

    一 安装telnet 1 检测telnet server的rpm包是否安装 root localhost rpm qa telnet server 若无输入内容 则表示没有安装 出于安全考虑telnet server rpm是默认没有安装的
  • NestedScrolling机制(一)——概述

    http blog csdn net al4fun article details 53888990 如今 NestedScrolling机制 可以称为嵌套滚动或嵌套滑动 在各种app中的应用已经十分广泛了 下图是 饿了么 中的一个例子 当
  • 虹膜识别 Iris_Osiris_v4.1源码,mfc测试用例

    01 资源 win10 vs2015 git opencv3 3 0 cmake 参考虹膜识别文档 开源虹膜识别软件OSIRIS4 1的使用入门 将开源虹膜识别算法OSIRIS4 1移植到Windows opencv3 3 0的配置参考 也
  • Leetcode 202. 快乐数(找规律注意回环)

    快乐数 编写一个算法来判断一个数 n 是不是快乐数 快乐数 定义为 对于一个正整数 每一次将该数替换为它每个位置上的数字的平方和 然后重复这个过程直到这个数变为 1 也可能是 无限循环 但始终变不到 1 如果 可以变为 1 那么这个数就是快
  • 记录几个CentOS安装包(rpm)的下载地址-离线安装必备

    1 http rpmfind net linux RPM index html 2 https centos pkgs org 3 http mirror centos org centos 7 extras x86 64 Packages
  • Java处理SSH

    JSch 登录 密码方式 session setPassword password 公私秘钥方式 jsch addIdentity ssh id rsaxxx SFTP简介 SFTP是Secure File Transfer Protoco
  • 【YOLOv7/YOLOv5系列算法改进NO.49】模型剪枝、蒸馏、压缩

    文章目录 前言 一 解决问题 二 基本原理 三 剪枝操作 四 知识蒸馏操作 前言 作为当前先进的深度学习目标检测算法YOLOv7 已经集合了大量的trick 但是还是有提高和改进的空间 针对具体应用场景下的检测难点 可以不同的改进方法 此后
  • go 设置 GOROOT 和 GOPATH

    点击在我的博客 xuxusheng com 中查看 有更好的排版哦 发表失败全部丢失 写完了又重写一遍 csdn 都没个自动保存功能 强烈吐槽 go 里面有两个非常重要的环境变量 GOROOT 和 GOPATH 其中 GOROOT 是安装
  • linux CPU性能监控(进阶)和杂谈

    线程与进程的区别 进程 是执行一段程序 即一旦程序被载入到内存中准备执行 它就是一个进程 线程 单个进程中执行每一个任务就是一个线程 一个线程只属于一个进程 一个进程里可以有多个线程 上下文切换 在处理器执行期间 运行进程的信息被存储在处理
  • javax.net.ssl.SSLException: Received fatal alert: protocol_version

    最近需要第三方回传数据到自己的地址 发现调不通 如下 1 第三方错误提示 根据提示是请求时所用的tls协议版本与目标地址所能使用的不一致 2 第三方查看代码中所有的tls版本 查看目标地址所能支持的tls版本 nmap script ssl
  • Python的十二道编程题,码住战胜一切

    一 计算文件大小 import os def get size path size 0 l path while l path l pop lst os listdir path for name in lst son path os pa
  • Visuial Studio 打开 Unity 新建脚本时,新脚本继承MonoBehaviour暂时失效为白色的解决方法

    点击 文件 gt 最近使用的项目和解决方案 gt 点击当前项目 即可瞬间重载当前项目 这个时候 白色的MonoBehaviour会变成绿色 就可以了 当然最传统的方法就是关掉VS再打开 不过挺浪费时间的
  • umijs框架加载cesium

    创建umi项目 yarn create umi 选择app 选择是否使用typescript N 选择依赖 yarn yarn start 项目创建完成后 添加cesium yarn add cesium 下载版本是1 67 不同版本配置方
  • 【Android】替换系统默认字体

    android系统默认字体分类 DroidSans ttf 系统默认英文字体 DroidSans Bold ttf 系统默认英文粗字体 DroidSansFallback ttf 系统默认中文字体 为系统新增字体 1 复制字体到framew
  • python机器学习之支持向量机——线性SVM决策过程的可视化案例

    线性SVM决策过程的可视化 1 导入需要的模块 from sklearn datasets import make blobs from sklearn svm import SVC import matplotlib pyplot as
  • QT中on_pushButton_clicked()用法

    在Qt里按钮控件默认对应一个on pushButton clicked 成员 如果想用点击信号 在代码中实现on pushButton clicked 成员即可 最近看了一段代码 里面并没有connect函数 只定义了pushbutton
  • 互联网编程之多线程/线程池TCP服务器端程序设计

    目录 需求 多线程TCP服务器 线程池TCP服务器 测试 日志模块 需求 多线程TCP服务器 30分 设计编写一个TCP服务器端程序 需使用多线程处理客户端的连接请求 客户端与服务器端之间的通信内容 以及服务器端的处理功能等可自由设计拓展