尝试着用Java编写一个网络聊天程序,发现总不如网上写的好,所以就直接引用了网上大神的优秀代码。代码如下:

  1 package project1;
  2
  3 import java.awt.*;
  4 import java.awt.event.*;
  5 import java.io.*;
  6 import java.net.*;
  7 import java.util.ArrayList;
  8 import java.util.Iterator;
  9 import javax.swing.*;
 10
 11 public class Server {
 12     private JFrame serverFrame;
 13     private JLabel portLabel;
 14     private JLabel sayLabel;
 15     private JLabel nicknameLabel;
 16     private JTextField portText;
 17     private JTextField sayText;
 18     private JTextField nicknameText;
 19     private JButton startButton;
 20     private JButton sayButton;
 21     private JButton nicknameButton;
 22     private JPanel jPanelNorth;
 23     private JPanel jPanelSouth0;
 24     private JPanel jPanelSouth1;
 25     private JPanel jPanelSouth2;
 26     private JScrollPane scroller;
 27     private JTextArea serverTextArea;
 28     private ArrayList<PrintWriter> clientOutputStreams;
 29     private String nickname;
 30
 31     public static void main(String[] args) {
 32         Server aServer = new Server();
 33         aServer.startUp();
 34     }
 35
 36     // 初始化组件
 37     public Server() {
 38         nickname = "服务器";
 39
 40         serverFrame = new JFrame();
 41         jPanelNorth = new JPanel();
 42         portLabel = new JLabel("端口", JLabel.LEFT);
 43         portText = new JTextField(30);
 44         startButton = new JButton("开始");
 45         serverTextArea = new JTextArea();
 46         scroller = new JScrollPane(serverTextArea);
 47         nicknameLabel = new JLabel("昵称", JLabel.LEFT);
 48         nicknameText = new JTextField(nickname, 30);
 49         nicknameButton = new JButton("确认");
 50         jPanelSouth0 = new JPanel();
 51         jPanelSouth1 = new JPanel();
 52         jPanelSouth2 = new JPanel();
 53         sayLabel = new JLabel("消息", JLabel.LEFT);
 54         sayText = new JTextField(30);
 55         sayButton = new JButton("确认");
 56     }
 57
 58     // 构建GUI
 59     private void buildGUI() {
 60         // 窗口的设置
 61         serverFrame.setTitle("服务器");
 62         serverFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 63         serverFrame.setSize(550, 550);
 64
 65         // 北区的组件
 66         jPanelNorth.add(portLabel);
 67         jPanelNorth.add(portText);
 68         jPanelNorth.add(startButton);
 69         serverFrame.getContentPane().add(BorderLayout.NORTH, jPanelNorth);
 70
 71         // 中间的组件
 72         serverTextArea.setFocusable(false);
 73         scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
 74         scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
 75         serverFrame.getContentPane().add(BorderLayout.CENTER, scroller);
 76
 77         // 南区的组件
 78         jPanelSouth1.add(nicknameLabel);
 79         jPanelSouth1.add(nicknameText);
 80         jPanelSouth1.add(nicknameButton);
 81         jPanelSouth2.add(sayLabel);
 82         jPanelSouth2.add(sayText);
 83         jPanelSouth2.add(sayButton);
 84         jPanelSouth0.setLayout(new BoxLayout(jPanelSouth0, BoxLayout.Y_AXIS));
 85         jPanelSouth0.add(jPanelSouth1);
 86         jPanelSouth0.add(jPanelSouth2);
 87         serverFrame.getContentPane().add(BorderLayout.SOUTH, jPanelSouth0);
 88
 89         // 设置窗口可见
 90         serverFrame.setVisible(true);
 91     }
 92
 93     // 服务器运行
 94     public void startUp() {
 95         buildGUI();
 96
 97         // 监听Start按钮,建立端口
 98         ActionListener startListener = new ActionListener() {
 99             @Override
100             public void actionPerformed(ActionEvent e) {
101                 clientOutputStreams = new ArrayList<PrintWriter>();
102                 String aPort = portText.getText();
103
104                 if (aPort.equals("")) {
105                     JOptionPane.showMessageDialog(serverFrame, "请输入正确的端口号!");
106                 } else {
107                     try {
108                         // 等待客户端连接的线程
109                         Runnable serverRunnable = new Runnable() {
110                             @Override
111                             public void run() {
112                                 ServerSocket serverSocket;
113                                 try {
114                                     serverSocket = new ServerSocket(Integer.parseInt(aPort));
115                                     serverTextArea.append("正在等待客户端连接...\n");
116                                     while (true) {
117                                         Socket clientSocket = serverSocket.accept();
118                                         serverTextArea.append("客户端已连接...\n");
119
120                                         PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());
121                                         clientOutputStreams.add(writer);
122
123                                         Thread t = new Thread(new ClientHandler(clientSocket));
124                                         t.start();
125                                     }
126                                 } catch (NumberFormatException | IOException e) {
127                                     e.printStackTrace();
128                                 }
129                             }
130                         };
131                         Thread serverThread = new Thread(serverRunnable);
132                         serverThread.start();
133                     } catch (Exception ex) {
134                         ex.printStackTrace();
135                     }
136                 }
137             }
138         };
139         startButton.addActionListener(startListener);
140         portText.addActionListener(startListener);
141
142         // 监听nickname,设置昵称
143         ActionListener nicknameListener = new ActionListener() {
144             @Override
145             public void actionPerformed(ActionEvent e) {
146                 String aText = nicknameText.getText();
147                 if (!aText.equals("")) {
148                     nickname = aText;
149                 }
150             }
151         };
152         nicknameButton.addActionListener(nicknameListener);
153         nicknameText.addActionListener(nicknameListener);
154         nicknameText.addFocusListener(new FocusListener() {
155             @Override
156             public void focusGained(FocusEvent e) {
157             }
158
159             @Override
160             public void focusLost(FocusEvent e) {
161                 String aText = nicknameText.getText();
162                 if (!aText.equals("")) {
163                     nickname = aText;
164                 }
165             }
166         });
167
168         // 监听Say按钮,发送消息
169         ActionListener SayListener = new ActionListener() {
170             @Override
171             public void actionPerformed(ActionEvent e) {
172                 String aText = sayText.getText();
173                 if (!aText.equals("")) {
174                     aText = nickname + ":" + aText;
175                     sendToEveryClient(aText);
176                     serverTextArea.append(aText + "\n");
177                     sayText.setText("");
178                 } else {
179                     JOptionPane.showMessageDialog(serverFrame, "内容不能为空!");
180                 }
181             }
182         };
183         sayButton.addActionListener(SayListener);
184         sayText.addActionListener(SayListener);
185     }
186
187     // 多客户端的线程
188     public class ClientHandler implements Runnable {
189         BufferedReader bReader;
190         Socket aSocket;
191
192         public ClientHandler(Socket clientSocket) {
193             try {
194                 aSocket = clientSocket;
195                 InputStreamReader isReader = new InputStreamReader(aSocket.getInputStream());
196                 bReader = new BufferedReader(isReader);
197             } catch (Exception ex) {
198                 ex.printStackTrace();
199             }
200         }
201
202         @Override
203         public void run() {
204             String message;
205             try {
206                 while ((message = bReader.readLine()) != null) {
207                     sendToEveryClient(message);
208                     serverTextArea.append(message + "\n");
209                 }
210             } catch (Exception ex) {
211                 ex.printStackTrace();
212             }
213         }
214     }
215
216     // 发送消息给所有客户端的方法
217     private void sendToEveryClient(String message) {
218         Iterator<PrintWriter> it = clientOutputStreams.iterator();
219         while (it.hasNext()) {
220             try {
221                 PrintWriter writer = (PrintWriter) it.next();
222                 writer.println(message);
223                 writer.flush();
224             } catch (Exception ex) {
225                 ex.printStackTrace();
226             }
227         }
228     }
229
230 }
  1 package project1;
  2
  3 import java.awt.*;
  4 import java.awt.event.*;
  5 import java.io.*;
  6 import java.net.*;
  7
  8 import javax.swing.*;
  9
 10 public class Client {
 11     private JFrame clientFrame;
 12     private JLabel IPLabel;
 13     private JLabel PortLabel;
 14     private JLabel sayLabel;
 15     private JLabel nicknameLabel;
 16     private JTextField IPText;
 17     private JTextField PortText;
 18     private JTextField nicknameText;
 19     private JTextField sayText;
 20     private JButton connectButton;
 21     private JButton nicknameButton;
 22     private JButton sayButton;
 23     private JPanel jPanelNorth;
 24     private JPanel jPanelSouth0;
 25     private JPanel jPanelSouth1;
 26     private JPanel jPanelSouth2;
 27     private JTextArea clientTextArea;
 28     private JScrollPane scroller;
 29     private BufferedReader reader;
 30     private PrintWriter writer;
 31     private String nickname;
 32
 33     public static void main(String args[]) {
 34         Client aClient = new Client();
 35         aClient.startUp();
 36     }
 37
 38     // 初始化组件
 39     public Client() {
 40         nickname = "客户端";
 41
 42         clientFrame = new JFrame();
 43         jPanelNorth = new JPanel();
 44         IPLabel = new JLabel("服务器IP", JLabel.LEFT);
 45         IPText = new JTextField(10);
 46         PortLabel = new JLabel("服务器端口", JLabel.LEFT);
 47         PortText = new JTextField(10);
 48         connectButton = new JButton("连接");
 49         clientTextArea = new JTextArea();
 50         scroller = new JScrollPane(clientTextArea);
 51         jPanelSouth0 = new JPanel();
 52         jPanelSouth1 = new JPanel();
 53         jPanelSouth2 = new JPanel();
 54         nicknameLabel = new JLabel("昵称", JLabel.LEFT);
 55         nicknameText = new JTextField(nickname, 30);
 56         nicknameButton = new JButton("确认");
 57         sayLabel = new JLabel("消息", JLabel.LEFT);
 58         sayText = new JTextField(30);
 59         sayButton = new JButton("确认");
 60     }
 61
 62     // 构建GUI
 63     private void buildGUI() {
 64         // 窗口的设置
 65         clientFrame.setTitle("客户端");
 66         clientFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 67         clientFrame.setSize(550, 550);
 68
 69         // 北区的组件
 70         jPanelNorth.add(IPLabel);
 71         jPanelNorth.add(IPText);
 72         jPanelNorth.add(PortLabel);
 73         jPanelNorth.add(PortText);
 74         jPanelNorth.add(connectButton);
 75         clientFrame.getContentPane().add(BorderLayout.NORTH, jPanelNorth);
 76
 77         // 中间的组件
 78         clientTextArea.setFocusable(false);
 79         scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
 80         scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
 81         clientFrame.getContentPane().add(BorderLayout.CENTER, scroller);
 82
 83         // 南区的组件
 84         jPanelSouth1.add(nicknameLabel);
 85         jPanelSouth1.add(nicknameText);
 86         jPanelSouth1.add(nicknameButton);
 87         jPanelSouth2.add(sayLabel);
 88         jPanelSouth2.add(sayText);
 89         jPanelSouth2.add(sayButton);
 90         jPanelSouth0.setLayout(new BoxLayout(jPanelSouth0, BoxLayout.Y_AXIS));
 91         jPanelSouth0.add(jPanelSouth1);
 92         jPanelSouth0.add(jPanelSouth2);
 93         clientFrame.getContentPane().add(BorderLayout.SOUTH, jPanelSouth0);
 94
 95         // 设置窗口可见
 96         clientFrame.setVisible(true);
 97     }
 98
 99     // 客户端运行
100     public void startUp() {
101         buildGUI();
102
103         // 接收服务器消息的线程
104         Runnable incomingReader = new Runnable() {
105             @Override
106             public void run() {
107                 String message;
108                 try {
109                     while ((message = reader.readLine()) != null) {
110                         clientTextArea.append(message + "\n");
111                     }
112                 } catch (Exception ex) {
113                     ex.printStackTrace();
114                 }
115             }
116         };
117
118         // 监听Connect按钮,实现服务器的连接
119         connectButton.addActionListener(new ActionListener() {
120             @Override
121             public void actionPerformed(ActionEvent e) {
122                 String aServerIP = IPText.getText();
123                 String aServerPort = PortText.getText();
124
125                 if (aServerIP.equals("") || aServerPort.equals("")) {
126                     JOptionPane.showMessageDialog(clientFrame, "请输入 完整的 IP和端口!");
127                 } else {
128                     try {
129                         @SuppressWarnings("resource")
130                         Socket clientSocket = new Socket(aServerIP, Integer.parseInt(aServerPort));
131                         InputStreamReader streamReader = new InputStreamReader(clientSocket.getInputStream());
132                         reader = new BufferedReader(streamReader);
133                         writer = new PrintWriter(clientSocket.getOutputStream());
134
135                         clientTextArea.append("服务器已连接...\n");
136
137                         Thread readerThread = new Thread(incomingReader);
138                         readerThread.start();
139                     } catch (Exception ex) {
140                         JOptionPane.showMessageDialog(clientFrame, "连接不上服务器!\n请确认 IP 和 端口 输入正确。");
141                     }
142                 }
143             }
144         });
145
146         // 监听nickname,设置昵称
147         ActionListener nicknameListener = new ActionListener() {
148             @Override
149             public void actionPerformed(ActionEvent e) {
150                 String aText = nicknameText.getText();
151                 if (!aText.equals("")) {
152                     nickname = aText;
153                 }
154             }
155         };
156         nicknameButton.addActionListener(nicknameListener);
157         nicknameText.addActionListener(nicknameListener);
158         nicknameText.addFocusListener(new FocusListener() {
159             @Override
160             public void focusGained(FocusEvent e) {
161             }
162
163             @Override
164             public void focusLost(FocusEvent e) {
165                 String aText = nicknameText.getText();
166                 if (!aText.equals("")) {
167                     nickname = aText;
168                 }
169             }
170         });
171
172         // 发送消息到服务器
173         ActionListener SayListener = new ActionListener() {
174             @Override
175             public void actionPerformed(ActionEvent e) {
176                 String aText = sayText.getText();
177                 if (aText.equals("")) {
178                     JOptionPane.showMessageDialog(clientFrame, "内容不能为空!");
179                 } else {
180                     try {
181                         writer.println(nickname + ":" + aText);
182                         writer.flush();
183                     } catch (Exception ex) {
184                         ex.printStackTrace();
185                     }
186                     sayText.setText("");
187                 }
188             }
189         };
190         sayButton.addActionListener(SayListener);
191         sayText.addActionListener(SayListener);
192
193     }
194
195 }

运行结果如下:

下面主要探讨一下Java中的socket与Linux中的socket API之间的联系。主要思路当然是查看一下Java中的serversocket和socket的源码,这样更清楚地明白底层Java调用栈。

首先要明白的一点是,Java实现的socket与Linux提供的socket是否一定是有关呢?答案是否定的!关键要看Java虚拟机是怎么实现的。

如果Java虚拟机是在Linux系统之上的,并且使用了系统调用,那显然两者之间有关系。并且JVM最后一定是调用了LinuxSocket。

但是如果是在一个裸机上实现的虚拟机或者其他非Linux系统上实现Java虚拟机,那显然二者之间没有一毛钱关系,最终只是有相同的接口名称而已。

所以最终还是要看JVM是怎么实现的。JVM屏蔽了底层不同的机器特性,向上提供了一个统一的虚拟机接口,所以还是JVM牛逼。

12-26 05:04