Socket编程一实现简易的聊天功能以及文件传输

来源:互联网 发布:超级数据恢复软件破解 编辑:程序博客网 时间:2024/05/22 14:43

Socket编程一实现简易的聊天功能以及文件传输

标签: androidsocket通讯协议聊天socket传输文件
174人阅读 评论(6)收藏举报

干程序是一件枯燥重复的事,每当感到内心浮躁的时候,我就会找小说来看。我从小就喜爱看武侠小说,一直有着武侠梦。从金庸,古龙,梁羽生系列到凤歌(昆仑),孙晓(英雄志)以及萧鼎的(诛仙)让我领略着不一样的江湖。

如果你有好看的武侠系列小说,给我留言哦。题外话就扯这么多了,接着还是上技术。

看看今天实现的功能效果图:

soc.gif

可以这里使用多台手机进行通讯,【凤歌】我采用的服务器发送消息。

是不是只有发送消息,有些显得太单调了。好,在发送消息的基础上增加文件传输。后期会增加视频,音频的传输,增加表情包。那一起来看看图文消息的效果图,带领大家一起来实现通讯的简易聊天功能。

soc.gif

需要解决的难点:

  • 如何判断socket接收的数据是字符串还是流?

如果你已是一名老司机,还请留言给出宝贵意见。带着这个疑问我们接着往下看。

Socket概述

Socket我们称之为”套接字”,用于消息通知系统(如:激光推送),时事通讯系统(如:环信)等等。用于描述IP地址和端口,是一个通信链的句柄。网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket,一个Socket由一个IP地址和一个端口号唯一确定(如:ServerSocket)。应用程序通常通过”套接字”向网络发出请求或者应答网络请求。SocketTCP/IP协议的一个十分流行的编程界面,但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。

java.NET包下有两个类:SocketServerSocket,基于TCP协议。

本文针对SocketServerSocket作主要讲解。

socket连接

建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket

客户端Socket

Socket的构造方法:

Socket(InetAddress address,int port); //创建一个流套接字并将其连接到指定 IP 地址的指定端口号Socket(String host,int port); //创建一个流套接字并将其连接到指定主机上的指定端口号Socket(InetAddress address,int port, InetAddress localAddr,int localPort); //创建一个套接字并将其连接到指定远程地址上的指定远程端口Socket(String host,int port, InetAddress localAddr,int localPort); //创建一个套接字并将其连接到指定远程主机上的指定远程端口Socket(SocketImpl impl); //使用用户指定的 SocketImpl 创建一个未连接 Socket

参数含义:

  • address 双向连接中另一方的IP地址

  • port 端口号

  • localPort 本地主机端口号

  • localAddr 本地机器地址

  • impl 是socket的父类,既可以用来创建serverSocket又可以用来创建Socket

注意:我们在选取端口号的时候需要特别注意,每一个端口提供一种特定的服务,只有给出正确的端口,才能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23。本文选取的端口号为30003

Socket的几个重要方法:

public InputStream getInputStream(); //方法获得网络连接输入,同时返回一个IutputStream对象实例public OutputStream getOutputStream(); //方法连接的另一端将得到输入,同时返回一个OutputStream对象实例public Socket accept(); //用于产生"阻塞",直到接受到一个连接,并且返回一个客户端的Socket对象实例。

注意对流异常的处理。

服务端ServerSocket

ServerSocket的构造方法:

ServerSocket(int port); //创建绑定到特定端口的服务器套接字ServerSocket(int port,int backlog); //利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号ServerSocket(int port,int backlog, InetAddress bindAddr); //使用指定的端口、侦听 backlog 和要绑定到的本地 IP地址创建服务器

消息与文件的接收和发送

基础常量

Constants常量类:

    //连接ip地址 可以通过以下代码查看当前 IP 地址,记住在网络适配里关掉其他的连接,只保留当前连接    //注意关掉防火墙,关闭杀毒软件    /**     * InetAddress ia = null;     * try {     * ia = ia.getLocalHost();     * <p>     * String localname = ia.getHostName();     * String localip = ia.getHostAddress();     * System.out.println("本机名称是:" + localname);     * System.out.println("本机的ip是 :" + localip);     * } catch (Exception e) {     * // TODO Auto-generated catch block     * e.printStackTrace();     * }     */    public static final String HOST = "192.168.199.164";    //端口号  避免端口冲突 我这里取30003    public static final int PORT = 30003;    //收到消息    public static final int RECEIVE_MSG = 0;    //发送消息    public static final int SEND_MSG = 1;    //发送文件    public static final int SEND_FILE = 2;    //传输文件    public static final int TRANSFER_FILE = 3;    //传输字符串    public static final int TRANSFER_STR = 4;    //聊天列表 发送消息    public static final int CHAT_SEND = 1;    //聊天列表 接收消息    public static final int CHAT_FROM = 2;    //更新进度    public static final int PROGRESS = 5;

注意:关闭多余的网络适配,关闭防火墙。

定义协议

为了保证接收到的数据类型统一(数据是字符串还是流),需要定义协议。定义协议的方式有很多种:

  • 发送一个握手信号。 根据握手信号来确定发送的是字符串还是流

  • 定义了Header(头)和Body(实体),头是固定大小的,用来告诉接收者数据的格式、用途、长度等信息,接收者根据Header来接受Body。

  • 自定义协议

我这里采用的自定义协议,原理跟前面两种类似。我传输的是JSON数据,根据字段标识传输的是字符串还是流,接收者根据标识去解析数据即可。

协议的实体类(Transmission):

    //文件名称    public String fileName;    //文件长度    public long fileLength;    //传输类型    public int transmissionType;    //传输内容    public String content;    //传输的长度    public long transLength;    //发送还是接受类型    1发送  2接收    public int itemType = 1;    //0 文本  1  图片    public int showType;

根据字段transmissionType去标识传输(序列化)或接收(反序列化)的类型。传输的过程中始终都是以JSON的格式存在的。传输文件时需要把流转换成字符串(方式很多种我用的是Base64加密与解密)。

客户端(ClientThread)

public class ClientThread extends Thread {    PrintWriter mPrintWriter;    BufferedReader mBufferedReader;    Socket mSocket;    Handler mSendHandler;    Handler mWriteHandler;    Gson mGson;    public ClientThread(Handler handler) {        mSendHandler = handler;        mGson = new Gson();    }    @Override    public void run() {        super.run();        try {            //创建socket            mSocket = new Socket(Constants.HOST, Constants.PORT);            //获取到读写对象            mPrintWriter = new PrintWriter(mSocket.getOutputStream());            mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));            //新开线程读取消息 并发送消息            new Thread() {                @Override                public void run() {                    super.run();                    String content = null;                    try {                        while ((content = mBufferedReader.readLine()) != null) {                            Transmission trans = mGson.fromJson(content, Transmission.class);                            if (trans.transmissionType == Constants.TRANSFER_STR) {                                Message msg = new Message();                                msg.what = Constants.RECEIVE_MSG;                                msg.obj = content;                                mSendHandler.sendMessage(msg);                            }                        }                    } catch (IOException e) {                        e.printStackTrace();                    }                }            }.start();            //当前线程创建 handler            Looper.prepare();            mWriteHandler = new Handler() {                @Override                public void handleMessage(Message msg) {                    super.handleMessage(msg);                    if (msg.what == Constants.SEND_MSG) {                        mPrintWriter.write(msg.obj.toString() + "\r\n");                        mPrintWriter.flush();                    } else if (msg.what == Constants.SEND_FILE) {//传输文件                        //定义标记判定是字符串还是文件                        sendFile(msg.obj.toString());                    }                }            };            Looper.loop();        } catch (IOException e) {            e.printStackTrace();            //出现异常关闭资源            try {                if (mPrintWriter != null) {                    mPrintWriter.close();                }                if (mBufferedReader != null) {                    mBufferedReader.close();                }                if (mSocket != null) {                    mSocket.close();                }            } catch (IOException e1) {                e1.printStackTrace();            }        }    }    /**     * 文件路径     *     * @param filePath     */    private void sendFile(String filePath) {        FileInputStream fis = null;        File file = new File(filePath);        try {            mSendHandler.sendEmptyMessage(Constants.PROGRESS);            fis = new FileInputStream(file);            Transmission trans = new Transmission();            trans.transmissionType = Constants.TRANSFER_FILE;            trans.fileName = file.getName();            trans.fileLength = file.length();            trans.transLength = 0;            byte[] bytes = new byte[1024];            int length = 0;            while ((length = fis.read(bytes, 0, bytes.length)) != -1) {                trans.transLength += length;                trans.content = Base64Utils.encode(bytes);                mPrintWriter.write(mGson.toJson(trans) + "\r\n");                mPrintWriter.flush();                Message message = new Message();                message.what = Constants.PROGRESS;                message.obj = 100 * trans.transLength / trans.fileLength;                mSendHandler.sendMessage(message);            }            fis.close();        } catch (FileNotFoundException e) {            e.printStackTrace();            if (fis != null) {                try {                    fis.close();                } catch (IOException e1) {                    e1.printStackTrace();                }            }        } catch (IOException e) {            e.printStackTrace();            mPrintWriter.close();        }    }

MainActivity中开启客户端线程:

    mClientThread = new ClientThread(mHandler);    mClientThread.start();

服务端(MyServer)

public class MyServer {    //多客户端    public static ArrayList<Socket> sSockets = new ArrayList<Socket>();    public static void main(String[] args) {        //DatagramSocket 基于UDP协议的        ServerSocket serverSocket = null;        try {            //创建服务器的socket对象            serverSocket = new ServerSocket(Constants.PORT);            while (true) {                Socket socket = serverSocket.accept();                sSockets.add(socket);                //开启线程                new Thread(new ServerThread(socket)).start();            }        } catch (IOException e) {            e.printStackTrace();        }    }}
  • 25

ServerThread 类:

public class ServerThread implements Runnable {    Socket mSocket;    BufferedReader mBufferedReader;    Gson mGson;    boolean mCreateFile = true;    public ServerThread(Socket socket) throws IOException {        mGson = new Gson();        mSocket = socket;        mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream(), "utf-8"));        //新开线程给客服端发送消息        new Thread() {            @Override            public void run() {                super.run();                sendMessage();            }        }.start();    }    @Override    public void run() {        readMessage();    }    //读取的数据发送给客户端    private void readMessage() {        String content = null;        FileOutputStream fos = null;        try {            while ((content = mBufferedReader.readLine()) != null) {                Transmission trans = mGson.fromJson(content, Transmission.class);                if (trans.transmissionType == Constants.TRANSFER_STR) {                    System.out.println("" + content);                    for (Iterator<Socket> it = MyServer.sSockets.iterator();                         it.hasNext(); ) {                        if (it == null) {                            break;                        }                        Socket s = it.next();                        try {                            PrintWriter printWriter = new PrintWriter(s.getOutputStream());                            printWriter.write(content + "\r\n");                            printWriter.flush();                        } catch (SocketException e) {                            e.printStackTrace();                            it.remove();                        }                    }                } else {                    long fileLength = trans.fileLength;                    long transLength = trans.transLength;                    if (mCreateFile) {                        mCreateFile = false;                        fos = new FileOutputStream(new File("d:/" + trans.fileName));                    }                    byte[] b = Base64Utils.decode(trans.content.getBytes());                    fos.write(b, 0, b.length);                    System.out.println("接收文件进度" + 100 * transLength / fileLength + "%...");                    if (transLength == fileLength) {                        mCreateFile = true;                        fos.flush();                        fos.close();                    }                }            }        } catch (IOException e) {            e.printStackTrace();            try {                if (fos != null) {                    fos.close();                }                if (mBufferedReader != null) {                    mBufferedReader.close();                }                if (mSocket != null) {                    mSocket.close();                }            } catch (IOException e1) {                e1.printStackTrace();            }            MyServer.sSockets.remove(mSocket);        }    }    //发送消息给连接的客服端    private void sendMessage() {        BufferedReader bufferedReader = null;        try {            while (true) {                bufferedReader = new BufferedReader(new InputStreamReader(System.in));                System.out.print("请输入发送的字符串:");                String str = bufferedReader.readLine();                for (Iterator<Socket> it = MyServer.sSockets.iterator();                     it.hasNext(); ) {                    if (it == null) {                        break;                    }                    Socket s = it.next();                    try {                        Transmission trans = new Transmission();                        trans.itemType = Constants.CHAT_FROM;                        trans.transmissionType = Constants.TRANSFER_STR;                        trans.content = str;                        PrintWriter printWriter = new PrintWriter(s.getOutputStream());                        printWriter.write(mGson.toJson(trans) + "\r\n");                        printWriter.flush();                    } catch (SocketException e) {                        e.printStackTrace();                        s.close();                        it.remove();                    }                }            }        } catch (IOException e) {            e.printStackTrace();            try {                if (bufferedReader != null) {                    bufferedReader.close();                }                MyServer.sSockets.remove(mSocket);            } catch (IOException e1) {                e1.printStackTrace();            }        }    }}

首先运行MyServermain函数,把服务启动起来,然后运行MainActivity把客户端启动起来。这样就实现了两端通讯。

源码地址


 
 

我的同类文章

  • 初谈一Dagger22017-02-12
  • 初谈一Java Annotation2017-02-04
  • 自定义View之案列篇(四):颜色选择器2016-11-22
  • Android 6.0 运行时权限封装之路2016-11-01
  • 自定义View之案列篇(一):魔方2016-10-18
  • EventBus使用大全2017-02-07
  • Android APK 更新之路2016-12-03
  • 自定义View之案列篇(三):仿QQ小红点2016-11-08
  • 自定义View之案列篇(二):扇形菜单2016-10-24
  • Android Studio JNI 开发简单案例2016-09-29
0 0
原创粉丝点击