目录
聊天程序
1.TCP协议
我们都知道远程连接到一个应用需要确认IP地址和端口,过多的底层原理就不必多说,直接网络编程的实现,TCP协议连接安全且传输的大型数据(如图片、视频)完整,UDP协议连接不安全且传输的大型数据不完整,导致传出去的图片、视频播放不出来
1.1.TCP三次握手
- 第一次握手是客户端向服务端发起请求连接信息
- 第二次握手是服务端接收到请求并且发送响应给客户端
- 第三次握手是客户端接收到响应后再给服务端发信息,完成连接
具体在下面代码完成后讲解
1.2.Java编程
Java建立TCP协议用到两个类:
-
Socket类:作为客户端应用所用到的类,用于确认连接的服务器(设置IP地址和端口),用于数据的发送与接收
Socket sk= new Socket("127.0.0.1",1200); //连接到的服务器IP地址为12.0.0.1,连接到服务器应用的端口为1200
-
ServerSocket类:这是作为服务端应用所用到的类,设置连接端口,接受客户端的发送请求
ServerSocket ss = new ServerSocket(1200); //设置服务端的端口为1200 Socket sk = ss.accept(); //转化为Socket对象
- 数据之间以字节传输,所以传输的数据完整,但也可转化成字符流传输方式,客户端与服务端之间传输信息是以写的方式发送信息,以读的方式接收信息,如下:
Socket sk= new Socket("127.0.0.1",1200); OutputStreamWriter out=new OutputStreamWriter(sk.getOutputStream()); //通过sk.getOutputStream()方式(网络方式)以OutputStreamWriter(字节流)输出信息 //同理如下 InputStreamReader in=new InputStreamReader(sk.getInputStream())
- 下面是字符流输出方式和接受方式
Socket sk= new Socket("127.0.0.1",1200); //利用BufferedWriter类 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(sk.getOutputStream())); //利用BufferedReader类 BufferedReader br=new BufferedReader(new InputStreamReader(sk.getInputStream()));
-
以字符流为例传输和接收过程,切记字符流不能完好传输图片、视频之类的数据
Socket sk= new Socket("127.0.0.1",1200); String str="你好"; BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(sk.getOutputStream())); bw.write(str); //字符流方式将字符串通过网络写给对方 bw.newLine(); //写后换行 bw.flush(); //表示刷新,也就是立即写给对方 //-----------------------------------------------------------------------------------// BufferedReader br=new BufferedReader(new InputStreamReader(sk.getInputStream())); String read = br.readLine(); //字符流方式将对方的信息一行一行的接收过来 //备注:write和read在计算机中实质上其实就是用于写入和读取应用的信息 //例如写入和读取本地记事本, //而只要远程连接,那么别人的应用也是你的应用, //因此在聊天软件中,服务端是一个应用,客户端也是一个应用,他们的交流方式固然也是write和read
1.3.完整功能实现
- TCP协议先启动服务端程序,目的是打开端口,再启动客户端,否则出错
- Java程序中TCP协议第一次握手和第二次握手在启动程序时就实现
- 第三次握手是在客户端给服务端发送第一条信息来完成
- 断开连接时应先关闭客户端,再关闭服务端,否则也会服务端出错
- 因为在Socket类下,当三次握手实现后就不区分客户端和服务端了,就是两个应用程序在远程交流
- 服务端代码如下:
import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Date; import javax.swing.*; public class Server { //以下是聊天窗口的实现,上一篇文章有说过,不必多说 private JFrame jf; private JButton jBsend; private JTextArea jTAcontent; private JTextField jText; private JLabel JLcontent; private Date data; private JPanel jPanel; private JScrollPane scroll; Server() { jf = new JFrame("服务端"); jBsend = new JButton("发送"); jTAcontent = new JTextArea(13, 40); jText = new JTextField(12); scroll = new JScrollPane(jTAcontent, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); //文本区添加竖直滑动条 JLcontent = new JLabel("聊天记录"); jPanel = new JPanel(); } public void Win() { Box boxVBox = Box.createVerticalBox(); //这里应用了垂直盒式布局模式排列组件 boxVBox.add(JLcontent); boxVBox.add(Box.createVerticalStrut(5)); boxVBox.add(scroll); boxVBox.add(Box.createVerticalStrut(10)); boxVBox.add(jText); boxVBox.add(Box.createVerticalStrut(10)); boxVBox.add(jBsend); boxVBox.add(Box.createVerticalStrut(10)); jPanel.add(boxVBox); jf.add(jPanel); jf.setSize(600, 400); jf.setResizable(false); jf.setLocationRelativeTo(null); jf.setVisible(true); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public void Connect() throws Exception { ServerSocket ss = new ServerSocket(1200); while (true) { Socket sk = ss.accept(); jBsend.addActionListener(e -> { //按钮响应事件,实现点击按钮发送信息 String str = jText.getText(); //获取文本框的内容 try { jTAcontent.append("我:" + str + "\n"); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sk.getOutputStream())); //以字符流发送信息 bw.write(str); //将文本框内容发送给对方 bw.newLine(); //发送后换行 bw.flush(); //立即发送 //不用bw.close(),为了可以一直发送信息 } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } jText.setText(""); }); new Thread(() -> { //开启线程,这里是为了服务端可同时接收到多个客户端信息 while (true) { //设置死循环,用于随时接受信息 try { BufferedReader br = new BufferedReader(new InputStreamReader(sk.getInputStream())); //字符流方式接受信息 String read = br.readLine(); //以字符串方式一行一行接受到信息 jTAcontent.append("客户:" + read + "\n"); //将接收的信息写入文本区 } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }).start(); //用start开启线程 } } public static void main(String[] args) throws Exception { Server server = new Server(); server.Win(); server.Connect(); } }
- 客户端代码如下:
import java.io.*; import java.net.Socket; import java.util.Date; import javax.swing.*; public class Client { private JFrame jf; private JButton jBsend; private JTextArea jTAcontent; private JTextField jText; private JLabel JLcontent; private Date data; private JPanel jPanel; JScrollPane scroll; Client(){ jf=new JFrame("客户端"); jBsend =new JButton("发送"); jTAcontent =new JTextArea(13,40); jText =new JTextField(12); scroll=new JScrollPane(jTAcontent,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); //文本区添加竖直滑动条 JLcontent=new JLabel("聊天记录"); jPanel=new JPanel(); } public void Win() { Box boxVBox=Box.createVerticalBox(); boxVBox.add(JLcontent); boxVBox.add(Box.createVerticalStrut(5)); boxVBox.add(scroll); boxVBox.add(Box.createVerticalStrut(10)); boxVBox.add(jText); boxVBox.add(Box.createVerticalStrut(10)); boxVBox.add(jBsend); boxVBox.add(Box.createVerticalStrut(10)); jPanel.add(boxVBox); jf.add(jPanel); jf.setSize(600, 400); jf.setResizable(false); jf.setLocationRelativeTo(null); jf.setVisible(true); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public void Connect() throws Exception{ Socket sk= new Socket("127.0.0.1",1200); jBsend.addActionListener(e->{ //Lambda表达式实现点击按钮发送信息 String str=jText.getText(); //获取文本框内容 if (str.matches("\\s+") || str.equals("")) { JOptionPane.showMessageDialog(jf, "不可发送空白内容"); return; } try { jTAcontent.append("我:"+str+"\n"); //文本区添加文本框内容 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(sk.getOutputStream())); //字符流发送信息 bw.write(str); //发送文本框的信息给对方 bw.newLine(); //发送后换行 bw.flush(); //立即发送 //不用bw.close(),为了可以一直发送信息 jText.setText(""); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } new Thread(()->{ //Lambda表达式创建线程 while(true){ //死循环随时接受信息 try { BufferedReader br=new BufferedReader(new InputStreamReader(sk.getInputStream())); //以字符流接受信息 String read = br.readLine(); //一行一行接受信息 jTAcontent.append("客服:"+read+"\n"); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }).start(); //开启线程 }); } public static void main(String[] args) throws Exception { Client client=new Client(); client.Win(); client.Connect(); } }
- 视频演示如下,这里我服务端和客户端是同一台电脑:
2.总结
- TCP协议执行三次握手,Java中第三次握手在客户端发送第一条信息实现
- Socket类和ServerSocket类实现连接
- 三次连接后不区分服务端和客户端,相当于两个应用程序远程交流
- TCP原始以字节流传输信息,可改成字符流,字节流可传输大型数据,字符流不可
- 注意服务端和客户端的开启顺序和关闭顺序