公司新项目中涉及到Socket通信有关的东西,虽然之前接触到一点,不过好长时间不用基本上忘个七七八八了,网上查了查资料,根据项目中的需求自己做了个小Demo,欢迎大家指正.
1.需求:
1.1:客户端测量完毕后,将测量数据以Socket的方式上传的服务端;
1.2:客户端每过1秒中向服务端发送一次心跳,服务端记录心跳时间并且每两秒钟判断一次心跳间隔,如果上一次心跳时间与现在时间间隔超过两秒钟视为连接异常,否则为连接正常.
2.基本概念
2.1:TCP IP协议:在早期计算机网络中,各大厂商都有着自己的一套网络协议,互不兼容,这就导致说中文的只能跟说中文的交流而不能跟说英文的交流,这样的现象很不友好,为了让不同种类的所有计算机都能毫无障碍的交流,就必须要使用一套统一的标准,所以internet应运而生,在Internet的各种协议中,最重要的两个协议就是TCP和IP协议.
2.2:在数以万计的计算机中,如果你想和某台计算机A通信,那么就必须知道A这台计算机在互联网中的ip地址(ip地址是每个计算机在网络中的唯一标识),而ip协议就是负责把你要发送的数据分割成n段打包然后从你的计算机发送到A计算机,但是在运输的过程中ip协议不会保证能把数据完整的送到A计算机,当然也不能保证数据会按照切割的先后顺序到达A计算机.
2.3:TCP协议是建立在IP协议之上的协议,TCP协议会在两台计算机之间建立可靠的连接,并且会保证ip协议切割打包的ip包完整顺序的到达A计算机,即使在传输的过程中丢包了,TCP协议也会重新发送ip包,保证数据的完整性.TCP协议会把传输的数据流分成适当长度的报文段,一个单独的报文段除了包含要传输的数据流外还包含源计算机ip地址、端口和目标计算机ip地址、端口.
2.4:端口:在给A计算机发消息的时候,只知道A的IP地址是不够的,还需要知道给A计算机的哪个程序发消息,例如我是给A计算机的微信发送的数据,那么我就需要知道微信这个网络程序向计算机申请的端口号,这样两个计算机中的两个网络程序才能互相建立连接.
2.5:TCP/IP的四层协议:
2.5.1网络接口层(数据链路层)是物理传输通道,对于网络层下发的ip数据包通过网络选择合适的路径转发;对于从网络上接收到的物理帧,抽出ip数据包并交给网络层.
2.5.2网络层对于网络接口层上传的ip数据包进行数据检验,检验此数据是否到达目的地址,是则去除包头将剩余数据上传到传输层,否则选择合适的路径继续转发;对于传输层下发的分组数据添加包头,封装成ip数据包交给网络接口层.
ARP:address resolution protocol 地址解析协议,通过获取到的ip地址来寻找获取相应主机的MAC地址;
RARP: reverse address resolution protocol 反地址解析协议,通过已知的MAC地址来获取相应主机的ip地址;
ICMP:Internet control manage protocol 网络控制管理协议,是网络层的补充,用于实现报文回送功能,像PING命令就是一种ICMP协议,用于发送ICMP的echo包,用于检验网络是否通畅。
2.5.3传输层实现两台主机的应用程序之间的端到端的通信,传输层可以格式化信息流、提供可靠传输(为实现可靠传输,传输层协议规定接收端必须发回确认,假如分组丢失,必须重新发送).
2.5.4应用层是应用程序间沟通的层,这里对用户来说是一个个的图形化界面,如浏览器 QQ 微信等,为发起网络请求、接收网络传输提供具体的应用程序.
3.Socket
3.1 Socket(套接字)对TCP/IP协议进行封装,是一个供我们编程调用的接口,它属于传输层,所以Socket分为流套接字(streamsocket,基于TCP协议)和数据报套接字(datagramsocket,基于UDP协议),
TCP协议:提供的是面向连接、可靠的字节流服务,客户端和服务器进行数据交互前双方必须建立连接,之后才能传输数据,TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP协议:是一个简单的面向数据报的运输层协议,他只是负责把应用程序传给ip层的数据报发出去(不会像TCP那样拆分),但是不能保证他们能到达目的地,由于UDP在传输数据的时候不用建立连接、没有超时重发等机制,故UDP的传输速度很快.
3.2 Socket使用(基于TCP)
<!-- 允许应用程序改变网络状态 --> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <!-- 允许应用程序改变WIFI连接状态 --> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 允许应用程序访问有关的网络信息 --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 允许应用程序访问WIFI网卡的网络信息 --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 允许应用程序完全使用网络 --> <uses-permission android:name="android.permission.INTERNET" />//客户端
Socket mSocket = new Socket("192.168.55.13", 20000); mSocket.setSoTimeout(10000);//设置连接超时时间 mSocket.setTcpNoDelay(true);//设置立即发送 DataOutputStream dataOutputStream = new DataOutputStream(mSocket.getOutputStream());//获取输出流 DataInputStream dataInputStream = new DataInputStream(mSocket.getInputStream());//获取输入流//读取服务端发送过来的消息
class SocketThrad extends Thread { @Override public void run() { while (true) { if (dataInputStream != null) { try { final String msg = dataInputStream.readUTF(); runOnUiThread(new Runnable() { @Override public void run() { mText.setText(msg); } }); } catch (IOException e) { try { dataOutputStream.close(); dataInputStream.close(); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } } } }//给服务端发送消息
@Override public void onClick(View v) { if (dataOutputStream != null) { try { dataOutputStream.writeUTF("消息"); dataOutputStream.flush(); } catch (IOException e) { try { dataOutputStream.close(); dataInputStream.close(); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } //服务端代码class SocketThread extends Thread { @Override public void run() { try { mServerSocket = new ServerSocket(20000); while (true) { Log.d("TAG", "等待SOCKET连接"); mSocket = mServerSocket.accept(); dataOutputStream = new DataOutputStream(mSocket.getOutputStream()); dataInputStream = new DataInputStream(mSocket.getInputStream()); Log.d("TAG", "SOCKET连接"); readMsg(dataInputStream); } } catch (IOException e) { try { dataOutputStream.close(); dataInputStream.close(); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } }private void readMsg(DataInputStream dataInputStream) { while (true) { if (dataInputStream != null) { try { Log.d("TAG", "等待数据发送"); final String msg = dataInputStream.readUTF(); runOnUiThread(new Runnable() { @Override public void run() { Log.d("TAG", "有数据发送"); mText.setText(msg); } }); } catch (IOException e) { try { dataOutputStream.close(); dataInputStream.close(); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } } }//给客户端发送消息
@Override public void onClick(View v) { if (dataOutputStream != null) { try { dataOutputStream.writeUTF("消息"); dataOutputStream.flush(); } catch (IOException e1) { try { dataOutputStream.close(); dataInputStream.close(); } catch (IOException e) { e.printStackTrace(); } e1.printStackTrace(); } } }
目前只是实现了两个单机之间的相互简单通信,贴上了部分代码以供大家参考,有什么建议欢迎指出;