題目
1. 功能概述
實現一個支持併發服務的網絡運算服務器程序。該服務器能夠同時接收來自於多個客戶端的運算請求,然後根據運算類型和請求參數完成實際的運算,最後把運算結果返
回給客戶端。
2. 具體要求
(1)至少支持加、減、乘、除四種基本運算。
(2)服務器端能夠分別記錄已經成功處理的不同運算類型請求的個數。
(2)客戶端與服務器端之間基於 UDP 協議進行通信。
(3)應用層協議自行設計。
源碼
// 服務端程序
import java.net.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class Counter { //運算符數目類
private int counter;
public Counter() {
counter=0;
}
public synchronized void increase() {
counter++;
}
public int getCounter() {
return counter;
}
}
class ThreadCounter { //線程數目類
private int counter;
public ThreadCounter() {
counter=0;
}
public synchronized void increase() {
counter++;
}
public synchronized void decrease() {
counter--;
}
public synchronized boolean compare(int n){ // 比較線程數目
return counter < n;
}
public void print(){
System.out.println("當前進程數目:"+ counter);
}
}
class PacketType{ // 數據包類型
private String op; // 運算符
private int num1;
private int num2;
public PacketType(String a,int b, int c) {
op = a;
num1 = b;
num2 = c;
}
public double computer(){
try {
if (op.equals("+")) {
UDPServer.operator[0].increase();
return num1 + num2;
}
if (op.equals("-")) {
UDPServer.operator[1].increase();
return num1 - num2;
}
if (op.equals("*")) {
UDPServer.operator[2].increase();
return num1 * num2;
}
if (op.equals("/")) {
UDPServer.operator[3].increase();
return (double) num1 / num2;
}
}catch (Exception e){
e.printStackTrace();
return -1;
}
System.out.println("Error Input");
return -1;
}
}
class WorkThread implements Runnable { //工作線程
public static byte[] double2Bytes(double d) { // 將浮點數類型轉換爲字節數組
long value = Double.doubleToRawLongBits(d);
byte[] byteRet = new byte[8];
for (int i = 0; i < 8; i++) {
byteRet[i] = (byte) ((value >> 8 * i) & 0xff);
}
return byteRet;
}
public void run() {
try {
while (true) {
DatagramPacket m = UDPServer.InputQueue.take();
String t = new String(m.getData()).substring(0,m.getLength());
String[] s = t.split("t");
String op = s[0];
int num1 = Integer.parseInt(s[1]);
int num2 = Integer.parseInt(s[2]);
PacketType n = new PacketType(op, num1, num2);
double ans = n.computer();
byte[] ans_byte = double2Bytes(ans);
System.out.println("此次運算結果: " + ans);
DatagramPacket res = new DatagramPacket(ans_byte, ans_byte.length,m.getAddress(),m.getPort());
UDPServer.OutputQueue.put(res);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
UDPServer.ThreadNumber.decrease();
}
}
}
class SendThread implements Runnable { //發送線程
public void run() {
while(true) {
try {
DatagramPacket m = UDPServer.OutputQueue.take();
UDPServer.bSocket.send(m);
// 打印當前接受的運算符數目
int [] num = new int[4];
for (int i = 0; i < 4; i++) {
num[i] = UDPServer.operator[i].getCounter();
}
UDPServer.ThreadNumber.print();
System.out.println("運算符數目如下");
System.out.println("+:"+num[0]);
System.out.println("-:"+num[1]);
System.out.println("*:"+num[2]);
System.out.println("/:"+num[3]);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class UDPServer{
// 全局變量的聲明以及定義
public static Counter[] operator = new Counter[4]; // 運算符計數器
public static ThreadCounter ThreadNumber = new ThreadCounter();
public static int MaxThreadNumber = 2; // 定義最大線程數
@SuppressWarnings("unchecked")
public static BlockingQueue<DatagramPacket> InputQueue = new LinkedBlockingQueue();
@SuppressWarnings("unchecked")
public static BlockingQueue<DatagramPacket> OutputQueue = new LinkedBlockingQueue();
public static DatagramSocket aSocket = null;
public static DatagramSocket bSocket = null;
public static void main(String args[]){
for(int i=0;i<4;i++){
operator[i] = new Counter();
}
try{
// DatagramPacket 公用一個buffer 可能出現意想不到的bug(例如之後讀出數據時getData() 方法的坑)
byte[] buffer = new byte[1000];
/*
aSocket:主線程接受報文,監聽6789端口
bSocket: 發送線程發送報文,使用隨機端口
*/
aSocket = new DatagramSocket(6789);
bSocket = new DatagramSocket();
SendThread sendthread = new SendThread();
Thread sendt = new Thread(sendthread); // 發送線程開始工作
sendt.start();
while(true){
//接受請求
DatagramPacket request = new DatagramPacket(buffer, buffer.length);
aSocket.receive(request);
// 請求入隊
InputQueue.put(request);
//創建工作線程 執行運算任務
if (ThreadNumber.compare(MaxThreadNumber)) {
WorkThread workthread = new WorkThread();
Thread workt = new Thread(workthread);
workt.start();
ThreadNumber.increase();
}
}
} catch (Exception e){
e.printStackTrace();
} finally {
if (aSocket != null) aSocket.close();
}
}
}
// 客戶端程序
import java.net.*;
import java.io.*;
public class UDPClient{
public static double bytes2Double(byte[] arr) { // 字節數組轉換爲double類型
long value = 0;
for (int i = 0; i < 8; i++) {
value |= ((long) (arr[i] & 0xff)) << (8 * i);
}
return Double.longBitsToDouble(value);
}
public static void main(String args[]){
DatagramSocket aSocket = null;
String userInput = null;
try {
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
aSocket = new DatagramSocket();
InetAddress aHost = InetAddress.getByName("127.0.0.1");
int serverPort = 6789;
while(!(userInput=stdIn.readLine()).equals("quit")) { // 輸入quit時退出客戶端程序
byte[] m = userInput.getBytes();
// 創建發送報文
DatagramPacket request = new DatagramPacket(m, m.length, aHost, serverPort);
aSocket.send(request);
byte[] buffer = new byte[1000];
// 準備接收報文
DatagramPacket reply = new DatagramPacket(buffer, buffer.length);
aSocket.receive(reply);
System.out.println("Reply: " + bytes2Double(reply.getData()));
}
} catch (SocketException e){
System.out.println("Socket: " + e.getMessage());
} catch (IOException e){
System.out.println("IO: " + e.getMessage());
} finally {
if(aSocket != null) aSocket.close();
}
}
}