Android客户端通过TCP接收服务器端发送的数据

引言

   因为我确实不懂TCP通信这一块儿,最近项目中要实现客户端接收服务器端发送过来的数据(这个数据是int型的,范围是0~360,而且服务器端用C语言写的,每一秒发送一次,客户端只需要不断接收就好了),很开心的用BufferedReader读取数据,结果发现一直读取不到数据,这下就慌了,搞了整整半天才用DataInputStream通过byte读取到数据。

一、BufferedReader

       BufferedReader可以从字符输入流中读取文本,通过缓存来达到高效的读取字符、数组等数据。

       说白了用它是因为它比较高效,它里边默认缓冲区的大小是8k。说下它的两个主要的函数:

       1.Read()   读取并返回一个单一的字符,做为int类型返回,这个类型的范围是0~65535,因为一个char的范围是-128~128,实际值是-128~127,当读取的值大于等于128时,返回的均是65535,具体代码如下:

public class TcpConnection extends Thread {
    private String ipAddress;
    private int ipPort;
    TcpResult tcpResult;

    public TcpResult getTcpResult() {
        return tcpResult;
    }

    public void setTcpResult(TcpResult tcpResult) {
        this.tcpResult = tcpResult;
    }

    public TcpConnection(String address, int port) {
        this.ipAddress = address;
        this.ipPort = port;
    }

    Socket socket;
    InputStream is;
    BufferedReader br;


    @Override
    public void run() {
        super.run();
        try {
            socket = new Socket(ipAddress, ipPort);
             socket.setSoTimeout(20000);
            if (socket.isConnected() && !socket.isClosed()) {
                is = socket.getInputStream();
                Log.e("test", "connect success");

                br = new BufferedReader(new InputStreamReader((is));
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        int data = 0;
                        try {
                            while ((data=br.read())!=-1) {

                                Log.e("test", "data="+data);
                                tcpResult.onSuccess(data);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();

            }


        } catch (IOException e) {
            e.printStackTrace();
            Log.e("test", e.toString());
           // tcpResult.onFailed(e.toString());
            close();
        }
    }


    public void close() {
        Log.e("test", "连接断开");
        CloseUtil.closeQuiety(br);
        CloseUtil.closeQuiety(is);
        CloseUtil.closeQuiety(socket);
    }

    public interface TcpResult {
        void onSuccess(int result);

        void onFailed(String error);
    }

}

为了不让C端的工程师觉得我太菜,我自己用安卓写了服务器端先自测,服务器端的代码为:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ServerThread st = new ServerThread();
        new Thread(st).start();
    }


    class ServerThread implements Runnable {

        @Override
        public void run() {
            try {
                ServerSocket ss = new ServerSocket(2000);
                while (true) {
                    Socket socket = ss.accept();
                    if (socket.isConnected()) {
                        OutputStream os = socket.getOutputStream();
                        sendData(os);
                        Log.e("test", "发送成功");
                        os.close();
                    }
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                Log.e("test", "error" + e.toString());
            }
        }
    }

    private void sendData(OutputStream outputStream) {
        for (int i = 1; i < 360; i++) {
            try {
                Thread.sleep(1000);
                outputStream.write(i);
                Log.e("test", "发送了" + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

结果是,客户端接收到的数据1~127均没有问题,128开始就是65535,我一下子就懵了,各种百度,就是因为char只能表示一个字符,而一个整数有4个字符,相当于没读完数据,所以会有问题。

在BufferedReader中有一个readLine()方法,它相当于读取每句话,或者到\n(换行)或者\r(回车)才会截止,实际代码为把上边客户端的接收数据的线程改为如下代码:

final BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            String data;
            while ((data=bufferedReader.readLine())!=null) {
                Log.e("test", data + "  **");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}).start();

这样写明显不行,服务器端是不断发送int型数据,我这边用String接收,服务器端不可能每次发完一个数据给我加个“\n”或者"\r"结束,到这里好烦躁,一直各种百度,网上都是这种方法,还有一个read(char[] cbuf,int off,int len),这个方法返回的是一个字符数组,也不行。

二.DataInputStream

  后来写c的老大实在等不了我了,找我的小伙伴给我帮忙,小伙伴问我要不试试用byte读取,因为流都是以byte的形式传输的,所以这样读肯定没有问题,于是我试着用DataInputStream来读取数据,代码如下:

public class TcpConnection extends Thread {
    private String ipAddress;
    private int ipPort;
    TcpResult tcpResult;

    public TcpResult getTcpResult() {
        return tcpResult;
    }

    public void setTcpResult(TcpResult tcpResult) {
        this.tcpResult = tcpResult;
    }

    public TcpConnection(String address, int port) {
        this.ipAddress = address;
        this.ipPort = port;
    }

    Socket socket;
    InputStream is;
    DataInputStream dis;


    @Override
    public void run() {
        super.run();
        try {
            socket = new Socket(ipAddress, ipPort);
            socket.setSoTimeout(20000);
            if (socket.isConnected() && !socket.isClosed()) {
                is = socket.getInputStream();
                Log.e("test", "connect success");
                final byte[] bytes = new byte[2];
                dis = new DataInputStream(is);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        int len = 0;
                        try {
                            while ((len = dis.read(bytes)) != -1) {

                                int value1 = bytes[0] & 0xff;
                                int value2 = bytes[1] & 0xff;
                                int iii = (int) ((value2 & 0xff) << 8) | ((value1 & 0xff) << 0);
                                Log.e("test", "len=" + len + "  " + value1 + " ** " + value2 + "  " + "  " + iii);
                                tcpResult.onSuccess(iii);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("test", e.toString());
            //      tcpResult.onFailed(e.toString());
            close();
        }
    }
    public void close() {
        Log.e("test", "连接断开");
        CloseUtil.closeQuiety(dis);
        CloseUtil.closeQuiety(is);
        CloseUtil.closeQuiety(socket);
    }

    public interface TcpResult {
        void onSuccess(int result);
        void onFailed(String error);
    }
}

 这样写最终没有问题,从服务器端接收到的数据最终完美的转了过来,至此,谢谢我的小伙伴的提醒,哈哈~~

   2018年4月4日补加:上边定义的byte数组的大小是2,问题刚好出在这儿,因为一个int型的整数包含4个byte,就会出现收到的第一组数据为正常数据,第二组数据为两个0,即收到一个int型的数据会接收两次,第一次为两个byte,第二次也为两个byte,一个数据也接收了两次,所以要改变数组的大小为4。

 解释下下边这块儿代码:

while ((len = dis.read(bytes)) != -1) {

    int value1 = bytes[0] & 0xff;
    int value2 = bytes[1] & 0xff;
    int iii = (int) ((value2 & 0xff) << 8) | ((value1 & 0xff) << 0);
    Log.e("test", "len=" + len + "  " + value1 + " ** " + value2 + "  " + "  " + iii);
    tcpResult.onSuccess(iii);
}
因为如果服务器端传过来的数据是360,360转成二进制就是101101000,是9位,所以传过来的就是两个byte,所以byte[0]转成整型就是104(二进制为1101000),byte[1]转成int就是1(二进制是00000001),所以要想得到360,就需要把byte[1]左移8位再加上byte[0],然后把它们转成int才可以,
int iii = (int) ((value2 & 0xff) << 8) | ((value1 & 0xff)

这句就是实现了上边的要求,最终可以打印出360,在Java中"|"这个符号是“位运算符”或(二进制,相应的二进制位上只要有一个为1,结果就为1,两个都为0的话结果为0)。研究出来对我这种基础不好的人来说真的是耗费了太多的时间。

 其实我自己写的服务器端(上边的代码),如果直接发送整型数据的话,这边的收到的会有问题,但是这边接收C发送的数据没有问题,如果Java写的服务器端想要把数据发过来并成功接收的话,需要把整型数据写成字符串,通过

"data".getBytes();

来发送,如果是int型的话,发送"360".getBytes(),才可以。

至此,终于完成了接收数据这项任务,说到底还是要好好努力啊!


  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值