网络编程简介(1)之JAVA网络编程

作为整个网络编程系列的第一篇,在这篇文章里,使用JAVA语言编写了一个小例子,实现了一个可以提供时间查询服务的Server端,和一个客户端。
服务端代码结构如下:
这里写图片描述
TimeServer是启动类,启动后监听请求,每当收到一个socket请求时,TimeServer类就新建一个Handler线程来处理该请求,具体的就是由TimeServerHandler来处理这个请求。
TimeServer类代码如下:

package bio.sample;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class TimeServer {

    public static void main(String[] args) {
        int port = 8070;
        if(args != null && args.length > 0)
        {
            port = Integer.valueOf(args[0]);
        }
        ServerSocket server = null;
        try{
            server = new ServerSocket(port);
            System.out.println("The time Server is start in port :"+ port);
            Socket socket = null;
            while(true)
            {
                socket = server.accept();
                new Thread(new TimeServerHandler(socket)).start();
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        try {
            server.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

TimeServerHandler类代码如下:

package bio.sample;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;

public class TimeServerHandler implements Runnable {

    private Socket socket;
    public TimeServerHandler(Socket socket)
    {
        this.socket = socket;
    }
    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            //PrintWriter构造方法的第二个参数,如果为 true,则 println、printf 或 format 方法将刷新输出缓冲区
            out = new PrintWriter(this.socket.getOutputStream(),true);
            String currentTime = null;
            String body = null;
            while(true)
            {
                body = in.readLine();
                if(body != null && body.length() != 0)
                    break;              
            }
            System.out.println("the time server receive order:"+body);
            currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? 
                    new Date(System.currentTimeMillis()).toString():"BAD ORDER";
            out.println(currentTime);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            in.close();
            out.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

客户端代码结构如下:
这里写图片描述
客户端代码分为两个类,启动类为TimeClient,这个类会不断的新建线程,并让新建的线程去请求TimeServer,同时记录开始时间与结束时间。
TimeClient代码如下:

package bio.sample;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;

public class TimeClient {

    private static int PORT           = 8070;
    private static String inataddress = "192.168.192.151";

    public static void main(String[] args) {    
        for(int i = 100;i <= 2000;i+=100)
        {
           test(i);     
           System.gc();
        }

    }

    private static void test(int THREAD_SIZE) {
        CountDownLatch startSignal = new CountDownLatch(THREAD_SIZE);
        CountDownLatch doneSignal = new CountDownLatch(THREAD_SIZE);
        Thread[] trs = new Thread[THREAD_SIZE];
        for(int i = 0;i < THREAD_SIZE;i++)
        {
            trs[i] = new Thread(new TimeClientThread(inataddress,PORT,startSignal,doneSignal));
        }
        long start_ms = System.currentTimeMillis();
        for(int i = 0;i < THREAD_SIZE;i++)
            trs[i].start();
        try {
            doneSignal.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        long end_ms   = System.currentTimeMillis();
        System.out.println("|"+THREAD_SIZE+"|"+ (end_ms - start_ms)+"|");
    }

}

TimeClientThread代码如下

package bio.sample;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;

public class TimeClientThread implements Runnable {

    private int port;
    private String serverAddress;
    private CountDownLatch startSignal;
    private CountDownLatch doneSignal;
    public TimeClientThread(String inatAddres,int serverPort, CountDownLatch sSignal, CountDownLatch dSignal)
    {
        port = serverPort;
        serverAddress = inatAddres;
        startSignal = sSignal;
        doneSignal  = dSignal;
    }
    @Override
    public void run() {
        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;

        try{
            startSignal.countDown();
            startSignal.await();
            socket = new Socket(serverAddress,port);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(),true);
            out.println("QUERY TIME ORDER");            
            //System.out.println("Send order to server");
            String resp = in.readLine();
            //System.out.print("Now is" + resp);
            doneSignal.countDown();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        try {
            in.close();
            out.close();
            socket.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

我们可以看到,在TimeClient端我们可以开启大量的线程去同时访问服务端,然后记录时间,就可以得到对服务端性能的一个简单评估。
然后我们将代码部署在两个主机上,两台主机都是百兆网卡,位于同一局域网,RTT<1ms;
其中服务端运行参数如下:-Xmx2G -Xms1000M
客户端运行参数如下:-Xmx1000m -Xms200M
然后先启动服务端,再启动客户端,结果如下:

线程数耗费时间(ms)
10052
20054
30080
40087
500107
600146
700130
800142
900168
1000184
1200230
1400259
1600302
1800332
2000380

然后我们对代码进行修改,使得每个线程在创建后,就立马开始发出请求,不再等待其他请求一起开始。即将TimeClientThread中的如下代码注释掉。

//          startSignal.countDown();
//          startSignal.await();

实验结果如下:

线程数耗费时间(ms)
10042
20053
30065
40075
50091
600120
700109
800123
900135
1000149
1200183
1400209
1600239
1800264
2000299
2500370
3000444
3500521
4000596
4500858
5000743
6000887
70001036
80001181
90001328
100001472

由上图可以看到由于不必所有线程同时开始,所以我们在实验室中可以启动的最大线程数就变多了,可以看到我们可以启动大约10000个线程,但是,在时间上和同时开始,并没有过大的区别。
所以我们可以估计整个程序的性能瓶颈并不是在客户端的请求方式,而是在服务端接受并处理请求的方式上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值