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

项目需求收集通过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的日志中打印:

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

一秒一次

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • 原文地址:http://www.cnblogs.com/skynet/archive/2010/12/12/190...
    archyly阅读 1,028评论 0 8
  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,202评论 0 6
  • 大纲 一.Socket简介 二.BSD Socket编程准备 1.地址 2.端口 3.网络字节序 4.半相关与全相...
    VD2012阅读 2,103评论 0 5
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 1,872评论 0 7
  • 我可否出现在你那温馨的床头亲吻你的额头 我可曾出现在你那浪漫的梦里牵着你的手一起奔跑 你愿意梦醒的时候和我一起继续...
    沧桑叔阅读 235评论 0 1