zoukankan      html  css  js  c++  java
  • Java实现简单网络聊天程序

    1.socket

    在进行网络编程前,我们需要了解socket。我们知道IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层。
    TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP协议是应用层协议,主要解决如何包装数据。

    那么socket是啥呢?
    • 首先呢,socket就是网络通信的工具,任何一门语言都有socket,他不是任何一个语言的专有名词,而是大家通过自己的程序与其他电脑进行网络通信的时候都用它。
    • 实际上socket是对TCP/IP协议的封装,它的出现只是使得程序员更方便地使用TCP/IP协议栈而已。socket本身并不是协议,它是应用层与TCP/IP协议族通信的中间软件抽象层,是一组调用接口(TCP/IP网络的API函数)。
    • socket非常类似于电话插座。以一个国家级电话网为例。电话的通话双方相当于相互通信的2个进程,区号是它的网络地址;区内一个单位的交换机相当于一台主机,主机分配给每个用户的局内号码相当于socket号。任何用户在通话之前,首先要占有一部电话机,相当于申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求。对方假如在场并空闲,拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机相当于关闭socket,撤消连接。
    socket在网路中的位置:

    2.Socket的TCP和UDP通信

    socket有两种建立通信的方式,一种是基于TCP的可靠传输,一种是基于UDP的不可靠传输

    TCP
    • 可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;文件传输程序。
    • 面向连接的协议,在socket之间进行数据传输之前必须要建立连接,所以在TCP中需要连接时间
    • TCP传输数据无大小限制,一旦建立连接,双方socket就可以按统一的格式传输大的数据(无限制)。
    • TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。
    UDP
    • 不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文(数据包),尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
    • 每个数据包中都给出了完整的地址信息,因此无需建立发送方和接收方的连接。
    • UDP传输数据时是有大小限制的,每个被传输的数据包必须限定在64KB之内。
    • UDP是一个不可靠的协议,发送方所发送的数据包并不一定以相同的顺序到达接收方。
    socket中TCP和UDP对比:

    3.Java中Socket方法

    方法 说明
    ServerSocket(int port) 创建ServerSocket
    bind(SocketAddress bindpoint) 将套接字绑定到本地地址
    accept() 阻塞方法,也就是说调用accept方法后程序会停下来等待连接请求
    close() 关闭此套接字
    connect(SocketAddress endpoint) 将此套接字连接到服务器
    InetAddress getInetAddress() 返回套接字的连接地址
    InetAddress getLocalAddress() 获取套接字绑定的本地地址
    InputStream getInputStream() 返回此套接字的输入流
    OutputStream getOutputStream() 返回此套接字的输出流
    SocketAddress getLocalSocketAddress() 返回此套接字绑定的端点地址,如果尚未绑定则返回 null
    SocketAddress getRemoteSocketAddress() 返回此套接字的连接的端点地址,如果尚未连接则返回 null
    int getLoacalPort() 返回此套接字绑定的本地端口
    int getPort() 返回此套接字连接的远程端口

    4.基于Java的socket网络编程

    4.1代码结构

    4.2基于TCP实现
    服务端
    • 创建一个服务器端socket套接字(套接字会在制定的端口上监听)
    • 当有使用ServerSocket中的accept()获取客户端socket对象
    • 使用多线程实现聊天:
      • 1.MyClientThread线程负责接收客户端发送给服务器端的消息
      • 2.MyServerThread线程负责向客户端发送消息
    package socket.chat;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Scanner;
    
    /**
     * socket实现聊天
     * 1.创建一个服务器端socket套接字(套接字会在制定的端口上监听);
     * 2.当有使用ServerSocket中的accept()获取客户端socket对象
     * 3.使用多线程实现聊天:1.MyClientThread线程负责接收客户端发送给服务器端的消息;
     *                      2.MyServerThread线程负责向客户端发送消息
     */
    class MyServerSocket{
    
        public static void main(String[] args) {
            ServerSocket serverSocket = null;//服务器端socket
            Socket clientSocket = null;//客户端socket
    
            try {
                //创建一个服务器端socket服务
                serverSocket = new ServerSocket(8888);
                while (true) {//使用while死循环模拟客户端一直启动
                    clientSocket = serverSocket.accept();//获取连接服务端的客户端socket
                    //该线程用于接收客户端发送的消息,并将该消息打印到控制台
                    MyClientThread myClientThread = new MyClientThread(clientSocket);
                    //该线程用于向客户端发送的消息
                    MyServerThread myServerThread = new MyServerThread(clientSocket);
    
                    //启动线程
                    myClientThread.start();
                    myServerThread.start();
                }
            } catch (IOException io) {
                io.printStackTrace();
            } finally {
                if (clientSocket != null) {
                    try {
                        clientSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (serverSocket != null) {
                    try {
                        serverSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }//main--end
    }//MyServerSocket--end
    
    /**
     * 接收客户端的内容
     * 1.根据客户端Socket获取指向客户端Socket对象的输入流对象(输入源)
     * 2.通过输入流对象将客户端输入的信息读取到内存中
     * 3.通过输出流对象(System.out.print)将内存中的数据打印到控制台
     */
    class MyClientThread extends Thread {
        private DataInputStream dataInputStream = null;
    
        public MyClientThread(Socket socket) {
            try {
                //获取客户端的输入流对象
                this.dataInputStream = new DataInputStream(socket.getInputStream());
            } catch (IOException io) {
                io.printStackTrace();
            }
        }//MyClientThread--end
        @Override
        public void run() {
            String tellClient = null;
            try {
                while (true) {
                    tellClient = this.dataInputStream.readUTF();//将客户端发送的信息写入到内存中
                    System.out.println("客户端说:"+tellClient);//将读取的客户端信息打印到控制台
                }
            } catch (IOException io) {
                io.printStackTrace();
            }
        }//run--end
    }//MyClientThread--end
    
    /**
     * 向发送客户端的消息
     * 1.根据客户端Socket获取指向客户端Socket对象的输出流对象(输出目的地)
     * 2.获取控制台输入流对象(输入源)
     * 3.通过输入流对象将控制台输入的信息读取到内存中
     * 4.通过输出流对象将内存中的数据返回给服务器端
     */
    class MyServerThread extends Thread {
    
        private DataOutputStream dataOutputStream = null;//用于输出服务器返回给客户端的信息
        private Scanner in = null;//用于将服务器端在控制台输入的信息读取到内存中
    
        public MyServerThread(Socket socket) {
            try {
                //根据客户端获取输出流对象
                this.dataOutputStream = new DataOutputStream(socket.getOutputStream());
                //获取控制台输入流对象
                in = new Scanner(System.in);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }//MyServerThread--end
    
        @Override
        public void run() {
            String tellServer = null;
    
            while (true) {
                try {
                    //将控制台中的信息读入到内存中
                    tellServer = in.nextLine();
                    //服务器端向客户端发送消息
                    this.dataOutputStream.writeUTF(tellServer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }//run
    }//server
    
    客户端
    • 根据IP和port获取和服务端连接的Socket对象
    • 通过服务端Socket对象获取指向服务端Socket对象的输入流/输出流,获取服务器端发送的信息或者向服务器发送消息
    package socket.chat;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.Socket;
    import java.util.Scanner;
    
    /**
     * 客户端socket
     * 1.根据IP和port获取和服务端连接的Socket对象
     * 2.通过服务端Socket对象获取指向服务端Socket对象的输入流/输出流,获取服务器端发送的信息或者向服务器发送消息
     */
    class MyClientSocket {
    
        public static void main(String[] args) {
            Socket serverSocket = null;
            try {
                serverSocket = new Socket("127.0.0.1", 8888);
    
                MyClientToSerThread myClientToSerThread = new MyClientToSerThread(serverSocket);
                MyAcceptServerThread myAcceptServerThread = new MyAcceptServerThread(serverSocket);
    
                myClientToSerThread.start();
                myAcceptServerThread.start();
            } catch (IOException io) {
                io.printStackTrace();
            }
    
        }//main--end
    }//MyClientSocket--end
    
    /**
     * 向服务端发送消息
     */
    class MyClientToSerThread extends Thread {
    
        private DataOutputStream dataOutputStream = null;
        private Scanner in = null;
    
        public MyClientToSerThread(Socket socket) {
            try {
                this.dataOutputStream = new DataOutputStream(socket.getOutputStream());
                this.in = new Scanner(System.in);
            } catch (IOException io) {
                io.printStackTrace();
            }
        }//MyClientToSerThread--end
    
        @Override
        public void run() {
            String tell = null;
            while (true) {
                try {
                    tell = in.nextLine();
                    dataOutputStream.writeUTF(tell);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }//run--end
    }//MyClientToSerThread--end
    /**
     *接收客户端的信息
     **/
    class MyAcceptServerThread extends Thread {
    
        private DataInputStream dataInputStream = null;
    
        public MyAcceptServerThread(Socket socket) {
            try {
                this.dataInputStream = new DataInputStream(socket.getInputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }//MyAcceptServerThread--end
    
        @Override
        public void run() {
            String tell = null;
            while (true) {
                try {
                    tell = this.dataInputStream.readUTF();
                    System.out.println("服务端:"+tell);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }//run--end
    }//MyAcceptServerThread--end
    
    4.3运行结果

    5.抓包分析

    使用wireshark抓取本地TCP包,选择回环网卡

    TCP三次握手建立过程

    服务端向客户端发信息

  • 相关阅读:
    下载某页面下的所有图片
    ruby程序处理HTML编辑器内容只保留类似UBB的内容
    用ruby获取Email邮箱标题并判断
    Win7 Ruby on rails 开发环境安装
    [SQL2005触发器学习]5、触发器的使用技巧
    使用jquery获取checkbox组和radio组的值
    一个小bug 看浏览器内核加载页面的方式
    我们是一群和平年代充满浮躁与抱怨的程序员
    COM+异常:系统找不到指定的文件。 (异常来自 HRESULT:0x80070002)
    以过桥算法来谈如何满足客户的需求和程序设计步骤
  • 原文地址:https://www.cnblogs.com/yingjiehuang/p/11997278.html
Copyright © 2011-2022 走看看