Java实现网络聊天中使用的socket API与Linux socket API之间的关系
尝试着用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牛逼。