实验目的
实验环境
·具有Internet连接的主机
·Java环境 + IDEA
实验内容
实验步骤
1. URL请求程序
要求
打印所请求网页的URL、存储文件名、文件大小等信息
思路
利用Scanner类获取用户输入的URL,并利用URL和HttpURLConnection类,通过HTTP GET方法访问指定URL的网页内容,并将获取到的内容保存为HTML文件。如果请求成功,则输出请求的URL、保存的文件名和文件大小;如果请求失败,则输出错误信息。
实现
package Computer_Network_lab4.q1;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
public class Program1 {
public static void main(String[] args) throws Exception{
//输入url
Scanner scanner = new Scanner(System.in);
System.out.print("Please input a URL: ");
String url = scanner.next();
URL pageUrl = new URL(url);
//打开与url的连接
HttpURLConnection connection = (HttpURLConnection) pageUrl.openConnection();
connection.setRequestMethod("GET");
//文件保存路径
String filename = "D:\\IntelliJ_IDEA\\IdeaProjects\\Internet_Programming\\src\\main\\java\\Computer_Network_lab4\\q1\\page";
//获取http响应码
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream inputStream = connection.getInputStream();
// 创建文件输出流,用于保存获取到的网页内容
FileOutputStream outputStream = new FileOutputStream(filename + ".html");
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.close();
inputStream.close();
File file = new File(filename + ".html");
long fileSize = file.length();
System.out.println("The requested url: " + url);
System.out.println("The saved file: " + file.getName());
System.out.println("The requested file size: " + fileSize + " bytes");
} else {
System.out.println("Error: HTTP response code - " + responseCode);
}
connection.disconnect();
}
}
运行效果
实验指导范例:
我的实现效果:
2. 系统时间查询
要求
·实现一个基于客户/服务器的系统时间查询程序。
·传输层使用TCP。
·交互过程
a. 客户端向服务器端发送字符串”Time”。
b. 服务器端收到该字符串后,返回当前系统时间。
c. 客户端向服务器端发送字符串”Exit”。
d. 服务器端返回”Bye”,然后结束TCP连接
思路
a. 服务端
利用Socket连接接受客户端的请求,服务器监听指定端口,等待客户端连接。一旦有客户端连接成功,服务器从客户端读取请求,如果请求是 “Time”,则服务器返回当前的日期时间;如果请求是 “Exit”,则发送 “Bye” 并关闭与客户端的连接;如果请求是其他无效命令,则发送 “Invalid command”。通过Socket进行数据的读取和发送,使用 BufferedReader 和 PrintWriter 类来处理输入和输出流。
b. 客户端
首先创建一个客户端套接字并连接到指定的服务器地址和端口。然后创建一个输入流(BufferedReader)用于从服务器接收数据,并创建一个输出流(PrintWriter)用于向服务器发送数据。接下来,向服务器发送 “Time” 命令,以请求服务器返回当前的系统时间。然后通过输入流读取服务器返回的响应,并将响应存储在 response 变量中,并打印出服务器返回的当前系统时间。之后,发送 “Exit” 命令给服务器,表示希望退出连接。然后再次通过输入流读取服务器返回的响应,并将响应存储在 response 变量中,并打印出服务器返回的响应消息。最后释放相关的资源。
实现
a. 服务端
package Computer_Network_lab4.q2;
import java.net.*;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Server {
public static void main(String[] args) {
int port = 12345;
try {
// 创建服务器套接字并绑定到指定端口
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("--------------------------------------");
System.out.println("The server is ready to connect.");
System.out.println("The server address: (\'" + InetAddress.getLocalHost().getHostAddress() + "\', " + serverSocket.getLocalPort() + ")");
System.out.println("--------------------------------------");
while (true) {
// 接受客户端连接请求
Socket clientSocket = serverSocket.accept();
System.out.println("Accepted a new connection.");
System.out.println("The connection address: (\'" + InetAddress.getLocalHost().getHostAddress() + "\', " + serverSocket.getLocalPort() + ")");
System.out.println("The client address: (\'" + clientSocket.getInetAddress().getHostAddress() + "\', " + clientSocket.getPort() + ")");
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
String inputLine;
while ((inputLine = in.readLine()) != null) {
//收到时间请求
if (inputLine.equals("Time")) {
System.out.println("Received a request: Time.");
LocalDateTime currentTime = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedTime = currentTime.format(formatter);
out.println(formattedTime);
System.out.println("Send a response: " + formattedTime + ".");
}
//收到退出请求
else if (inputLine.equals("Exit")) {
System.out.println("Received a request: Exit.");
out.println("Bye");
System.out.println("Send a response: Bye.");
System.out.println("--------------------------------------");
break;
}
//无效命令
else {
out.println("Invalid command");
}
}
in.close();
out.close();
clientSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
b. 客户端
package Computer_Network_lab4.q2;
import java.io.*;
import java.net.*;
public class Client {
public static void main(String[] args) {
String serverAddress = "localhost";
int port = 12345;
try {
// 创建客户端套接字并连接到指定的服务器地址和端口
Socket socket = new Socket(serverAddress, port);
System.out.println("A client is running.");
System.out.println("The client address: (\'" + InetAddress.getLocalHost().getHostAddress() + "\', "+ socket.getLocalPort() +")");
System.out.println("Connecting to " + serverAddress + ":" + port + ".");
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
// 发送时间请求
out.println("Time");
System.out.println("Send a request: Time.");
String response = in.readLine();
System.out.println("Received the current system time on the server: " + response + ".");
// 发送退出请求
out.println("Exit");
System.out.println("Send a request: Exit.");
response = in.readLine();
System.out.println("Received a response: " + response);
in.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行效果
实验指导范例:
我的实现效果:
3. 网络文件传输
要求
实现一个基于客户端/服务器的网络文件传输程序, 传输层使用TCP, 打印服务器与客户端的交互过程.
交互过程:
a. 客户端从用户输入获得待请求的文件名.
b. 客户端向服务器发送文件名.
c. 服务器收到文件名后传输文件.
d. 客户端接收文件, 重命名并存储在硬盘.
思路
a. 服务端
首先创建一个服务器套接字并绑定到指定的端口来监听客户端连接。一旦有客户端连接成功,则会接收客户端发送的文件名,并根据文件名在指定路径下查找对应的文件。如果文件存在,会向客户端发送 “FileFound” 的响应,并逐块将文件内容发送给客户端。如果文件不存在,会向客户端发送 “FileNotFound” 的响应。通过使用输入流和输出流来处理数据的读取和发送,并在传输过程中输出一些调试信息。
b. 客户端
首先创建一个套接字并指定服务器的地址和端口来与服务器建立连接。一旦连接成功,用户可以输入一个文件名,并将文件名发送给服务器。然后等待服务器的响应。如果服务器回复 “FileFound”,表示文件存在,客户端将创建一个文件输出流来接收从服务器传输过来的文件内容,并将其保存到本地文件中。通过使用输入流从套接字接收文件数据,并逐块写入文件输出流。同时,客户端会输出接收到的数据块的大小。如果服务器回复 “FileNotFound”,表示文件不存在,客户端会相应地打印出文件不存在的消息。
实现
a. 服务端
package Computer_Network_lab4.q3;
import java.net.*;
import java.io.*;
public class Server {
public static void main(String[] args) throws IOException{
int port = 12345;
ServerSocket serverSocket = null;
Socket clientSocket = null;
try {
serverSocket = new ServerSocket(port);
System.out.println("--------------------------------------");
System.out.println("The server is ready to connect.");
System.out.println("--------------------------------------");
// 等待客户端连接
clientSocket = serverSocket.accept();
System.out.println("Accepted a new connection.");
// 创建输入流和输出流
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
// 接收客户端发送的文件名
String fileName = in.readLine();
System.out.println("Received a filename '" + fileName + "'.");
// 根据文件名创建文件对象
File file = new File("D:\\IntelliJ_IDEA\\" +
"IdeaProjects\\Internet_Programming" +
"\\src\\main\\java\\Computer_Network_lab4\\q3\\"
+ fileName);
long fileSize = file.length();
// 如果文件存在,则逐块读取文件内容并发送给客户端
if (file.exists()) {
out.println("FileFound");
FileInputStream fileInputStream = new FileInputStream(file);
byte[] buffer = new byte[64];
int bytesRead;
OutputStream outputStream = clientSocket.getOutputStream();
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
outputStream.flush();
System.out.println("Sent a chunk of " + bytesRead + " bytes.");
}
fileInputStream.close();
outputStream.close();
System.out.println("Send a file of " + fileSize + " bytes.");
System.out.println("Done!");
} else {
out.println("FileNotFound");
}
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
clientSocket.close();
serverSocket.close();
}
}
}
b. 客户端
package Computer_Network_lab4.q3;
import java.net.*;
import java.io.*;
public class Client {
public static void main(String[] args) {
String serverAddress = "127.0.0.1";
int port = 12345;
try {
System.out.println("A client is running.");
Socket socket = new Socket(serverAddress, port);
System.out.println("Connecting to " + serverAddress + ":" + port + ".");
// 创建输入流和输出流
BufferedReader userInput = new BufferedReader(new InputStreamReader(System.in));
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
System.out.print("Please input a filename to request: ");
String fileName = userInput.readLine();
System.out.println("Send the filename '" + fileName + "' to the server.");
out.println(fileName);
// 读取服务器的响应
String response = in.readLine();
if (response.equals("FileFound")) {
// 如果服务器回复 "FileFound",创建文件输出流
FileOutputStream fileOutputStream = new FileOutputStream("D:\\IntelliJ_IDEA\\" +
"IdeaProjects\\Internet_Programming\\" +
"src\\main\\java\\Computer_Network_lab4\\q3\\received_" + fileName);
byte[] buffer = new byte[64];
int bytesRead;
InputStream inputStream = socket.getInputStream();
// 接收并写入文件内容
while ((bytesRead = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, bytesRead);
System.out.println("Received a chunk of " + bytesRead + " bytes.");
}
// 创建接收到的文件对象
File receivedFile = new File("D:\\IntelliJ_IDEA\\IdeaProjects" +
"\\Internet_Programming\\src\\main\\java" +
"\\Computer_Network_lab4\\q3\\received_" + fileName);
long fileSize = receivedFile.length();
System.out.println("--------------------------------------");
System.out.println("A file name '" + receivedFile.getName() + "' of " + fileSize + " bytes is saved.");
System.out.println("Done!");
System.out.println("--------------------------------------");
fileOutputStream.close();
} else if (response.equals("FileNotFound")) {
System.out.println("File does not exist.");
}
// 释放相关资源
in.close();
out.close();
userInput.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行效果
实验指导范例:
我的实现效果:
4. 网络聊天室
要求
实现一个基于客户端/服务器的网络聊天程序, 传输层使用UDP, 能实现多个用户的群聊, 客户端打印聊天信息, 服务器打印系统信息
思路
服务端
书写一个类:ClientInfo类,用于表示客户端的信息,包括用户名、地址和端口。整个服务端核心使用DatagramSocket接收和发送数据报,允许多个客户端通过UDP协议与服务器进行通信。服务器接收来自客户端的消息,并根据消息内容执行相应的操作。客户端可以注册加入聊天,发送消息给其他客户端,并可以发送"QUIT"消息退出聊天。服务器通过start()方法启动并监听指定的端口,接收客户端发送的数据报。每当接收到消息时,它会调用handleClientMessage()方法处理该消息。如果消息是"QUIT",服务器处理客户端退出;如果消息以"REGISTER:“开头,服务器处理新客户端加入;否则,服务器将该消息转发给所有其他客户端。
b. 客户端
客户端使用DatagramSocket创建一个UDP套接字,并获取本地地址和端口。然后,用户输入用户名,并将其保存到username变量中,然后向服务器发送注册请求,将用户名通过UDP数据报发送到服务器。然后客户端创建一个接收线程,该线程循环接收服务器发送的UDP数据报,并将其打印到控制台上。同时,在主线程中,客户端循环读取用户在控制台上输入的消息,并将其发送到服务器。如果用户输入"quit”,客户端向服务器发送退出信息,并终止客户端程序。
实现
a. 服务端
package Computer_Network_lab4.q4;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
public class Server {
private static final int PORT = 12345;
private static final int BUFFER_SIZE = 1024;
private DatagramSocket socket; //用于接收和发送数据报
private List<ClientInfo> clients; //存储客户信息
public void start() {
try {
socket = new DatagramSocket(PORT);
System.out.println("Server started on port " + PORT);
// 用户列表
clients = new ArrayList<>();
while (true) {
//接受数据的缓冲区
byte[] buffer = new byte[BUFFER_SIZE];
//接收客户端发送的数据报,填充packet的缓冲区
DatagramPacket packet = new DatagramPacket(buffer, BUFFER_SIZE);
socket.receive(packet);
//将接收到的字节数组转换为字符串
String message = new String(packet.getData(), 0, packet.getLength());
handleClientMessage(packet.getAddress().getHostAddress(), packet.getPort(), message);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null && !socket.isClosed()) {
socket.close();
}
}
}
// 处理客户端发来的消息
private void handleClientMessage(String clientAddress, int clientPort, String message) throws IOException {
// 处理客户端请求退出的情况
if (message.equals("QUIT")) {
handleClientQuit(clientAddress, clientPort);
}
// 处理新用户加入的情况
else if (message.startsWith("REGISTER:")) {
// 取出用户名
String username = message.substring(9).trim();
// 新建用户
ClientInfo clientInfo = new ClientInfo(username, clientAddress, clientPort);
clients.add(clientInfo);
System.out.println("Client " + username + " joined the chat!");
//
sendMessage(clientInfo, clientInfo.getUsername() + " has joined the chat!");
for (ClientInfo client : clients) {
if (!client.equals(clientInfo)) {
sendMessage(clientInfo, client.getUsername() + " has joined the chat!");
}
}
}
else {
ClientInfo sender = findClient(clientAddress, clientPort);
if (sender != null) {
System.out.println("Received message from " + sender.getUsername() + ": " + message);
for (ClientInfo client : clients) {
if (!client.equals(sender)) {
System.out.println("Send message to " + client.getUsername() + ": " + message);
}
}
}
sendMessageToAllClients(sender.getUsername() + ": " + message);
}
}
// 找出发送信息的客户端
private ClientInfo findClient(String clientAddress, int clientPort) {
for (ClientInfo client : clients) {
if (client.getAddress().equals(clientAddress) && client.getPort() == clientPort) {
return client;
}
}
return null;
}
// 将某一个用户发送的信息发送到所有未退出的客户端
private void sendMessageToAllClients(String message) throws IOException {
for (ClientInfo client : clients) {
sendMessage(client, message);
}
}
// 发送信息
private void sendMessage(ClientInfo client, String message) throws IOException {
byte[] buffer = message.getBytes();
InetAddress address = InetAddress.getByName(client.getAddress());
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, client.getPort());
socket.send(packet);
}
//处理用户退出
private void handleClientQuit(String clientAddress, int clientPort) throws IOException {
ClientInfo client = findClient(clientAddress, clientPort);
if (client != null) {
// 删除用户
clients.remove(client);
System.out.println("Client " + client.getUsername() + " has quit the chat.");
sendMessageToAllClients(client.getUsername() + " has left the chat.");
}
}
// main方法,启动服务器
public static void main(String[] args) {
Server server = new Server();
server.start();
}
// 这个对象用于表示客户端用户信息
private static class ClientInfo {
private String username;
private String address;
private int port;
public ClientInfo(String username, String address, int port) {
this.username = username;
this.address = address;
this.port = port;
}
public String getUsername() {
return username;
}
public String getAddress() {
return address;
}
public int getPort() {
return port;
}
}
}
b. 客户端
(本次实验共使用了三个客户端,这里只展示其中一个,因为三个客户端区别只在于类名不同)
package Computer_Network_lab4.q4;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class Client1{
private static final int SERVER_PORT = 12345;
private static final int BUFFER_SIZE = 1024;
private String username; //用户名
private InetAddress serverAddress;
private DatagramSocket socket;
public void start() {
try {
socket = new DatagramSocket();
serverAddress = InetAddress.getLocalHost();
System.out.println("Your client is created at port " + socket.getLocalPort());
// 用户名输入
System.out.print("Please input your nickname: ");
Scanner scanner = new Scanner(System.in);
username = scanner.nextLine();
// 向服务器发送注册请求
register();
// 创建接收线程,用于接收服务器发送的消息
Thread receiveThread = new Thread(() -> {
try {
while (true) {
byte[] buffer = new byte[BUFFER_SIZE];
DatagramPacket packet = new DatagramPacket(buffer, BUFFER_SIZE);
socket.receive(packet);
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println(message);
}
} catch (IOException e) {
e.printStackTrace();
}
});
receiveThread.start();
// 循环读取用户在控制台输入的消息,并发送给服务器
while (true) {
String message = scanner.nextLine();
sendMessageToServer(message);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null && !socket.isClosed()) {
socket.close();
}
}
}
private void register() throws IOException {
// 向服务器发送新用户的用户名
String registerMessage = "REGISTER:" + username;
byte[] buffer = registerMessage.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, serverAddress, SERVER_PORT);
socket.send(packet);
}
private void sendMessageToServer(String message) throws IOException {
// 向服务器发送退出信息
if (message.equalsIgnoreCase("quit")) {
String quitMessage = "QUIT";
byte[] buffer = quitMessage.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, serverAddress, SERVER_PORT);
socket.send(packet);
// 终止客户端程序
System.exit(0);
}
//普通信息
else {
byte[] buffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, serverAddress, SERVER_PORT);
socket.send(packet);
}
}
// main方法,启动客户端
public static void main(String[] args) {
Client1 client1 = new Client1();
client1.start();
}
}
运行效果
实验指导范例:
客户端:
服务端:
我的实现效果:
客户端1:
客户端2:
客户端3:
服务端:
实验小结
在本次实验中,我实现了四个基于Java的网络程序:URL请求程序、系统时间查询、网络文件传输和网络聊天室。
1. URL请求程序:
这个程序使用Java的URL类和URLConnection类来发送HTTP请求并接收响应。通过使用这些类,我能够与远程服务器建立连接,发送GET或POST请求,并获取服务器返回的数据。这个程序让我更好地理解了HTTP请求的过程,并学会了如何通过Java代码与Web服务器进行交互。
2. 系统时间查询:
这个程序使用Java的Socket和ServerSocket类建立了客户端和服务器之间的连接,通过流传输时间数据。通过使用Socket和ServerSocket类,我了解了TCP协议在系统时间查询中的应用,并掌握了使用Java进行简单网络通信的技巧。
3. 网络文件传输:
这个程序实现了基于TCP协议的网络文件传输功能。它使用Java的Socket和ServerSocket类来建立客户端和服务器之间的连接,并通过流传输文件数据。我可以选择发送文件或接收文件,通过网络传输大型文件或者任意类型的文件。这个程序让我了解了TCP协议在文件传输中的应用,并学会了如何使用Java进行网络文件传输。
4. 网络聊天室:
这个程序实现了一个简单的网络聊天室,基于UDP协议。通过使用Java的DatagramSocket和DatagramPacket类来实现消息的广播和接收。我能够在不同的客户端之间进行实时的消息传递,并在控制台上显示聊天记录。这个程序展示了UDP协议在实时通信中的应用,并让我了解了基于UDP的简单聊天系统的实现原理。
通过实现这些网络程序,我获得了以下收获:
·熟悉了Java的网络编程 API,如URL、URLConnection、Socket和ServerSocket等类的使用。
·加深了对HTTP请求和响应的理解,学会了通过Java代码与Web服务器进行交互。
·学会了使用网络通信获取服务器的时间信息,并进行时间处理。
·理解了TCP协议在文件传输中的应用,学会了使用Java进行网络文件传输。
·了解了UDP协议在实时通信中的应用,学会了实现简单的网络聊天室。
总体而言,通过这些实验,我加深了对网络编程概念和Java网络编程API的理解,并掌握了一些常见网络应用的实现方法。这些知识和技能对于开发网络应用和理解网络通信的原理都非常有用。