目錄
需求
多線程TCP服務(wù)器
線程池TCP服務(wù)器
測(cè)試
日志模塊
需求
多線程TCP服務(wù)器(30分):
設(shè)計(jì)編寫一個(gè)TCP服務(wù)器端程序,需使用多線程處理客戶端的連接請(qǐng)求??蛻舳伺c服務(wù)器端之間的通信內(nèi)容,以及服務(wù)器端的處理功能等可自由設(shè)計(jì)拓展,無特別限制和要求。
線程池TCP服務(wù)器(30分):
設(shè)計(jì)編寫一個(gè)TCP服務(wù)器端程序,需使用線程池處理客戶端的連接請(qǐng)求??蛻舳伺c服務(wù)器端之間的通信內(nèi)容,以及服務(wù)器端的處理功能等可自由設(shè)計(jì)拓展,無特別限制和要求,但應(yīng)與第1項(xiàng)要求中的服務(wù)器功能一致,便于對(duì)比分析。
比較分析不同編程技術(shù)對(duì)服務(wù)器性能的影響(20分):
自由編寫客戶端程序和設(shè)計(jì)測(cè)試方式,對(duì)1和2中的服務(wù)器端程序進(jìn)行測(cè)試,分析比較兩個(gè)服務(wù)器的并發(fā)處理能力。
設(shè)計(jì)編寫可重用的服務(wù)器日志程序模塊,日志記錄的內(nèi)容和日志存儲(chǔ)方式可自定(比如可以記錄客戶端的連接時(shí)間、客戶端IP等,日志存儲(chǔ)為.TXT或.log文件等),分別在1和2的服務(wù)器程序中調(diào)用該日志程序模塊,使多線程TCP服務(wù)器和線程池TCP服務(wù)器都具備日志功能,注意線程之間的同步操作處理。(20分)
多線程TCP服務(wù)器
這段代碼是一個(gè)基于Java的多線程服務(wù)器實(shí)現(xiàn),用于接收客戶端的連接并處理其發(fā)送的消息。
-
首先,在
MultithreadingServer
類的main
方法中:- 創(chuàng)建了一個(gè)
ServerSocket
對(duì)象,并指定它監(jiān)聽的端口號(hào)為8888,同時(shí)設(shè)置最大連接數(shù)量為10000。 - 進(jìn)入一個(gè)無限循環(huán),用于持續(xù)接受客戶端的連接請(qǐng)求。
- 每次循環(huán),當(dāng)有客戶端連接時(shí),創(chuàng)建一個(gè)新的
MultiThread
實(shí)例,并傳入對(duì)應(yīng)的Socket
對(duì)象。 - 同時(shí),創(chuàng)建一個(gè)
Logger
實(shí)例,記錄連接的相關(guān)信息,包括客戶端的IP地址、連接時(shí)間和日志文件名。
- 創(chuàng)建了一個(gè)
-
在
MultiThread
類中:- 繼承了
Thread
類,并重寫了run
方法。 - 在
run
方法中,通過BufferedReader
從Socket
的輸入流獲取一個(gè)字符輸入流,并通過InputStreamReader
將其轉(zhuǎn)換為字符流,然后讀取客戶端發(fā)送的數(shù)據(jù)。 - 使用一個(gè)循環(huán)來連續(xù)讀取,直到達(dá)到輸入流的末尾(客戶端關(guān)閉連接)為止。
- 在每次循環(huán)中,打印接收到的消息到標(biāo)準(zhǔn)輸出。
- 最后,關(guān)閉輸入流和
Socket
連接。
- 繼承了
整體而言,這段代碼實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的多線程服務(wù)器,能夠接收并處理客戶端的連接請(qǐng)求,以及讀取和輸出客戶端發(fā)送的消息。日志記錄部分使用了自定義的Logger
類,但其具體實(shí)現(xiàn)不在這段代碼中顯示。需要注意的是,異常處理方面可能需要根據(jù)實(shí)際需求進(jìn)行補(bǔ)充和調(diào)整。
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服務(wù)器
這段代碼是一個(gè)使用線程池的多線程服務(wù)器實(shí)現(xiàn),與前面的代碼相比,在并發(fā)處理客戶端連接方面進(jìn)行了改進(jìn)。
-
在
ThreadPoolServer
類的main
方法中:- 創(chuàng)建了一個(gè)具有200個(gè)線程的固定大小線程池
ExecutorService
。 - 創(chuàng)建了一個(gè)
ServerSocket
對(duì)象,并指定它監(jiān)聽的端口號(hào)為9999,同時(shí)設(shè)置最大連接數(shù)量為10000。 - 進(jìn)入一個(gè)無限循環(huán),用于持續(xù)接受客戶端的連接請(qǐng)求。
- 每次循環(huán),當(dāng)有客戶端連接時(shí),將一個(gè)新的
TheadPoolTask
任務(wù)提交給線程池進(jìn)行執(zhí)行。 - 同時(shí),創(chuàng)建一個(gè)
Logger
實(shí)例,記錄連接的相關(guān)信息,包括客戶端的IP地址、連接時(shí)間和日志文件名。
- 創(chuàng)建了一個(gè)具有200個(gè)線程的固定大小線程池
-
在
TheadPoolTask
類中:- 實(shí)現(xiàn)了
Runnable
接口,并重寫了run
方法。 - 在
run
方法中,通過BufferedReader
從Socket
的輸入流獲取一個(gè)字符輸入流,并通過InputStreamReader
將其轉(zhuǎn)換為字符流,然后讀取客戶端發(fā)送的數(shù)據(jù)。 - 使用一個(gè)循環(huán)來連續(xù)讀取,直到達(dá)到輸入流的末尾(客戶端關(guān)閉連接)為止。
- 在每次循環(huán)中,打印接收到的消息到標(biāo)準(zhǔn)輸出。
- 最后,關(guān)閉輸入流和
Socket
連接。
- 實(shí)現(xiàn)了
整體而言,這段代碼與前一段代碼類似,不同之處在于使用了線程池來管理線程資源,提高了并發(fā)處理能力。通過將任務(wù)提交給線程池執(zhí)行,可以控制并發(fā)線程數(shù),并重復(fù)利用線程,避免頻繁創(chuàng)建和銷毀線程帶來的開銷。需要注意的是,異常處理方面可能需要根據(jù)實(shí)際需求進(jìn)行補(bǔ)充和調(diào)整。
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();
}
}
}
測(cè)試
編寫一個(gè)壓力測(cè)試程序,測(cè)試多線程服務(wù)器和線程池服務(wù)器在高并發(fā)時(shí)的表現(xiàn),實(shí)現(xiàn)run()方法,發(fā)起連接請(qǐng)求,為了保證多線程并發(fā)訪問long類型變量時(shí)的線程安全性,使用線程安全的AtomicLong類來記錄服務(wù)器響應(yīng)的時(shí)間,如圖7所示,附件已含源代碼。
圖7
主程序同時(shí)向兩個(gè)服務(wù)器發(fā)起多個(gè)連接,連接規(guī)模從1000個(gè)連接請(qǐng)求開始一直增加到10000個(gè),通過在短時(shí)間內(nèi)發(fā)起大量連接請(qǐng)求來對(duì)服務(wù)器進(jìn)行壓力測(cè)試,如圖8所示。
圖8
測(cè)試過程數(shù)據(jù)如圖9所示。
圖9
分析兩個(gè)服務(wù)器的表現(xiàn)情況,如圖10所示,可見在處理大量短任務(wù)(如處理網(wǎng)絡(luò)請(qǐng)求)的情況下,使用線程池可以避免頻繁地創(chuàng)建、銷毀線程所帶來的開銷,因此會(huì)更快一些。
圖10
這段代碼是一個(gè)簡(jiǎn)單的測(cè)試服務(wù)器和客戶端的程序。
-
在
TestServerClient
類的main
方法中:- 通過循環(huán)來控制不同規(guī)模(power)的測(cè)試。
- 在每個(gè)測(cè)試規(guī)模下,通過嵌套循環(huán)啟動(dòng)一定數(shù)量(2000)的測(cè)試任務(wù)。
- 每個(gè)測(cè)試任務(wù)使用
TestTask
類創(chuàng)建一個(gè)線程,構(gòu)造函數(shù)傳入不同的端口號(hào)(9999或8888),然后調(diào)用run
方法運(yùn)行測(cè)試任務(wù)。 - 在每次測(cè)試任務(wù)完成后,將消耗的時(shí)間輸出到控制臺(tái)。
-
在
TestTask
類中:- 定義了一個(gè)
port
變量,表示客戶端連接的目標(biāo)端口。 - 聲明了兩個(gè)靜態(tài)的
AtomicLong
對(duì)象timePool
和timeMulti
,用于記錄線程池和多線程方式的測(cè)試消耗時(shí)間。 - 在構(gòu)造函數(shù)中接收一個(gè)端口號(hào),并將其賦值給
port
變量。 -
run
方法實(shí)現(xiàn)了客戶端的測(cè)試邏輯:- 創(chuàng)建一個(gè)空的
Socket
對(duì)象。 - 構(gòu)建
InetSocketAddress
對(duì)象,指定本地主機(jī)地址和目標(biāo)端口。 - 記錄當(dāng)前時(shí)間為起始時(shí)間。
- 調(diào)用
socket.connect
方法與服務(wù)器建立連接,等待連接完成。 - 關(guān)閉
socket
對(duì)象。 - 記錄當(dāng)前時(shí)間為結(jié)束時(shí)間。
- 根據(jù)不同的端口號(hào),將測(cè)試消耗時(shí)間累加到相應(yīng)的
AtomicLong
對(duì)象中。
- 創(chuàng)建一個(gè)空的
- 定義了一個(gè)
該程序的主要目的是通過多次連接服務(wù)器的測(cè)試來比較線程池和多線程方式的性能消耗。它會(huì)啟動(dòng)一定數(shù)量的測(cè)試任務(wù),并分別記錄兩種方式的測(cè)試消耗時(shí)間。在每次測(cè)試任務(wù)完成后,將消耗時(shí)間輸出到控制臺(tái)。通過對(duì)不同規(guī)模測(cè)試結(jié)果的比較,可以初步評(píng)估線程池和多線程方式的性能表現(xiàn)。
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("線程池:規(guī)模為%d消耗時(shí)間%d毫秒\n", scale/2, TestTask.timePool.get());
System.out.printf("多線程:規(guī)模為%d消耗時(shí)間%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);
}
}
}
日志模塊
這段代碼是一個(gè)簡(jiǎn)單的日志記錄器類。它具有以下功能:
-
構(gòu)造函數(shù):接受一個(gè)IP地址、日期和文件路徑作為參數(shù),并生成一條以IP地址和日期為內(nèi)容的日志信息。
-
keep() 方法:該方法使用了 synchronized 關(guān)鍵字,以確保在多線程環(huán)境下只有一個(gè)線程可以訪問該方法。在方法內(nèi)部,它創(chuàng)建一個(gè) BufferedWriter 對(duì)象,并將日志內(nèi)容寫入指定的文件中。文章來源:http://www.zghlxwxcb.cn/news/detail-515844.html
總體來說,這個(gè)代碼實(shí)現(xiàn)了一個(gè)基本的日志記錄功能,將用戶登錄的 IP 地址和日期寫入指定的文件中。文章來源地址http://www.zghlxwxcb.cn/news/detail-515844.html
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();
}
}
}
到了這里,關(guān)于互聯(lián)網(wǎng)編程之多線程/線程池TCP服務(wù)器端程序設(shè)計(jì)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!