失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Android客户端通过TCP接收服务器端发送的数据

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

时间:2020-04-11 14:06:25

相关推荐

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() {@Overridepublic 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 {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ServerThread st = new ServerThread(); new Thread(st).start(); }class ServerThread implements Runnable {@Overridepublic 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() {@Overridepublic 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); }}

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

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(),才可以。

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

如果觉得《Android客户端通过TCP接收服务器端发送的数据》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。