Android-socket的基本使用,发送文字和图片以及心跳

原创 2017年07月20日 15:55:20

项目需求收集通过Socket向服务器发送图片,之前没搞过,网上搜搜写了下面的例子,勉强解决了需求。

为了测试切换着方便,所以方法写的有点碎了。。。
原文地址 http://blog.csdn.net/qq_25806863/article/details/75533109

要求发送的消息的格式是,8个字节的消息长度+消息体

因为需要8个字节,所以消息长度决定用long

如果需要4个字节,可以用int

手机客户端接收服务器的文字消息

服务端

服务端定义好端口号,开启以一个ServerSocket,写入文字消息:

public class Service {
    private static Socket socket;
  //定义端口号
    private static final int POST = 30000;

    public static void main(String[] args) {
        try {
          //发送的内容
            sendMsg("来自服务器的问候");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void sendMsg(String msg) throws IOException {
      System.out.println("开始连接");
      //创建socket服务端口是30000,并等待连接
        ServerSocket serverSocket = new ServerSocket(POST);
        Socket socket = serverSocket.accept();
      //获取输出流
        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
        sendTextMsg(out, msg);
        out.close();
        socket.close();
        serverSocket.close();
      System.out.println("通讯结束");
    }

    public static void sendTextMsg(DataOutputStream out, String msg) throws IOException {
      //先写长度,就是消息体的字节数,long刚好8个字节  
        out.writeLong(msg.getBytes().length);
      //写入消息
        out.write(msg.getBytes());
    }
}

客户端,接收消息

客户端首先要跟服务器进行连接,然后才能进行通讯。

Socket连接需要网络权限:

<uses-permission android:name="android.permission.INTERNET"/>

因为属于网络通讯,所以socket连接也不能放在主线程中,否则会报错

  • 添加按钮

    在布局中加一个按钮,点击方法是receive

    <Button
      android:text="接收"
      android:layout_marginTop="50dp"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:onClick="receive"/>
  • 定义方法

    在Activity中定义方法

    因为不能再主线程中访问,所以需要子线程。这里直接new了、、

    public void receive(View view){
      new Thread(new Runnable() {
          @Override
          public void run() {
              Socket socket;
              try {
                //这里进行连接服务器,
                //host是服务器ip地址,如“192.168.2.12”
                //post是端口,上面的服务端提供的端口号是30000
                  socket = new Socket(host, post);
                //获取输入流
                  DataInputStream input = new DataInputStream(socket.getInputStream());
                //读取长度,也即是消息头,
                  long len = input.readLong();
                //创建这个长度的字节数组
                  byte[] bytes = new byte[(int)len];
                //再读取这个长度的字节数,也就是真正的消息体
                  input.read(bytes);
                //将字节数组转为String
                  String s = new String(bytes);
                  Log.i("accept", "len: "+len);
                  Log.i("accept", "msg: "+s);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }).start();
    }

运行测试

先运行服务端,会发现程序没有一次执行完,在阻塞着等待连接

这时点击app中的接收按钮,日志中会打印接收到的信息

长度为24,消息内容为“来自服务器的问候”。

因为在utf-8中一个汉字是3个字节,所以8个汉字的消息长度是24字节。

这是看服务器端的打印:

发现服务端也正常执行完毕了。

手机客户端向服务端发送文字消息

服务端接收消息

public class Service {
    private static Socket socket;
  //定义端口号
    private static final int POST = 30000;

    public static void main(String[] args) {
        try {
          //接收消息
            getMsg();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void getMsg() throws IOException {
        System.out.println("开始连接");
        ServerSocket serverSocket = new ServerSocket(POST);
        Socket socket = serverSocket.accept();
      //获取输入流,通过这个流来读取消息
        DataInputStream input = new DataInputStream(socket.getInputStream());
      //接收文字消息
        getTextMsg(input);
        input.close();
        socket.close();
        serverSocket.close();
        System.out.println("通讯结束");
    }

    public static String getTextMsg(DataInputStream input) throws IOException {
      //一样先读长度,再根据长度读消息
        long len = input.readLong();
        System.out.println("len = " + len);
        byte[] bytes = new byte[(int) len];
        input.read(bytes);
        String msg = new String(bytes);
        System.out.println("msd = " + msg);
        return msg;
    }
}

客户端发送消息

  • 增加一个发送的按钮:
<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送!"
        android:onClick="send"/>
  • 定义方法
public void send(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Socket socket;
                try {
                //建立连接
                    socket = new Socket(host, post);
                    //获取输出流,通过这个流发送消息
                    DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                    //发送文字消息
                    sendTextMsg(out,"来自手机客户端的消息");
                    out.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
public void sendTextMsg(DataOutputStream out, String msg) throws IOException {
        byte[] bytes = msg.getBytes();
        long len = bytes.length;
  //先发送长度,在发送内容
        out.writeLong(len);
        out.write(bytes);
    }

运行测试

先运行服务端,也会停留着这个界面等待连接:

然后点击客户端的发送按钮,这是服务端会变成下面这样,完成这次消息的通讯

手机客户端想服务端发送图片

服务端接收图片,并保存到本地

在上面的getMsg()方法中,将getTextMsg(input);改为getImgMsg(input);

下面是getImgMsg(input);方法:

public static void getImgMsg(DataInputStream input) throws IOException {
  //同样是先读长度
    long len = input.readLong();
    System.out.println("len = " + len);
    byte[] bytes = new byte[(int) len];
  //然后在读这个长度的字节到字节数组
    input.readFully(bytes);
  //将独到的内容保存为文件到本地
    File file = new File("/Users/xxx/" + len + ".png");
    FileOutputStream fileOutputStream = new FileOutputStream(file);
    fileOutputStream.write(bytes);
    System.out.println("ok");
}

客户端发送图片

  • 增加一个按钮:
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="200dp"
    android:onClick="sendImg"
    android:text="发送图片" />
  • 定义方法

    public void sendImgMsg(DataOutputStream out ) throws IOException {
    //发送的图片为图标,就是安卓机器人,将bitmap转为字节数组
      Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round);
      ByteArrayOutputStream bout = new ByteArrayOutputStream();
      bitmap.compress(Bitmap.CompressFormat.PNG,100,bout);
    //写入字节的长度,再写入图片的字节
      long len = bout.size();
    //这里打印一下发送的长度
      Log.i("sendImgMsg", "len: "+len);
      out.writeLong(len);
      out.write(bout.toByteArray());
    }

运行测试

还是先开启服务端,服务器变成:

然后点击app的发送图片按钮,app中打印日志:

说明发送的图片的长度是11418个字节,大致换算大小是11k。

然后看服务端的日志:

接收到的长度也是11418,并且保存到了文件,

关于心跳包

上面的例子中,每发送一次之后就把链接关闭了:

out.close();
socket.close();
serverSocket.close();

心跳其实就是定期向服务端发送一个小数据,比如0.

让服务器知道这个链接还有用,不用关闭。

简单实现起来就是客户端通过一个无限循环,不停地向服务端发送消息,服务端通过一个无限循环不停地接收消息,都不关闭这个链接就行了。

服务端

public static void main(String[] args) {
        try {
          System.out.println("开始接收信息");
            ServerSocket serverSocket = new ServerSocket(POST);
            socket = serverSocket.accept();
            DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());

            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            long len = 0;
                            len = dataInputStream.readLong();
                            System.out.println("len = " + len);
                            byte[] bytes = new byte[(int) len];
                            dataInputStream.readFully(bytes);
                            String s = new String(bytes);
                            System.out.println("data = " + s);
                        } catch (IOException e) {
                            e.printStackTrace();
                            isConnect = false;
                        }
                    }
                }
            }).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

客户端,一秒发送一次消息

try {
  //建立一次链接
    socket = new Socket(host,post);
    outputStream = new DataOutputStream(socket.getOutputStream());
    inputStream = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
    e.printStackTrace();
}
Log.i(TAG, "run: 开始循环发送发送心跳");
//一秒发送一个0,
while (true){
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Log.i(TAG, "run: 发送心跳0");
    try {
        outputStream.writeLong("0".getBytes().length);
        outputStream.write("0".getBytes());
    } catch (IOException e) {
        e.printStackTrace();
        isConnect = false;
    }
}

运行测试

先运行服务端:

然后点击发送心跳的按钮,app的日志中打印:

在服务端的日志中可以看到:

一秒一次

Android socket与服务器通信及心跳连接的实现

在项目中,有如下需求:Android客户端向服务器发送数据,收到服务器返回的数据发送成功标识后,客户端即与服务器建立数据一来一往的心跳连接,若服务器端断开时,客户端接收到通知,关闭Service停止发...

【Android】Android心跳包详解

在做IM通信时,都会遇到这样一个概念:心跳包。又是一个比较抽象的概念,那么心跳包到底是什么呢? 之前做的机房预定系统里,使用的Socket和服务器通信。长连接测试服务器: Socket基本上都这么...

Android socket通信的长连接与心跳检测

在Android开发中,我们可能需要和服务器保持连接不断开,这时需要用到socket通信的长连接,并且定时发送消息检测是否是连接状态——心跳检测。 我们需要一个客户端和一个服务器端的...

socket长时间连接,需要心跳包。

其次socket的长时间连接的话,是需要心跳包。

心跳包机制及Socket通信服务的心跳包

本文转自http://www.cppblog.com/tx7do/archive/2009/11/09/100513.html  http://xue08161981.blog.163.com/blo...
  • QGJava
  • QGJava
  • 2010年07月19日 11:15
  • 26861

Android客户端和服务器交换数据的一种方案

由于很多因素使得现在

Android Socket(简单的字符串)

简单的介绍下Android的socket编程,

在Android上面如何使用带有心跳检测的Socket

由于移动设备的网络的复杂性,经常会出现网络断开,如果没有心跳包的检测,客户端只会在需要发送数据的时候才知道自己已经断线,会延误,甚至丢失服务器发送过来的数据。一下简单建立一个带有心跳检测的Socket...

ios socket第三方框架 AsyncSocket使用简介,连接,心跳,断线,数据发送与接收

摘要 ios socket第三方框架 AsyncSocket使用简介,连接,心跳,断线,数据发送与接收 iOS socket AsyncSocket 即时通讯 目录[-] ...
  • Lu_Ca
  • Lu_Ca
  • 2015年09月15日 15:54
  • 3447
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android-socket的基本使用,发送文字和图片以及心跳
举报原因:
原因补充:

(最多只允许输入30个字)