关于网络编程上一章内容,可以参考:
https://blog.csdn.net/Raine_Yang/article/details/128697335?spm=1001.2014.3001.5501
使用服务器处理多个客户端
一般来说,同一服务器要持续运行处理多个客户端的请求。我们可以为每一个客户端请求分配一个单独的线程进行处理,从而实现处理多客户端
服务器端
package networking;
import java.io.*;
import java.net.*;
public class MultiThreadServer implements Runnable{
private int clientNo = 0;
private ServerSocket serverSocket;
public MultiThreadServer(int port) {
clientNo = 0;
try {
serverSocket = new ServerSocket(port);
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
while (true) {
Socket socket = serverSocket.accept();
clientNo++;
InetAddress inetAddress = socket.getInetAddress();
System.out.println(inetAddress.getHostName() + " " + inetAddress.getHostAddress());
new Thread(new HandleClient(socket)).start();
}
} catch (IOException e) {
System.err.println(e);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread server = new Thread(new MultiThreadServer(8000));
server.start();
}
}
1
public MultiThreadServer(int port) {
clientNo = 0;
try {
serverSocket = new ServerSocket(port);
} catch (IOException e) {
e.printStackTrace();
}
}
在指定port上新建服务器socket
2
while (true) {
Socket socket = serverSocket.accept();
clientNo++;
InetAddress inetAddress = socket.getInetAddress();
System.out.println(inetAddress.getHostName() + " " + inetAddress.getHostAddress());
new Thread(new HandleClient(socket)).start();
}
无限循环接受客户端请求。每当接收到客户端请求建立连接后创建新线程运行HandleClient
HandleClient程序:
package networking;
import java.net.*;
import java.io.*;
public class HandleClient implements Runnable{
private Socket socket;
public HandleClient(Socket socket) {
this.socket = socket;
}
public void run() {
try {
DataInputStream inputFromClient = new DataInputStream(socket.getInputStream());
DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream());
while (true) {
double radius = inputFromClient.readDouble();
double area = radius * radius * Math.PI;
outputToClient.writeDouble(area);
}
} catch(IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
该线程处理单个客户端请求,从客户端读入输入半径,返回圆的面积(和上一章同一示例)
客户端程序:
package networking;
import java.io.*;
import java.net.*;
public class Client implements Runnable{
private String hostName;
private int port;
private double radius;
public Client(String hostName, int port, double radius) {
this.hostName = hostName;
this.port = port;
this.radius = radius;
}
public void run() {
try {
Socket socket = new Socket(hostName, port);
DataOutputStream toServer = new DataOutputStream(socket.getOutputStream());
DataInputStream fromServer = new DataInputStream(socket.getInputStream());
while(true) {
try {
toServer.writeDouble(radius);
toServer.flush();
double area = fromServer.readDouble();
System.out.println("Area: " + area);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException ex) {
System.out.println(ex.toString());
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread client1 = new Thread(new Client("localhost", 8000, 5));
Thread client2 = new Thread(new Client("localhost", 8000, 6));
Thread client3 = new Thread(new Client("localhost", 8000, 7));
Thread client4 = new Thread(new Client("localhost", 8000, 8));
client1.start();
client2.start();
client3.start();
client4.start();
}
}
这里我们创建了4个客户端同时向服务器发送请求,执行效果如下:
可以看到服务器同时处理多个客户端请求
网络传输对象
在之前的示例中,我们一直在使用DataInputStream和DataOutputStream传输基础数据类型。要在客户端和服务器间传输对象我们需要使用ObjectInputStream和ObjectOutputStream实现
下一示例中客户端向服务器发送一个StudentAddress类对象,服务器得到对象后写入到一个名为student.dat的文件中
StudentAddress类:
package networking;
public class StudentAddress implements java.io.Serializable {
private String name;
private String street;
private String city;
public StudentAddress(String name, String street, String city) {
this.name = name;
this.street = street;
this.city = city;
}
public String getName() {
return name;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
}
服务器端:
package networking;
import java.io.*;
import java.net.*;
public class StudentServer implements Runnable{
public void run() {
ObjectInputStream inputFromClient = null;
ObjectOutputStream outputToFile = null;
try {
ServerSocket serverSocket = new ServerSocket(8000);
outputToFile = new ObjectOutputStream(new FileOutputStream("student.dat", true));
while (true) {
Socket socket = serverSocket.accept();
inputFromClient = new ObjectInputStream(socket.getInputStream());
Object object = inputFromClient.readObject();
outputToFile.writeObject(object);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputFromClient.close();
outputToFile.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread server = new Thread(new StudentServer());
server.start();
}
}
1
outputToFile = new ObjectOutputStream(new FileOutputStream("student.dat", true));
创建输出对象数据流,将数据写入student.dat文件
2
inputFromClient = new ObjectInputStream(socket.getInputStream());
Object object = inputFromClient.readObject();
outputToFile.writeObject(object);
创建对象输入数据流,从socket得到输入数据。利用readObject()方法获取对象,并通过writeObject()方法将对象输入文件
3
finally {
try {
inputFromClient.close();
outputToFile.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
这里要使用finally方法的目的是保证无论前面获取对象时是否抛出异常,输入流和输出流都可以正常关闭。
客户端:
package networking;
import java.net.*;
import java.io.*;
public class StudentClient implements Runnable {
public void run() {
try {
StudentAddress s1 = new StudentAddress("a", "a", "a");
Socket socket = new Socket("localhost", 8000);
ObjectOutputStream toServer = new ObjectOutputStream(socket.getOutputStream());
toServer.writeObject(s1);
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread client = new Thread(new StudentClient());
client.start();
}
}
1
ObjectOutputStream toServer = new ObjectOutputStream(socket.getOutputStream());
toServer.writeObject(s1);
在客户端创建数据输出流,并发送StudentAddress对象