什么是Socket?
socket是一个编程调用接口,不是一种协议,是属于传输层,主要解决数据如何在网络中传输。通过Socket,我们才能在Andorid平台上通过 TCP/IP协议进行开发,实现客户端和服务端双向的通信连接。
IP 协议
- 即互联网协议(Internet Protocol),用于报文交换网络的一种面向数据的协议,是TCP/IP协议中网络层的主要协议。
- 作用:根据源主机和目的主机的地址传送数据。
- IP定义了寻址方法和数据报的封装结构。(IPv4、IPv6)
TCP协议
- TCP/IP模型中,面向连接的、可靠的、基于字节流的传输层通信协议;
传输层存在的意义:
应用层之间需要可靠的,像管道一样的连接,但网络层不提供这样的流机制,只提供不可靠的包交换,因此需要传输层的协调。
TCP协议作用:
- TCP协议把应用层向传输层发送网间传输的、用8位字节表示的数据流分成适当长度的报文段(受数据链路层最大传输单元MTU限制);
- 把包传给网络层,由网络层将包传送给接收端实体的传输层;TCP为了不发生丢包,给每个包一个序号,同时序号保证了传送到接收端实体的包能按序接收。然后接收实体成功收到包后回复相应的ACK确认;
- 如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就会被认为丢失,将被重传;
- TCP协议用一个校验和(Checksum)函数来检验数据是否错误,发送与接收时候都需要计算校验和。
UDP协议
- TCP/IP模型中面向无连接的传输层协议,提供面向事务的简单不可靠信息传输服务。
- UDP不提供对IP协议的可靠机制、流控制以及错误恢复功能等,在数据传输之前不需要简历连接。
- 由于UDP较简单,因此比TCP负载消耗少。
PC服务端:可以用的是SocketTool软件模拟
image.png流程
1、连接socket的服务端(ip和port):开启异步线程和socket
2、发送数据(OutputStream):异步
3、接收数据(InputStream):注意连接状态,异步读取
4、关闭连接:关闭socket和对应线程
注意
1、异常:android.os.NetworkOnMainThreadException。 socket需要在线程中使用
2、前后端统一传输或者接收协议 [requestcode size d1 d2 d3 ... ],在解析时候用得到
3、实施监控socket的连接状态,还是用心跳包发过去,然后返回数据,一段时间没有的话则代表socket连接失败。
tcp-socket的客户端
/**
* Created by wujn on 2019/2/15.
* Version : v1.0
* Function: tcp client 长度无限制
*/
public class TcpClient {
/**
* single instance TcpClient
* */
private static TcpClient mSocketClient = null;
private TcpClient(){}
public static TcpClient getInstance(){
if(mSocketClient == null){
synchronized (TcpClient.class) {
mSocketClient = new TcpClient();
}
}
return mSocketClient;
}
String TAG_log = "Socket";
private Socket mSocket;
private OutputStream mOutputStream;
private InputStream mInputStream;
private SocketThread mSocketThread;
private boolean isStop = false;//thread flag
/**
* 128 - 数据按照最长接收,一次性
* */
private class SocketThread extends Thread {
private String ip;
private int port;
public SocketThread(String ip, int port){
this.ip = ip;
this.port = port;
}
@Override
public void run() {
Log.d(TAG_log,"SocketThread start ");
super.run();
//connect ...
try {
if (mSocket != null) {
mSocket.close();
mSocket = null;
}
InetAddress ipAddress = InetAddress.getByName(ip);
mSocket = new Socket(ipAddress, port);
//设置不延时发送
//mSocket.setTcpNoDelay(true);
//设置输入输出缓冲流大小
//mSocket.setSendBufferSize(8*1024);
//mSocket.setReceiveBufferSize(8*1024);
if(isConnect()){
mOutputStream = mSocket.getOutputStream();
mInputStream = mSocket.getInputStream();
isStop = false;
uiHandler.sendEmptyMessage(1);
}
/* 此处这样做没什么意义不大,真正的socket未连接还是靠心跳发送,等待服务端回应比较好,一段时间内未回应,则socket未连接成功 */
else{
uiHandler.sendEmptyMessage(-1);
Log.e(TAG_log,"SocketThread connect fail");
return;
}
}
catch (IOException e) {
uiHandler.sendEmptyMessage(-1);
Log.e(TAG_log,"SocketThread connect io exception = "+e.getMessage());
e.printStackTrace();
return;
}
Log.d(TAG_log,"SocketThread connect over ");
//read ...
while (isConnect() && !isStop && !isInterrupted()) {
int size;
try {
byte[] buffer = new byte[1024];
if (mInputStream == null) return;
size = mInputStream.read(buffer);//null data -1 , zrd serial rule size default 10
if (size > 0) {
Message msg = new Message();
msg.what = 100;
Bundle bundle = new Bundle();
bundle.putByteArray("data",buffer);
bundle.putInt("size",size);
bundle.putInt("requestCode",requestCode);
msg.setData(bundle);
uiHandler.sendMessage(msg);
}
Log.i(TAG_log, "SocketThread read listening");
//Thread.sleep(100);//log eof
}
catch (IOException e) {
uiHandler.sendEmptyMessage(-1);
Log.e(TAG_log,"SocketThread read io exception = "+e.getMessage());
e.printStackTrace();
return;
}
}
}
}
//==============================socket connect============================
/**
* connect socket in thread
* Exception : android.os.NetworkOnMainThreadException
* */
public void connect(String ip, int port){
mSocketThread = new SocketThread(ip, port);
mSocketThread.start();
}
/**
* socket is connect
* */
public boolean isConnect(){
boolean flag = false;
if (mSocket != null) {
flag = mSocket.isConnected();
}
return flag;
}
/**
* socket disconnect
* */
public void disconnect() {
isStop = true;
try {
if (mOutputStream != null) {
mOutputStream.close();
}
if (mInputStream != null) {
mInputStream.close();
}
if (mSocket != null) {
mSocket.close();
mSocket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
if (mSocketThread != null) {
mSocketThread.interrupt();//not intime destory thread,so need a flag
}
}
/**
* send byte[] cmd
* Exception : android.os.NetworkOnMainThreadException
* */
public void sendByteCmd(final byte[] mBuffer,int requestCode) {
this.requestCode = requestCode;
new Thread(new Runnable() {
@Override
public void run() {
try {
if (mOutputStream != null) {
mOutputStream.write(mBuffer);
mOutputStream.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
/**
* send string cmd to serial
*/
public void sendStrCmds(String cmd, int requestCode) {
byte[] mBuffer = cmd.getBytes();
sendByteCmd(mBuffer,requestCode);
}
/**
* send prt content cmd to serial
*/
public void sendChsPrtCmds(String content, int requestCode) {
try {
byte[] mBuffer = content.getBytes("GB2312");
sendByteCmd(mBuffer,requestCode);
}
catch (UnsupportedEncodingException e1){
e1.printStackTrace();
}
}
Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what){
//connect error
case -1:
if (null != onDataReceiveListener) {
onDataReceiveListener.onConnectFail();
disconnect();
}
break;
//connect success
case 1:
if (null != onDataReceiveListener) {
onDataReceiveListener.onConnectSuccess();
}
break;
//receive data
case 100:
Bundle bundle = msg.getData();
byte[] buffer = bundle.getByteArray("data");
int size = bundle.getInt("size");
int mequestCode = bundle.getInt("requestCode");
if (null != onDataReceiveListener) {
onDataReceiveListener.onDataReceive(buffer, size, mequestCode);
}
break;
}
}
};
/**
* socket response data listener
* */
private OnDataReceiveListener onDataReceiveListener = null;
private int requestCode = -1;
public interface OnDataReceiveListener {
public void onConnectSuccess();
public void onConnectFail();
public void onDataReceive(byte[] buffer, int size, int requestCode);
}
public void setOnDataReceiveListener(
OnDataReceiveListener dataReceiveListener) {
onDataReceiveListener = dataReceiveListener;
}
}
MainActivity.java:使用
private void initListener(){
//socket connect
btn_connect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String ip = et_ip.getText().toString();
String port = et_port.getText().toString();
if(TextUtils.isEmpty(ip)){
Toast.makeText(TcpActivity.this,"IP地址为空",Toast.LENGTH_SHORT).show();
return;
}
if(TextUtils.isEmpty(port)){
Toast.makeText(TcpActivity.this,"端口号为空",Toast.LENGTH_SHORT).show();
return;
}
connect(ip, Integer.parseInt(port));
}
});
//socket disconnect
btn_disconnect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
disconnect();
}
});
//socket send
btn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (TcpClient.getInstance().isConnect()) {
byte[] data = et_send.getText().toString().getBytes();
send(data);
} else {
Toast.makeText(TcpActivity.this,"尚未连接,请连接Socket",Toast.LENGTH_SHORT).show();
}
}
});
//clear receive
btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tv_receive.setText("");
}
});
}
/**
* socket data receive
* */
private void initDataReceiver(){
TcpClient.getInstance().setOnDataReceiveListener(dataReceiveListener);
}
/**
* socket connect
* */
private void connect(String ip, int port){
TcpClient.getInstance().connect(ip, port);
}
/**
* socket disconnect
* */
private void disconnect(){
TcpClient.getInstance().disconnect();
tv_state.setText("未连接");
}
/**
* socket send
* */
private void send(byte[] data){
TcpClient.getInstance().sendByteCmd(data,1001);
}
/**
* socket data receive
* data(byte[]) analyze
* */
private TcpClient.OnDataReceiveListener dataReceiveListener = new TcpClient.OnDataReceiveListener() {
@Override
public void onConnectSuccess() {
Log.i(TAG_log,"onDataReceive connect success");
tv_state.setText("已连接");
}
@Override
public void onConnectFail() {
Log.e(TAG_log,"onDataReceive connect fail");
tv_state.setText("未连接");
}
@Override
public void onDataReceive(byte[] buffer, int size, int requestCode) {
//获取有效长度的数据
byte[] data = new byte[size];
System.arraycopy(buffer, 0, data, 0, size);
final String oxValue = Arrays.toString(HexUtil.Byte2Ox(data));
Log.i(TAG_log,"onDataReceive requestCode = "+requestCode + ", content = "+oxValue);
tv_receive.setText(tv_receive.getText().toString() + oxValue + "\n");
}
};
@Override
protected void onDestroy() {
TcpClient.getInstance().disconnect();
super.onDestroy();
}
参考博文:
https://www.jianshu.com/p/4fca571f8a74
https://www.jianshu.com/p/7dac7e5dffb2
https://www.jianshu.com/p/089fb79e308b
OkSocket库:https://www.jianshu.com/p/8ee3ee766265
网友评论