Android——Tcp服务端
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
Tcp分为服务端(网络应用程序)和客户端(网络应用程序),Tcp通信过程,首先打开服务器监听自己的网络通信端口(假设为7628),打开客户端,设置好要连接的ip地址和服务器的网络通信端口(7628),这样服务器监听到网络通信端口有连接,二者就建立了连接。
客户端:可以连接服务端、发送数据、接收数据、关闭连接等。
服务端:可以实现绑定通信端口,接收客户端连接、接收数据、发送数据等。
在java项目中我们可以较为快捷地使用TCP通讯,但想要在Android中使用还有很多要注意的地方。下面我们来学习下Tcp服务端在Android上的实现。
我之前做过一个项目里有个要通过Tcp获取智能手环上电量、心率,血压血氧、步数等基本数据的功能。(这里只是个功能一样的简单小Demo,报文需要处理才能看到想要的信息)
服务端ServerSocket的构造方法:
1、ServerSocket():构造一个新的未绑定的ServerSocket。
2、ServerSocket(int port):构造一个新的ServerSocket并绑定到指定端口,如果port等于0,则端口由系统自动分配。
3、ServerSocket(int port,int backlog):构造一个新的ServerSocket并绑定到指定端口以及指定进入队列的数目。如果port等于0,则端口由系统自动分配。
4、ServerSocket(int port,int backlog,InetAddress localAddress):构造一个新的ServerSocket并绑定到指定端口以及指定进入队列的数目。如果port等于0,则端口由系统自动分配。如果localAddress为null,则可以使用任意地址。
Tcp服务端使用步骤为:
①创建ServerSocket,绑定指定端口
try {
//开启服务、指定端口号
socket = new ServerSocket(mServerPort);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "服务开启失败", Toast.LENGTH_SHORT).show();
return;
}
②调用ServerSocket的accept(),监听连接请求
③调用Socket类中的getInputStream()和getOutputStream()获取输入输出流
class SocketAcceptThread extends Thread {
@Override
public void run() {
try {
//等待客户端的连接,Accept会阻塞,直到建立连接,
//所以需要放在子线程中运行。
mSocket = socket.accept();
//获取输入流
mInStream = mSocket.getInputStream();
//获取输出流
mOutStream = mSocket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
startAccept();
return;
}
Log.i(TAG, "accept success");
SocketReceive();
}
}
④接收、发送数据
/**
* 发送
*/
private void sendText() {
if (mSocket == null) {
Toast.makeText(MainActivity.this, "没有客户端连接", Toast.LENGTH_LONG).show();
return;
}
String str = content.getText().toString();
if (str.length() == 0) {
return;
}
Message msg = new Message();
msg.what = Two;
msg.obj = str;
mSubThreadHandler.sendMessage(msg);
}
private void writeMsg(String msg) {
if (msg.length() == 0 || mOutStream == null) {
return;
}
try {
//发送
mOutStream.write(msg.getBytes());
mOutStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
int count = mInStream.read(buffer);
⑤关闭服务
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
注意:
<uses-permission android:name="android.permission.INTERNET"/>
下面是MainActivity.java完整代码 :
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
/**
* 设置
*/
private Button setting;
/**
* 开启服务
*/
private Button open_service;
/**
* 输入内容
*/
private EditText content;
/**
* 发送
*/
private Button send_out;
/**
* 接收到的数据
*/
private EditText receive;
private final String TAG = "MainActivity";
private static final char HexCharArr[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* 服务端端口
*/
private int mServerPort;
private SharedPreferences sp;
private static final String PORT = "port";
public Socket mSocket;
private HandlerThread mHandlerThread;
/**
* 子线程中的Handler实例
*/
private Handler mSubThreadHandler;
private SocketAcceptThread mAcceptThread;
private SocketReceiveThread mReceiveThread;
private ServerSocket socket;
/**
* 输出流
*/
private OutputStream mOutStream;
/**
* 输入流
*/
private InputStream mInStream;
private static final int One = 1;
private static final int Two = 2;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case One:
String sb = receive.getText().toString() + "\r\n" + (String) msg.obj;
receive.setText(sb);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setting = findViewById(R.id.setting);
open_service = findViewById(R.id.open_service);
content = findViewById(R.id.content);
send_out = findViewById(R.id.send_out);
receive = findViewById(R.id.receive);
setting.setOnClickListener(this);
open_service.setOnClickListener(this);
send_out.setOnClickListener(this);
//获取sharedPreferences对象
sp = getSharedPreferences("settings", Context.MODE_PRIVATE);
//如果key不存在,则返回默认defValue
mServerPort = sp.getInt(PORT, 7628);
initHandlerThraed();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestory");
closeConnect();
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void closeConnect() {
try {
if (mOutStream != null) {
mOutStream.close();
}
if (mInStream != null) {
mInStream.close();
}
if (mSocket != null) {
mSocket.close(); //关闭socket
mSocket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
if (mReceiveThread != null) {
mReceiveThread.threadExit();
mReceiveThread = null;
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
//设置
case R.id.setting:
set();
break;
//开启服务
case R.id.open_service:
startServer();
break;
//发送
case R.id.send_out:
sendText();
break;
default:
break;
}
}
/**
* 设置
*/
private void set() {
//获取布局
View setview = LayoutInflater.from(this).inflate(R.layout.port, null);
//获取布局中的控件
final EditText editport = (EditText) setview.findViewById(R.id.server_port);
Button ensureBtn = (Button) setview.findViewById(R.id.server_ok);
editport.setText(sp.getInt(PORT, 7628) + "");
final android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(this);
//设置dialog显示一个view
builder.setView(setview);
//dialog显示
final AlertDialog dialog = builder.show();
ensureBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String port = editport.getText().toString();
if (port != null && port.length() > 0) {
mServerPort = Integer.parseInt(port);//将内容转换成整数
}
SharedPreferences.Editor editor = sp.edit(); //SharedPreferences.Editor 简单的数据存储
editor.putInt(PORT, mServerPort); //存储数据
editor.commit(); //提交
dialog.dismiss(); //dialog消失
}
});
}
/**
* 开启服务
*/
void startServer() {
try {
//开启服务、指定端口号
socket = new ServerSocket(mServerPort);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "服务开启失败", Toast.LENGTH_SHORT).show();
return;
}
startAccept();
Toast.makeText(this, "服务开启", Toast.LENGTH_LONG).show();
}
/**
* 开启SocketAcceptThread()线程
*/
private void startAccept() {
mAcceptThread = new SocketAcceptThread();
mAcceptThread.start();
}
class SocketAcceptThread extends Thread {
@Override
public void run() {
try {
//等待客户端的连接,Accept会阻塞,直到建立连接,
//所以需要放在子线程中运行。
mSocket = socket.accept();
//获取输入流
mInStream = mSocket.getInputStream();
//获取输出流
mOutStream = mSocket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
startAccept();
return;
}
Log.i(TAG, "accept success");
SocketReceive();
}
}
private void SocketReceive() {
mReceiveThread = new SocketReceiveThread();
mReceiveThread.start();
}
class SocketReceiveThread extends Thread {
private boolean threadExit = false;
@Override
public void run() {
byte[] buffer = new byte[1024];
while (threadExit == false) {
try { //读取数据,返回值表示读到的数据长度。-1表示结束
int count = mInStream.read(buffer);
if (count == -1) {
Log.i(TAG, "read read -1");
startAccept();
break;
} else {
String receiveData;
receiveData = byteArrToHex(buffer);
Log.i(TAG, "read buffer:" + receiveData + ",count=" + count);
Message msg = new Message();
msg.what = One;
msg.obj = receiveData;
mHandler.sendMessage(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
void threadExit() {
threadExit = true;
}
}
/**
* 发送
*/
private void sendText() {
if (mSocket == null) {
Toast.makeText(MainActivity.this, "没有客户端连接", Toast.LENGTH_LONG).show();
return;
}
String str = content.getText().toString();
if (str.length() == 0) {
return;
}
Message msg = new Message();
msg.what = Two;
msg.obj = str;
mSubThreadHandler.sendMessage(msg);
}
private void writeMsg(String msg) {
if (msg.length() == 0 || mOutStream == null) {
return;
}
try {
//发送
mOutStream.write(msg.getBytes());
mOutStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 字节数组转换十六进制
*/
public static String byteArrToHex(byte[] btArr) {
char strArr[] = new char[btArr.length * 2];
int i = 0;
for (byte bt : btArr) {
strArr[i++] = HexCharArr[bt >>> 4 & 0xf];
strArr[i++] = HexCharArr[bt & 0xf];
}
return new String(strArr);
}
private void initHandlerThraed() {
//创建HandlerThread实例
mHandlerThread = new HandlerThread("handler_thread");
//开始运行线程
mHandlerThread.start();
//获取HandlerThread线程中的Looper实例
Looper loop = mHandlerThread.getLooper();
//创建Handler与该线程绑定。
mSubThreadHandler = new Handler(loop) {
@Override
public void handleMessage(Message msg) {
Log.i(TAG, "mSubThreadHandler handleMessage thread:" + Thread.currentThread());
switch (msg.what) {
case Two:
writeMsg((String) msg.obj);
break;
default:
break;
}
}
};
}
}
最后是运行效果:设置端口、开启服务、获取的数据(需要处理)
Demo中用到的Handler、dialog对话框、SharedPreferences不理解的可以参考一下我之前的发布的文章。