1个应用 = 1个服务器进程 + 1…N个客户端进程,其中服务器管理资源,并通过操作这种资源为客户端服务。
客户端-服务器模型中的基本操作是事务(transaction)(注:不同于数据库中的transaction)。
Step1:客户端向服务器发送一个请求,发起一个事务;
Step2:服务器收到请求后,进行解释,并以适当的方式操作它的资源;
Step3:服务器给客户端发送一个响应,并等待下一个请求;
Step4:客户端收到响应并处理。
Java客户端和服务端建立连接的过程可以分为以下几个步骤:
要实现一个需随时待命的服务器应用,因为不知道客户端什么时候会发来请求,此时,我们需要使用ServerSocket用来等待客户端的请求。一旦获得一个连接请求,就创建一个Socket对象来与服务器进行通信。
ServerSocket ss = new ServerSocket(port);
Socket s = ss.accept();
在Java中,Socket是用于建立网络连接的基本类。服务器客户端需要创建一个Socket对象,一个Socket类代表一个客户端套接字,以便与服务端建立连接。
Socket socket = new Socket("服务器IP地址", 服务器端口);
客户端需要获取Socket对象的输入输出流,以便向服务端发送请求和接收服务端的响应。
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
客户端向服务端发送请求,可以通过输出流将请求数据发送给服务端。
os.write("请求数据".getBytes());
服务端接收到客户端的请求后,会返回相应的数据。客户端可以通过输入流接收服务端的响应。
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, len));
}
客户端在完成请求和响应后,需要关闭与服务端的连接。
socket.close();
public class Server {
public static void main(String[] args) {
// 创建服务器
// 创建ServerSocket对象
try {
// 启动服务器
System.out.println("原神,启动!");
ServerSocket ss = new ServerSocket(8888);
// 等待客户,如果有可断来访问,就创建一个
// 针对该客户端的一个Socket对象
Socket s = ss.accept();
// 每个socket都是一个io对象
// 输入读取客户端发送过来的请求
InputStream is = s.getInputStream();
byte[]bytes = new byte[1024];
int len = 0;
len = is.read(bytes);
String req = new String(bytes, 0, len, "utf-8");
System.out.println(req);
// 响应服务器
OutputStream os = s.getOutputStream();
String resp = "请求已收到,这是返回响应";
os.write(resp.getBytes("utf-8"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public class Client {
public static void main(String[] args) throws IOException {
// 创建套接字对象访问服务器端口
Socket s = new Socket("127.0.0.1", 8888);
OutputStream os = s.getOutputStream();
os.write("这里是客户端,收到请回答".getBytes("utf-8"));
InputStream is = s.getInputStream();
int len = 0;
byte[]bytes = new byte[1024];
len = is.read(bytes);
System.out.println(new String(bytes, 0, len, "utf-8"));
}
}
现阶段的服务器大多都不会是1对1,而是为多个客户端提供服务,面向更多的用户,则需要使用多线程以及网络编程。代码示例如下:
ServerSocket ss = new ServerSocket(8888);
// 等待创建套接字
while (true) {
Socket s = ss.accept();
new ServerThread(s).start();
}
编写线程类,传递Socket对象,要实现的功能,就通过run方法调用,或者直接写在run方法中。
@Override
public void run() {
try {
// 文件的保存位置
String uuid = UUID.randomUUID().toString();
FileOutputStream fos = new FileOutputStream("img/" + uuid);
// 读取客户端发送的文件
byte[]bytes = new byte[1024];
int len = 0;
len = s.getInputStream().read(bytes);
fos.write(bytes, 0, len);
fos.close();
File file = new File("img/" + uuid);
file.renameTo(new File("img/" + uuid + ".png"));
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
在Java客户端和服务端建立连接的过程中,需要注意以下几点:
客户端需要知道服务端的IP地址和端口号,才能与服务端建立连接。IP地址是服务端的唯一标识符,端口号是服务端程序的监听端口。
在使用输入输出流时,需要注意及时关闭流,否则会导致资源泄露。
在建立连接的过程中,可能会出现各种异常。客户端需要对异常进行适当处理,以保证程序的稳定性。