您的位置:首页 > 理论基础 > 计算机网络

java网络编程(二)复用Socket连接以及使用多线程完成多个客户端的连接

2016-01-26 16:20 507 查看
在前面的示例中,客户端中建立了一次连接,只发送一次数据就关闭了,这就相当于拨打电话时,电话打通了只对话一次就关闭了,其实更加常用的应该是拨通一次电话以后多次对话,这就是复用客户端连接。

那 么如何实现建立一次连接,进行多次数据交换呢?其实很简单,建立连接以后,将数据交换的逻辑写到一个循环中就可以了。这样只要循环不结束则连接就不会被关 闭。按照这种思路,可以改造一下上面的代码,让该程序可以在建立连接一次以后,发送三次数据,当然这里的次数也可以是多次,示例代码如下:

[code]public class SocketClient {
    public static void main(String[] args) {
        Socket socket = null;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        String data[] = { "hello world1", "hello world2", "hello world3" };
        try {
            // 创建Socket实例
            socket = new Socket("hadoop", 10000);
            // 获取输出流,向服务器发生数据
            outputStream = socket.getOutputStream();
            /// 获取输入流,获取服务器的响应
            inputStream = socket.getInputStream();
            byte[] buff = new byte[1024];
            // 循环发送
            for (int i = 0; i < data.length; i++) {
                // 发送数据
                outputStream.write(data[i].getBytes());
                int len = inputStream.read(buff);
                // 打印服务端的相应
                System.out.println(new String(buff, 0, len));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            try {
                inputStream.close();
                outputStream.close();
                socket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}


现在客户端可以向服务器发生三次请求且获取三次相应,而我们服务器端是对话一次数据以后就关闭了连接,如果服务器端程序关闭了,客户端继续发送数据肯定会出现异常

按照客户端实现的逻辑,也可以复用服务器端的连接,实现的原理也是将服务器端的数据交换逻辑写在循环中即可,按照该种思路改造以后的服务器端代码为:

[code]public class SocketService {

    public static void main(String[] args) {
         InputStream inputStream =null;
         OutputStream outputStream =null;
       Socket socket = null;
       ServerSocket serviceSocket =null;
        try {
            //服务器端接收消息的类。定制端口号为8888
             serviceSocket = new ServerSocket(10000);
            //获取socket。这个方法是阻塞式的
            socket = serviceSocket.accept();
          inputStream = socket.getInputStream();
          byte buf[] = new byte[1024];
          //向客户端生成响应
            outputStream = socket.getOutputStream();
          for(int i=0;i<3;i++){
              int len =inputStream.read(buf);
            //打印客户端的消息
                System.out.println(new String(buf,0,len));
                outputStream.write("收到".getBytes());
          }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
             //释放资源
              try {
                     outputStream.close();
                     inputStream.close();
                socket.close();
                serviceSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }


在该示例代码中,也将数据发送和接收的逻辑写在了一个for循环内部,只是在实现时硬性的将循环次数规定成了3次,这样代码虽然比较简单,但是通用性比较差,客户端和服务器端的次数控制还不够灵活,如果客户端的次数不固定怎么办呢?不着急,这个问题会一一进行解答。

现在我们学习另外一个网络编程的突出问题。

如何使服务器端支持多个客户端同时工作?

前面介绍的服务器端程序,只是实现了概念上的服务器端,离实际的服务器端程序结构距离还很遥远,如果需要让服务器端能够实际使用,那么最需要解决的问题就是——如何支持多个客户端同时工作。

一个服务器端一般都需要同时为多个客户端提供通讯,如果需要同时支持多个客户端,则必须使用线程的来解决。简单来说,也就是当服务器端接收到一个连接时,启动一个专门的线程处理和该客户端的通讯。

按照这个思路改写的服务端示例程序将由两个部分组成,SocketService类实现服务器端控制,实现接收客户端连接,然后开启专门的逻辑线程处理该连接,LogicThread类实现对于一个客户端连接的逻辑处理,将处理的逻辑放置在该类的run方法中。该服务端示例的代码实现为:

[code]public class SocketService {

    public static void main(String[] args) {
       Socket socket = null;
       ServerSocket serviceSocket =null;
        try {
            //服务器端接收消息的类。定制端口号为8888
             serviceSocket = new ServerSocket(10000);
             System.out.println("服务器已经启动");
             while(true){
                    //获取socket。这个方法是阻塞式的
                    socket = serviceSocket.accept();
                    //一但获取连接后就开子线程来处理
                  new LogicThread(socket);
             }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
             //释放资源
              try {
                serviceSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }


[code]public class LogicThread extends Thread {

    Socket socket;
    InputStream inputStream;
    OutputStream outputStream;

    public LogicThread(Socket socket) {
        this.socket = socket;
        start(); // 启动线程
    }
    public void run() {
        byte[] buf = new byte[1024];
        try {
            // 初始化流
            outputStream = socket.getOutputStream();
            inputStream = socket.getInputStream();
            for (int i = 0; i < 3; i++) {
                // 读取数据
                int len = inputStream.read(buf);
                System.out.println(new String(buf,0,len));
                // 反馈数据
                outputStream.write("收到".getBytes());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close();
        }
    }
    /**
     * 
     * 关闭流和连接
     * 
     */
    private void close() {
        try {
            // 关闭流和连接
            outputStream.close();
            inputStream.close();
            socket.close();
        } catch (Exception e) {
        }
    }
}


每次使用一个连接对象构造该线程,该连接对象就是该线程需要处理的连接,在线程构造完成以后,该线程就被启动起来了,然后在run方法内部对客户端连接进行处理,数据交换的逻辑和前面的示例代码一致,都是IO流处理。

这里的示例还只是基础的服务器端实现,在实际的服务器端实现中,由于硬件和端口数的限制,所以不能无限制的创建线程对象,而且频繁的创建线程对象效率也比较低,所以程序中都实现了线程池来提高程序的执行效率。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: