Java中网络编程基础知识(二)

来源:互联网 发布:上海炫踪网络信息科技 编辑:程序博客网 时间:2024/05/29 13:18

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

   客户端的程序不需要改变,不过还是贴出来

复制代码
import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;import java.net.UnknownHostException;public class MulSocketClient {    public static void main(String[] args) {        Socket socket = null;        InputStream inputStream = null;        OutputStream outputStream = null;        String serverIP = "127.0.0.1";        int port = 10000;        String[] out_data = {"client_first", "client_second", "client_third"};                try {            socket = new Socket(serverIP, port);            outputStream = socket.getOutputStream();            inputStream = socket.getInputStream();            byte[] bytes = new byte[1024];            for (int i = 0; i < out_data.length; i++) {                // 发送数据                outputStream.write(out_data[i].getBytes());                // 接收数据并显示                int n = inputStream.read(bytes);                System.out.println(new String(bytes, 0, n));            }        } catch (UnknownHostException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            try {                outputStream.close();                inputStream.close();                socket.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }}
复制代码

    服务器端改成接收到一个连接,便新启动一个线程响应,具体代码如下:

复制代码
import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;public class MulThreadSocketServer {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        ServerSocket serverSocket = null;        Socket socket = null;        int port = 10000;        try {            serverSocket = new ServerSocket(port);            System.out.println("The service has started ... ");            while (true) {                socket = serverSocket.accept();                // start the thread                new Thread(new LogicThread(socket)).start();            }        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            try {                serverSocket.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }}class LogicThread implements Runnable {    InputStream inputStream = null;    OutputStream outputStream = null;    String[] data_out = {"server1", "server2", "server3"};    String in_message;    Socket socket;    public LogicThread(Socket socket) {        // TODO Auto-generated constructor stub        this.socket = socket;    }        @Override    public void run() {        // TODO Auto-generated method stub        try {            inputStream = socket.getInputStream();            outputStream = socket.getOutputStream();            byte[] bytes = new byte[1024];            for (int i = 0; i < data_out.length; i++) {                int n = inputStream.read(bytes);                in_message = new String(bytes, 0, n);                System.out.println("Thread id "+Thread.currentThread().getId()+" recieve "+in_message);                outputStream.write(data_out[i].getBytes());            }        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            try {                inputStream.close();                outputStream.close();                socket.close();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }    }
复制代码

    运行效果(先启动服务器端程序,然后执行两次客户端程序)(服务器端的打印结果):

复制代码
The service has started ... Thread id 8 recieve client_firstThread id 8 recieve client_secondThread id 8 recieve client_thirdThread id 9 recieve client_firstThread id 9 recieve client_secondThread id 9 recieve client_third
复制代码

    运行效果(先启动服务器端程序,然后执行两次客户端程序)(客户端的打印结果):

server1server2server3

 

    UDP方式的网络编程也在Java语言中获得了良好的支持,由于其在传输数据的过程中不需要建立专用的连接等特点,所以在Java API中设计的实现结构和TCP方式不太一样。当然,需要使用的类还是包含在java.net包中。
    在Java API中,实现UDP方式的编程,包含客户端网络编程和服务器端网络编程,主要由两个类实现,分别是:
    DatagramSocket,DatagramSocket类实现“网络连接”,包括客户端网络连接和服务器端网络连接。虽然UDP方式的网络通讯不需要建立专用的网络连接,但是毕竟还是需要发送和接收数据,DatagramSocket实现的就是发送数据时的发射器,以及接收数据时的监听器的角色。类比于TCP中的网络连接,该类既可以用于实现客户端连接,也可以用于实现服务器端连接。
    DatagramPacket,DatagramPacket类实现对于网络中传输的数据封装,也就是说,该类的对象代表网络中交换的数据。在UDP方式的网络编程中,无论是需要发送的数据还是需要接收的数据,都必须被处理成DatagramPacket类型的对象,该对象中包含发送到的地址、发送到的端口号以及发送的内容等。其实DatagramPacket类的作用类似于现实中的信件,在信件中包含信件发送到的地址以及接收人,还有发送的内容等,邮局只需要按照地址传递即可。在接收数据时,接收到的数据也必须被处理成DatagramPacket类型的对象,在该对象中包含发送方的地址、端口号等信息,也包含数据的内容。和TCP方式的网络传输相比,IO编程在UDP方式的网络编程中变得不是必须的内容,结构也要比TCP方式的网络编程简单一些。

    这里讨论一下Java中TCP方式与UDP方式的不同:这部分来自Java编程艺术
      UDP方式中,Datagram中必须指定股数据报的字节长度,这个长度不能超过64KB。而在TCP方式中则没有这个规定,传送的数据以流的形式,不必考虑其长度。因而UDP方式适用于数据长度固定而且字节长度小的情况;TCP方式适用于大批量集中式数据传输。
      UDP方式不保证数据报传输时的到达次序,而TCP方式则不存在这个问题。因而UDP方式适用于与数据接收次序无关的应用,TCP方式适用于有序数据流的传输。
      UDP方式无需进行专门的计算机间连接操作,而TCP方式在数据传输前必须首先建立计算机间的连接,而且这种连接必须在数据传输器件继续保持,否则抛出IOException。因而UDP方式用于无需监控实时连接状态的应用。
  UDP方式不能保证数据传输的可靠性,它适用于可靠度不高的数据传输应用,而且UDP方式速度快,消耗低,经常用于实时应用中。

    一个最简单的UDP传输的例子(客户端):

复制代码
import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.SocketException;import java.net.UnknownHostException;public class SimpleUDPclient {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        // 连接对象,DatagramSocket实现的就是发送数据时的发射器,以及接收数据时的监听器的角色。        // 类比于TCP中的网络连接,该类既可以用于实现客户端连接,也可以用于实现服务器端连接。        DatagramSocket socket = null;        // 发送数据报对象,在UDP方式的网络编程中,无论是需要发送的数据还是需要接收的数据,        // 都必须被处理成DatagramPacket类型的对象,        // 该对象中包含发送到的地址、发送到的端口号以及发送的内容等。        DatagramPacket send_packet = null;        // 接收数据包对象        DatagramPacket receive_packet = null;        // 服务器IP        String serverIP = "127.0.0.1";        // 服务器端口号        int port = 10000;        // 客户端发送数据内荣        String send_message = "Hello";        try {            // 该客户端连接使用系统随机分配的一个本地计算机的未用端口号。            // 在该连接中,不指定服务器端的IP和端口,            // 所以UDP方式的网络连接更像一个发射器,而不是一个具体的连接。            // 当然,可以通过制定连接使用的端口号来创建客户端连接。            socket = new DatagramSocket();            // 将发送的内容转换为byte数组            byte[] bytes = send_message.getBytes();            // 将服务器IP转换为InetAddress            InetAddress address = InetAddress.getByName(serverIP);            // 构造发送数据报的内容            send_packet = new DatagramPacket(bytes, bytes.length, address, port);            // 发送数据报            socket.send(send_packet);                        // 构造一个数据缓冲数组,该数组用于存储接收的服务器端反馈数据,            // 该数组的长度必须大于或等于服务器端反馈的实际有效数据的长度            byte[] re_bytes = new byte[1024];            // 以该缓冲数组为基础构造一个DatagramPacket数据包对象            receive_packet = new DatagramPacket(re_bytes, re_bytes.length);            // 接收数据内容            socket.receive(receive_packet);                        // 输出接收到的内容            byte[] message = receive_packet.getData();            // 缓冲区数组中只有一部分数据是反馈数据,            // 所以需要使用DatagramPacket对象中的getLength方法获得有效数据的长度            int length = receive_packet.getLength();            System.out.println(new String(message, 0, length));                    } catch (SocketException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (UnknownHostException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            socket.close();        }    }}
复制代码

     一个最简单的UDP传输的例子(服务器端):

复制代码
import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.SocketException;import java.net.UnknownHostException;public class SimpleUDPServer {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        // 连接对象,DatagramSocket实现的就是发送数据时的发射器,以及接收数据时的监听器的角色。        // 类比于TCP中的网络连接,该类既可以用于实现客户端连接,也可以用于实现服务器端连接。        DatagramSocket socket = null;        // 发送数据报对象,在UDP方式的网络编程中,无论是需要发送的数据还是需要接收的数据,        // 都必须被处理成DatagramPacket类型的对象,        // 该对象中包含发送到的地址、发送到的端口号以及发送的内容等。        DatagramPacket send_packet = null;        // 接收数据包对象        DatagramPacket receive_packet = null;        // 要监听端口号        int port = 10000;        // 服务器发送数据内容        String response_message = "World";        try {            // 建立连接,监听端口            socket = new DatagramSocket(port);                        // 构造一个数据缓冲数组,该数组用于存储接收的服务器端反馈数据,            // 该数组的长度必须大于或等于服务器端反馈的实际有效数据的长度            byte[] re_bytes = new byte[1024];            // 以该缓冲数组为基础构造一个DatagramPacket数据包对象            receive_packet = new DatagramPacket(re_bytes, re_bytes.length);            // 接收数据内容, 和TCP方式的网络编程类似,服务器端的receive方法是阻塞方法,            // 如果客户端不发送数据,则程序会在该方法处阻塞            socket.receive(receive_packet);                        // 输出接收到的内容            byte[] message = receive_packet.getData();            // 缓冲区数组中只有一部分数据是反馈数据,            // 所以需要使用DatagramPacket对象中的getLength方法获得有效数据的长度            int length = receive_packet.getLength();            System.out.println(new String(message, 0, length));                        // 将发送的内容转换为byte数组            byte[] bytes = response_message.getBytes();            // 获得客户端连接的InetAddress            InetAddress address = receive_packet.getAddress();            // 获得客户端连接的端口号            int client_port = receive_packet.getPort();            // 构造发送数据报的内容            send_packet = new DatagramPacket(bytes, bytes.length, address, client_port);            // 发送数据报            socket.send(send_packet);        } catch (SocketException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (UnknownHostException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            socket.close();        }    }}
复制代码

     运行效果:客户端发送Hello,服务器端响应World。

     一个支持多客户端的最简单UDP通信的例子(客户端)

复制代码
import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.SocketException;import java.net.UnknownHostException;public class MulUDPClient {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        DatagramSocket socket = null;        DatagramPacket send_packet = null;        DatagramPacket receive_packet = null;        String serverIP = "127.0.0.1";        int port = 10001;        String[] send_messages = {"first", "second", "third", "fourth"};                try {            socket = new DatagramSocket();            InetAddress address = InetAddress.getByName(serverIP);            for (int i = 0; i < send_messages.length; i++) {                byte[] bytes = send_messages[i].getBytes();                int length = bytes.length;                send_packet = new DatagramPacket(bytes, length, address, port);                socket.send(send_packet);                System.out.println(send_messages[i]+" is sent ... ");                                Thread.sleep(1000);                                byte[] re_bytes = new byte[1024];                receive_packet = new DatagramPacket(re_bytes, re_bytes.length);                socket.receive(receive_packet);                byte[] receive_bytes = receive_packet.getData();                int receive_bytes_length = receive_packet.getLength();                System.out.println(new String(receive_bytes, 0, receive_bytes_length));            }        } catch (SocketException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (UnknownHostException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } finally {            socket.close();        }    }}
复制代码

      一个支持多客户端的最简单UDP通信的例子(服务器端)

复制代码
import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.SocketException;public class MulThreadUDPServer {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        DatagramSocket socket = null;        DatagramPacket receive_packet;        int port = 10001;                        try {            socket = new DatagramSocket(port);            System.out.println("The server is running ... ");            while(true) {                byte[] bytes = new byte[1024];                receive_packet = new DatagramPacket(bytes, bytes.length);                socket.receive(receive_packet);                LogicUDPThread udpThread = new LogicUDPThread(socket, receive_packet, bytes);                udpThread.start();            }        } catch (SocketException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }finally {            socket.close();        }    } }class LogicUDPThread extends Thread {    private DatagramPacket packet;    private DatagramSocket socket;    private String message = "Roger";        public LogicUDPThread(DatagramSocket socket, DatagramPacket receive_packet, byte[] bytes) {        // TODO Auto-generated constructor stub        this.socket = socket;        this.packet = receive_packet;        System.out.println("the thread is started ... ");                System.out.println(new String(bytes, 0, bytes.length));    }    @Override    public void run() {        // TODO Auto-generated method stub        InetAddress address = packet.getAddress();        System.out.println(address.toString());        int port = packet.getPort();        try {            socket.send(new DatagramPacket(message.getBytes(), message.getBytes().length, address, port));        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}
复制代码

     运行效果(客户端):

复制代码
first is sent ... Rogersecond is sent ... Rogerthird is sent ... Rogerfourth is sent ... Roger
复制代码

     运行效果(服务器端):

复制代码
The server is running ... the thread is started ... first/127.0.0.1the thread is started ... second/127.0.0.1the thread is started ... third/127.0.0.1the thread is started ... fourth/127.0.0.1
复制代码
原创粉丝点击