计网实验二:Socket通信编程TCP/UDP(Java)
下面的代码还设计了一个简单的登录界面和发送消息界面
TCP
Socket编程客户端的主要步骤
1.创建一个Socket实例,指定连接的服务器Socket地址和端口号。
2.通过Socket实例获取输入输出流,并使用输入输出流进行数据传输。
3.通信完成后,关闭Socket连接。
Socket编程服务器端的主要步骤
1.创建一个服务器Socket实例,指定监听的端口号
2.启动服务器Socket,并等待客户端Socket的连接请求。
3.建立Socket连接后,通过Socket实例进行数据传输。
4.通信完成后,关闭Socket连接
Server:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class Server {
private static Map<String, PrintWriter> clients = new HashMap<>();
private static Map<String, String> users = new HashMap<>();
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(12345)) {
System.out.println("服务器已启动,等待客户端连接...");
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("客户端连接成功");
ClientHandler clientHandler = new ClientHandler(clientSocket);
Thread thread = new Thread(clientHandler);
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
static class ClientHandler implements Runnable {
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
private String username;
private long loginTime;
public ClientHandler(Socket socket) {
this.clientSocket = socket;
}
public void run() {
try {
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new PrintWriter(clientSocket.getOutputStream(), true);
// 读取用户名和密码
username = in.readLine();
String password = in.readLine();
if (isValidUser(username, password)) {
loginTime = System.currentTimeMillis();
System.out.println("用户 " + username + " 登录成功"+ "! 登录时间:" + getFormattedTime(loginTime));
out.println("登录成功,欢迎 " + username + "! 登录时间:" + getFormattedTime(loginTime));
// 添加客户端到客户端列表
synchronized (clients) {
clients.put(username, out);
}
// 接收并广播消息
String message;
while ((message = in.readLine()) != null) {
System.out.println(username + ": " + message);
broadcastMessage(username + ": " + message);
}
} else {
System.out.println("用户 " + username + " 登录失败");
out.println("登录失败,请检查用户名和密码");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 移除客户端并关闭连接
synchronized (clients) {
clients.remove(username);
}
clientSocket.close();
System.out.println("用户 " + username + " 已退出");
broadcastMessage("用户 " + username + " 已退出");
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static boolean isValidUser(String username, String password) {
users.put("bu", "21121157");
users.put("user", "12345");
return users.containsKey(username) && users.get(username).equals(password);
}
private void broadcastMessage(String message) {
// 向所有客户端广播消息
synchronized (clients) {
for (PrintWriter clientWriter : clients.values()) {
clientWriter.println(message);
}
}
}
private String getFormattedTime(long time) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(time);
return sdf.format(date);
}
}
}
client:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.Socket;
public class Client {
private static Socket socket;
private static BufferedReader in;
private static PrintWriter out;
private static String username;
private JFrame frame;
private JTextField usernameField;
private JPasswordField passwordField;
private JButton loginButton;
private JButton exitButton;
private JTextField messageField;
private JButton sendButton;
private JButton sendFileButton;
private JTextArea chatArea;
public Client() {
initialize();
connectToServer();
}
private void initialize() {
frame = new JFrame("Chat App");
frame.setPreferredSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel loginPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
usernameField = new JTextField(20);
passwordField = new JPasswordField(20);
loginButton = new JButton("登录");
exitButton = new JButton("退出");
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.CENTER;
gbc.insets = new Insets(20, 10, 20, 10);
loginPanel.add(new JLabel("欢迎使用聊天应用"), gbc);
gbc.gridwidth = 1;
gbc.gridy++;
gbc.anchor = GridBagConstraints.EAST;
loginPanel.add(new JLabel("用户名:"), gbc);
gbc.gridy++;
loginPanel.add(new JLabel("密码:"), gbc);
gbc.gridx++;
gbc.gridy--;
gbc.anchor = GridBagConstraints.WEST;
loginPanel.add(usernameField, gbc);
gbc.gridy++;
loginPanel.add(passwordField, gbc);
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.CENTER;
loginPanel.add(loginButton, gbc);
gbc.gridy++;
loginPanel.add(exitButton, gbc);
frame.add(loginPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
loginButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
login();
}
});
exitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
exit();
}
});
}
private void connectToServer() {
try {
socket = new Socket("localhost", 12345);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
} catch (Exception e) {
e.printStackTrace();
}
}
private void login() {
username = usernameField.getText();
String password = new String(passwordField.getPassword());
out.println(username);
out.println(password);
try {
String serverResponse = in.readLine();
if (serverResponse.startsWith("登录成功")) {
frame.getContentPane().removeAll();
frame.setLayout(new BorderLayout());
chatArea = new JTextArea();
chatArea.setEditable(false);
chatArea.setLineWrap(true);
JScrollPane scrollPane = new JScrollPane(chatArea);
frame.add(scrollPane, BorderLayout.CENTER);
JPanel inputPanel = new JPanel(new BorderLayout());
frame.add(inputPanel, BorderLayout.SOUTH);
messageField = new JTextField();
inputPanel.add(messageField, BorderLayout.CENTER);
sendButton = new JButton("发送消息");
sendButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sendMessage();
}
});
inputPanel.add(sendButton, BorderLayout.EAST);
sendFileButton = new JButton("发送文件");
sendFileButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sendFile();
}
});
inputPanel.add(sendFileButton, BorderLayout.WEST);
JButton exitChatButton = new JButton("退出");
exitChatButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
exit();
}
});
inputPanel.add(exitChatButton, BorderLayout.SOUTH);
frame.pack();
frame.setSize(800, 600);
frame.revalidate();
frame.repaint();
displayMessage(serverResponse);
} else {
JOptionPane.showMessageDialog(frame, serverResponse, "登录失败", JOptionPane.ERROR_MESSAGE);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void exit() {
try {
socket.close();
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendMessage() {
String message = messageField.getText();
if (!message.isEmpty()) {
out.println(username + ": " + message);
messageField.setText("");
displayMessage(username + ": " + message);
}
}
private void sendFile() {
JFileChooser fileChooser = new JFileChooser();
int result = fileChooser.showOpenDialog(null);
if (result == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
try {
BufferedReader fileReader = new BufferedReader(new FileReader(selectedFile));
String fileLine;
while ((fileLine = fileReader.readLine()) != null) {
out.println(fileLine);
}
fileReader.close();
displayMessage("文件已发送: " + selectedFile.getName());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void displayMessage(String message) {
chatArea.append(message + "\n");
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Client();
}
});
}
}
UDP
Server
import java.io.IOException;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class ServerU {
private static Map<String, InetAddress> clients = new HashMap<>();
private static Map<String, String> users = new HashMap<>();
public static void main(String[] args) {
DatagramSocket socket = null;
try {
socket = new DatagramSocket(12345);
byte[] receiveData = new byte[1024];
System.out.println("服务器已启动,等待客户端连接...");
while (true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
String message = new String(receivePacket.getData(), 0, receivePacket.getLength());
if (message.startsWith("LOGIN:")) {
String[] loginInfo = message.split(":");
if (loginInfo.length == 3) {
String username = loginInfo[1];
String password = loginInfo[2];
if (isValidUser(username, password)) {
long loginTime = System.currentTimeMillis();
System.out.println("用户 " + username + " 登录成功" + "! 登录时间:" + getFormattedTime(loginTime));
sendMessage("登录成功,欢迎 " + username + "! 登录时间:" + getFormattedTime(loginTime), clientAddress, clientPort);
// 添加客户端到客户端列表
synchronized (clients) {
clients.put(username, clientAddress);
}
} else {
System.out.println("用户 " + username + " 登录失败");
sendMessage("登录失败,请检查用户名和密码", clientAddress, clientPort);
}
}
} else if (message.startsWith("MSG:")) {
String[] msgInfo = message.split(":");
if (msgInfo.length == 3) {
String username = msgInfo[1];
String msg = msgInfo[2];
String formattedMessage = username + ": " + msg;
// 接收并广播消息
System.out.println(formattedMessage);
broadcastMessage(formattedMessage, username);
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null && !socket.isClosed()) {
socket.close();
}
}
}
// 这里简单设计了一个用户验证,创建了两个用户信息
private static boolean isValidUser(String username, String password) {
users.put("bu", "211211");
users.put("user", "12345");
return users.containsKey(username) && users.get(username).equals(password);
}
private static void sendMessage(String message, InetAddress address, int port) throws IOException {
DatagramSocket socket = new DatagramSocket();
byte[] sendData = message.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, port);
socket.send(sendPacket);
socket.close();
}
private static void broadcastMessage(String message, String senderUsername) throws IOException {
// 向所有客户端广播消息
synchronized (clients) {
for (Map.Entry<String, InetAddress> entry : clients.entrySet()) {
if (!entry.getKey().equals(senderUsername)) {
sendMessage(message, entry.getValue(), 12345);
}
}
}
}
private static String getFormattedTime(long time) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(time);
return sdf.format(date);
}
}
Client
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.*;
public class ClientU {
private static DatagramSocket socket;
private static InetAddress serverAddress;
private static int serverPort = 12345;
private static String username;
private JFrame frame;
private JTextField usernameField;
private JPasswordField passwordField;
private JButton loginButton;
private JButton exitButton;
private JTextField messageField;
private JButton sendButton;
private JButton sendFileButton;
private JTextArea chatArea;
public ClientU() {
initialize();
connectToServer();
}
private void initialize() {
frame = new JFrame("Chat App");
frame.setPreferredSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel loginPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(10, 10, 10, 10);
usernameField = new JTextField(20);
passwordField = new JPasswordField(20);
loginButton = new JButton("登录");
exitButton = new JButton("退出");
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.CENTER;
gbc.insets = new Insets(20, 10, 20, 10);
loginPanel.add(new JLabel("欢迎使用"), gbc);
gbc.gridwidth = 1;
gbc.gridy++;
gbc.anchor = GridBagConstraints.EAST;
loginPanel.add(new JLabel("用户名:"), gbc);
gbc.gridy++;
loginPanel.add(new JLabel("密码:"), gbc);
gbc.gridx++;
gbc.gridy--;
gbc.anchor = GridBagConstraints.WEST;
loginPanel.add(usernameField, gbc);
gbc.gridy++;
loginPanel.add(passwordField, gbc);
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.CENTER;
loginPanel.add(loginButton, gbc);
gbc.gridy++;
loginPanel.add(exitButton, gbc);
frame.add(loginPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
loginButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
login();
}
});
exitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
exit();
}
});
}
private void connectToServer() {
try {
socket = new DatagramSocket();
serverAddress = InetAddress.getByName("localhost");
} catch (Exception e) {
e.printStackTrace();
}
}
private void login() {
username = usernameField.getText();
String password = new String(passwordField.getPassword());
String loginMessage = "LOGIN:" + username + ":" + password;
sendUDPMessage(loginMessage);
try {
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);
String serverResponse = new String(receivePacket.getData(), 0, receivePacket.getLength());
if (serverResponse.startsWith("登录成功")) {
frame.getContentPane().removeAll();
frame.setLayout(new BorderLayout());
chatArea = new JTextArea();
chatArea.setEditable(false);
chatArea.setLineWrap(true);
JScrollPane scrollPane = new JScrollPane(chatArea);
frame.add(scrollPane, BorderLayout.CENTER);
JPanel inputPanel = new JPanel(new BorderLayout());
frame.add(inputPanel, BorderLayout.SOUTH);
messageField = new JTextField();
inputPanel.add(messageField, BorderLayout.CENTER);
sendButton = new JButton("发送消息");
sendButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sendMessage();
}
});
inputPanel.add(sendButton, BorderLayout.EAST);
sendFileButton = new JButton("发送文件");
sendFileButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sendFile();
}
});
inputPanel.add(sendFileButton, BorderLayout.WEST);
JButton exitChatButton = new JButton("退出");
exitChatButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
exit();
}
});
inputPanel.add(exitChatButton, BorderLayout.SOUTH);
frame.pack();
frame.setSize(800, 600);
frame.revalidate();
frame.repaint();
displayMessage(serverResponse);
} else {
JOptionPane.showMessageDialog(frame, serverResponse, "登录失败", JOptionPane.ERROR_MESSAGE);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void exit() {
socket.close();
System.exit(0);
}
private void sendMessage() {
String message = messageField.getText();
if (!message.isEmpty()) {
String chatMessage = "MSG:" + username + ":" + message;
sendUDPMessage(chatMessage);
messageField.setText("");
displayMessage(username + ": " + message);
}
}
private void sendFile() {
JFileChooser fileChooser = new JFileChooser();
int result = fileChooser.showOpenDialog(null);
if (result == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
try {
BufferedReader fileReader = new BufferedReader(new FileReader(selectedFile));
String fileLine;
while ((fileLine = fileReader.readLine()) != null) {
String fileMessage = "MSG:" + username + ":" + fileLine;
sendUDPMessage(fileMessage);
}
fileReader.close();
displayMessage("文件已发送: " + selectedFile.getName());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void displayMessage(String message) {
chatArea.append(message + "\n");
}
private void sendUDPMessage(String message) {
try {
byte[] sendData = message.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);
socket.send(sendPacket);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ClientU();
}
});
}
}
将客户端发给服务器的字符串大写后回显到客户端
Server:
import java.net.*;
import java.util.Arrays;
public class UDPServer {
public static void main(String[] args) {
DatagramSocket socket = null;
try {
socket = new DatagramSocket(12345);
while (true) {
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
String receivedMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());
// 转换字符串为大写
String uppercaseMessage = receivedMessage.toUpperCase();
byte[] sendData = uppercaseMessage.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, clientAddress, clientPort);
socket.send(sendPacket);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket != null && !socket.isClosed()) {
socket.close();
}
}
}
}
Client:
import java.net.*;
import java.util.Scanner;
public class UDPClient {
public static void main(String[] args) {
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
InetAddress serverAddress = InetAddress.getByName("localhost");
int serverPort = 12345;
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("输入消息 (或输入 'exit' 退出): ");
String message = scanner.nextLine();
if (message.equals("exit")) {
break;
}
byte[] sendData = message.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, serverPort);
socket.send(sendPacket);
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
socket.receive(receivePacket);
String receivedMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("服务器响应: " + receivedMessage);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket != null && !socket.isClosed()) {
socket.close();
}
}
}
}
TCP和UDP编程的主要差异和特点。
连接的建立与终止:
TCP:TCP是一种面向连接的协议。在数据传输之前,它需要通过“三次握手”过程建立连接。在数据传输完成后,需要通过“四次挥手”过程终止连接。
UDP:UDP是无连接的协议。发送数据之前不需要建立连接,也无需终止连接。
数据传输:
TCP:TCP提供可靠的数据传输。它使用确认、超时和重传等机制来确保数据包的顺序和完整性。
UDP:UDP不保证数据传输的可靠性。数据包可能会丢失或在接收方处以不同的顺序到达。
数据检查:
TCP:TCP提供错误检查机制,能够检测数据在传输过程中是否发生了损坏。
UDP:UDP没有错误检查机制。
流量控制:
TCP:TCP提供流量控制机制,可以防止发送方超过接收方的处理能力。
UDP:UDP没有流量控制机制。
传输方式:
TCP:TCP是面向字节流的。它将数据看作是无边界的字节流,不关心数据的内部结构。
UDP:UDP是面向数据报的。它保留数据的边界,每个数据报都被单独处理。
头部开销:
TCP:TCP的头部开销比UDP大。TCP头部固定为20字节,而UDP头部固定为8字节。
UDP:UDP的头部开销较小,因此更适合于传输小数据包。
编程接口:
TCP:TCP使用套接字(socket)编程接口,常用的编程语言都提供了对套接字的支持。
UDP:UDP同样使用套接字编程接口,但使用不同的套接字类型(DGRAM套接字)。
应用场景:
TCP:TCP适用于需要可靠、有序和错误检查的数据传输场景,如网页浏览、电子邮件等。
UDP:UDP适用于对实时性要求较高的应用,如视频通话、在线游戏等,其中数据包丢失或重排对应用的影响较小。
注:以上代码和信息主要是本人对实验的简单记录,还有如果不知道怎么运行的,我用的是eclipse,使用的时候可以开两个IDE,一个用来运行Server,一个用来运行Client。