文章目录
- 一、如何实现TCP通信
- 二、编写C/S架构聊天程序
-
- (一)编写服务器端程序 - Server.java
- (二)编写客户端程序 - Client.java
- (三)测试服务器端与客户端能否通信
- (四)程序优化思路 - 服务器端采用多线程
一、如何实现TCP通信
- 要实现TCP通信需要创建一个服务器端程序和一个客户端程序,为了保证数据传输的安全性,首先需要实现服务器端程序,然后在编写客户端程序。
- 在本机运行服务器端程序,在远程机运行客户端程序
- 本机的IP地址:
192.168.129.222
- 远程机的IP地址:
192.168.214.213
二、编写C/S架构聊天程序
(一)编写服务器端程序 - Server.java
- 在
net.hw.network
包里创建Server
类
package net.hw.network;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server extends JFrame {
static final int PORT = 8136;
static final String HOST_IP = "192.168.129.222";
private JPanel panel1, panel2;
private JTextArea txtContent, txtInput, txtInputIP;
private JScrollPane panContent, panInput;
private JButton btnClose, btnSend;
private ServerSocket serverSocket;
private Socket socket;
private DataInputStream netIn;
private DataOutputStream netOut;
public static void main(String[] args) {
new Server();
}
public Server() {
super("服务器");
panel1 = new JPanel();
panel2 = new JPanel();
txtContent = new JTextArea(15, 60);
txtInput = new JTextArea(3, 60);
panContent = new JScrollPane(txtContent, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
panInput = new JScrollPane(txtInput, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
btnClose = new JButton("关闭");
btnSend = new JButton("发送");
getContentPane().add(panContent, "Center");
getContentPane().add(panel1, "South");
panel1.setLayout(new GridLayout(0, 1));
panel1.add(panInput);
panel1.add(panel2);
panel2.add(btnSend);
panel2.add(btnClose);
txtContent.setEditable(false);
txtContent.setFont(new Font("宋体", Font.PLAIN, 13));
txtInput.setFont(new Font("宋体", Font.PLAIN, 15));
txtContent.setLineWrap(true);
txtInput.setLineWrap(true);
txtInput.requestFocus();
setSize(450, 350);
setLocation(50, 200);
setResizable(false);
setVisible(true);
try {
txtContent.append("服务器已启动...\n");
serverSocket = new ServerSocket(PORT);
txtContent.append("等待客户请求...\n");
socket = serverSocket.accept();
txtContent.append("连接一个客户。\n" + socket + "\n");
netIn = new DataInputStream(socket.getInputStream());
netOut = new DataOutputStream(socket.getOutputStream());
} catch (IOException e1) {
e1.printStackTrace();
}
txtContent.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
displayClientMsg();
}
});
txtInput.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
displayClientMsg();
}
});
panel2.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
displayClientMsg();
}
});
txtInput.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
displayClientMsg();
}
});
txtInput.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
displayClientMsg();
}
});
btnSend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
String serverMsg = txtInput.getText();
if (!serverMsg.trim().equals("")) {
txtContent.append("服务器>" + serverMsg + "\n");
netOut.writeUTF(serverMsg);
} else {
JOptionPane.showMessageDialog(null, "不能发送空信息!", "服务器", JOptionPane.WARNING_MESSAGE);
}
txtInput.setText("");
txtInput.requestFocus();
} catch (IOException ie) {
ie.printStackTrace();
}
}
});
btnClose.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
try {
netIn.close();
netOut.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
System.exit(0);
}
});
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
try {
netIn.close();
netOut.close();
socket.close();
serverSocket.close();
} catch (IOException ie) {
ie.printStackTrace();
}
System.exit(0);
}
public void windowActivated(WindowEvent e) {
txtInput.requestFocus();
}
});
}
void displayClientMsg() {
try {
if (netIn.available() > 0) {
String clientMsg = netIn.readUTF();
txtContent.append("客户端>" + clientMsg + "\n");
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
(二)编写客户端程序 - Client.java
- 在
net.hw.network
包里创建Client
类
package net.hw.network;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
public class Client extends JFrame {
private JPanel panel1, panel2;
private JTextArea txtContent, txtInput;
private JScrollPane panContent, panInput;
private JButton btnClose, btnSend;
private Socket socket;
private DataInputStream netIn;
private DataOutputStream netOut;
public static void main(String[] args) {
new Client();
}
public Client() {
super("客户端");
panel1 = new JPanel();
panel2 = new JPanel();
txtContent = new JTextArea(15, 60);
txtInput = new JTextArea(3, 60);
panContent = new JScrollPane(txtContent, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
panInput = new JScrollPane(txtInput, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
btnClose = new JButton("关闭");
btnSend = new JButton("发送");
getContentPane().add(panContent, "Center");
getContentPane().add(panel1, "South");
panel1.setLayout(new GridLayout(0, 1));
panel1.add(panInput);
panel1.add(panel2);
panel2.add(btnSend);
panel2.add(btnClose);
txtContent.setEditable(false);
txtContent.setFont(new Font("宋体", Font.PLAIN, 13));
txtInput.setFont(new Font("宋体", Font.PLAIN, 15));
txtContent.setLineWrap(true);
txtInput.setLineWrap(true);
txtInput.requestFocus();
setSize(450, 350);
setLocation(550, 200);
setResizable(false);
setVisible(true);
try {
txtContent.append("连接服务器...\n");
socket = new Socket(Server.HOST_IP, Server.PORT);
txtContent.append("连接服务器成功。\n" + socket + "\n");
netIn = new DataInputStream(socket.getInputStream());
netOut = new DataOutputStream(socket.getOutputStream());
} catch (IOException e1) {
JOptionPane.showMessageDialog(null, "服务器连接失败!\n请先启动服务器程序!", "客户端", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
txtContent.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
displayServerMsg();
}
});
txtInput.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
displayServerMsg();
}
});
panel2.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
displayServerMsg();
}
});
txtInput.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
displayServerMsg();
}
});
txtInput.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
displayServerMsg();
}
});
btnSend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
String clientMsg = txtInput.getText();
if (!clientMsg.trim().equals("")) {
netOut.writeUTF(clientMsg);
txtContent.append("客户端>" + clientMsg + "\n");
} else {
JOptionPane.showMessageDialog(null, "不能发送空信息!", "客户端", JOptionPane.WARNING_MESSAGE);
}
txtInput.setText("");
txtInput.requestFocus();
} catch (IOException ie) {
ie.printStackTrace();
}
}
});
btnClose.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
netIn.close();
netOut.close();
socket.close();
} catch (IOException ie) {
ie.printStackTrace();
}
System.exit(0);
}
});
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
try {
netIn.close();
netOut.close();
socket.close();
} catch (IOException ie) {
ie.printStackTrace();
}
System.exit(0);
}
public void windowActivated(WindowEvent e) {
txtInput.requestFocus();
}
});
}
void displayServerMsg() {
try {
if (netIn.available() > 0) {
String serverMsg = netIn.readUTF();
txtContent.append("服务器>" + serverMsg + "\n");
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
(三)测试服务器端与客户端能否通信
- 在本机[
192.168.129.222
]上启动服务器端
- 在远程机[
192.168.214.213
]上启动客户端
- 显示连接服务器[
192.168.129.222
]成功,切换到服务器端查看,显示连接了一个客户[192.168.214.213
]
- 此时,服务器端和客户端就可以相互通信了
(四)程序优化思路 - 服务器端采用多线程
- 其实,很多服务器端程序都是允许被多个应用程序访问的,例如门户网站可以被多个用户同时访问,因此服务器端都是多线程的。