如何使服务器端支持多个客户端同时工作?
服务器端改成接收到一个连接,便新启动一个线程响应,具体代码如下:
在Java API中,实现UDP方式的编程,包含客户端网络编程和服务器端网络编程,主要由两个类实现,分别是:
DatagramSocket,DatagramSocket类实现“网络连接”,包括客户端网络连接和服务器端网络连接。虽然UDP方式的网络通讯不需要建立专用的网络连接,但是毕竟还是需要发送和接收数据,DatagramSocket实现的就是发送数据时的发射器,以及接收数据时的监听器的角色。类比于TCP中的网络连接,该类既可以用于实现客户端连接,也可以用于实现服务器端连接。
DatagramPacket,DatagramPacket类实现对于网络中传输的数据封装,也就是说,该类的对象代表网络中交换的数据。在UDP方式的网络编程中,无论是需要发送的数据还是需要接收的数据,都必须被处理成DatagramPacket类型的对象,该对象中包含发送到的地址、发送到的端口号以及发送的内容等。其实DatagramPacket类的作用类似于现实中的信件,在信件中包含信件发送到的地址以及接收人,还有发送的内容等,邮局只需要按照地址传递即可。在接收数据时,接收到的数据也必须被处理成DatagramPacket类型的对象,在该对象中包含发送方的地址、端口号等信息,也包含数据的内容。和TCP方式的网络传输相比,IO编程在UDP方式的网络编程中变得不是必须的内容,结构也要比TCP方式的网络编程简单一些。
一个服务器端一般都需要同时为多个客户端提供通讯,如果需要同时支持多个客户端,则必须使用前面介绍的线程的概念。简单来说,也就是当服务器端接收到一个连接时,启动一个专门的线程处理和该客户端的通讯。
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_first
Thread id 8 recieve client_second
Thread id 8 recieve client_third
Thread id 9 recieve client_first
Thread id 9 recieve client_second
Thread id 9 recieve client_third
运行效果(先启动服务器端程序,然后执行两次客户端程序)(客户端的打印结果):
server1
server2
server3
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();
}
}
}