Java 网络通信【01】TCP
不积跬步,无以至千里;不积小流,无以成江海。——《荀子劝学》
JAVA中设计网络编程模式的主要有TCP和UDP两种。
TCP是属于即时通信,点对点连接进行通信。
UDP是通过数据包来进行通信,UDP当中就会牵扯到数据的解析和传送。
在安全性能方面,TCP要略胜一筹,通信过程中不容易出现数据丢失的现象,有一方中断,两方的通信就会结束,UDP数据包传送的过程当中,一方中断,数据包有很大的可能丢失,还有可能传来的数据包的顺序是错乱的;
在效率方面,UDP要比TCP快的不只是一点点的问题,若终端有解析数据方法的函数,数据包就会源源不断的传送过来,然后反馈回去。
public class Server { static BufferedReader br; //服务器端的输入流 static PrintStream ps; //服务器端的输出流 static JTextArea text; //服务器相关的界面组件 public Server() { JFrame frame = new JFrame("服务器端"); text = new JTextArea(); JScrollPane scroll = new JScrollPane(text); frame.add(scroll); frame.setVisible(true); frame.setSize(300, 400); text.setEditable(false); } public static void main(String[] args) throws Exception { new Server(); //生成服务器界面 //通过服务器端构造函数 ServerSocket(port) 实例化一个服务器端口 ServerSocket server = new ServerSocket(2000); text.append("监听2000端口" + "\n"); Socket client = server.accept(); br = new BufferedReader(new InputStreamReader(client.getInputStream())); ps = new PrintStream(client.getOutputStream()); String msg; //如果输入流不为空,将接受到的信息打印到相应的文本框中并反馈回收到的信息 while ((msg = br.readLine()) != null) { text.append("服务器端收到:" + msg + "\n"); ps.println(msg); if (msg.equals("quit")) { text.append("客户端“2000”已退出!" + "\n"); text.append("服务器程序将退出!"); break; } } ps.close(); br.close(); client.close(); } }
Client类:
import java.awt.*; import java.awt.event.*; import java.io.*; import javax.swing.*; import java.net.*; public class Client implements ActionListener { //这里有两个图形界面,一个是连接的frame,另一个和服务器通信的界面frame1 private JFrame frame; private JLabel adress; private JLabel port; JTextField adresstext; JTextField porttext; JButton connect; private JFrame frame1; private JLabel shuru; private JPanel panel1; private JPanel panel2; private JLabel jieshou; JButton send; static JTextArea shurukuang; static TextArea jieshoukuang; static BufferedReader br1; //从服务端接受的数据流 static PrintStream ps; //从客户端输出的数据流 static BufferedReader br2; //从通信界面中的输入框接受的数据流 static Socket client; //将输入框字符串转换为字符串流所需的字符串的输入流 static ByteArrayInputStream stringInputStream; public Client() { frame = new JFrame(); adress = new JLabel("IP 地址"); port = new JLabel("端口号"); adresstext = new JTextField("127.0.0.1", 10); porttext = new JTextField("2000", 10); connect = new JButton("连接"); //连接界面的布局 frame.setLayout(new FlowLayout()); frame.add(adress); frame.add(adresstext); frame.add(port); frame.add(porttext); frame.add(connect); frame.setVisible(true); frame.setSize(200, 150); connect.addActionListener(this); //通信界面的实例化 frame1 = new JFrame(); shuru = new JLabel("请输入"); shurukuang = new JTextArea("请输入····", 5, 40); panel1 = new JPanel(); panel1.add(shuru); panel1.add(shurukuang); panel1.setLayout(new FlowLayout()); send = new JButton("发送"); panel2 = new JPanel(); jieshou = new JLabel("已接受"); jieshoukuang = new TextArea(8, 60); jieshoukuang.setEditable(false); panel2.add(jieshou); panel2.add(jieshoukuang); panel2.setLayout(new FlowLayout()); frame1.setLayout(new FlowLayout()); //通信界面都的布局 frame1.add(BorderLayout.NORTH, panel1); frame1.add(send); frame1.add(BorderLayout.SOUTH, panel2); //连接时通信界面是处于看不到的 frame1.setVisible(false); frame1.setSize(500, 350); send.addActionListener(this); } //两个界面当中都有相应的按钮时间,为相应的时间添加动作 public void actionPerformed(ActionEvent e) { if (e.getSource() == connect) { try { //当触发连接按钮时,实例化一个客户端 client = new Socket("127.0.0.1", 2000); //隐藏连接界面,显示通信界面 frame.setVisible(false); frame1.setVisible(true); jieshoukuang.append("已经连接上服务器!" + "\n"); } catch (IOException e1) { System.out.println("链接失败!"); e1.printStackTrace(); } } //通信界面中的发送按钮相应的时间处理 if (e.getSource() == send) { //将输入框中的字符串转换为字符串流 stringInputStream = new ByteArrayInputStream((shurukuang.getText()).getBytes()); br2 = new BufferedReader(new InputStreamReader(stringInputStream)); String msg; try { while ((msg = br2.readLine()) != null) { ps.println(msg); //将输入框中的内容发送给服务器端 jieshoukuang.append("向服务器发送:" + msg + "\n"); jieshoukuang.append("客户端接受相应:" + br1.readLine() + "\n"); if (msg.equals("quit")) { jieshoukuang.append("客户端将退出!"); br1.close(); ps.close(); client.close(); frame1.setVisible(false); break; } } } catch (IOException e2) { System.out.println("读输入框数据出错!"); } shurukuang.setText(""); } } public static void main(String[] args) throws IOException { new Client(); //实例化连接界面 client = new Socket("127.0.0.1", 2000); br1 = new BufferedReader(new InputStreamReader(client.getInputStream())); //从服务端接受的数据 ps = new PrintStream(client.getOutputStream()); //从客户端输出的数据 } }
写完这两个类以后还是有几个问题:
1)main 函数为什么非要用 static 来修饰?
2)缓冲对象 BufferedReader 为什么不能直接用于判断,非要将读到的数据赋值给字符串来进行操作?
3)在连接界面当中的 Connect 按钮事件 当中我有实例化一个 客户端的对象,但是我注释掉主函数当中 client=new Socket("127.0.0.1",2000); 的这一句的时候,就会发现抛出 NULLPOINTEXCEPTION 异常,我很不理解?