最近在做一个机器人的项目,需要用到TCP通讯这个东西。需要在手机做一个客户端,然后上去网上查了巨久巨多代码,为了避免让有需要的人少走弯路,就做一篇博文来推一下自己的做法,如果各位大大们有什么好的建议,也希望各位可以在评论区写下高见抑或是邮箱到1262706641@qq.com。
由于个人的时间问题,目前这个工程的mian文件加已经上传到github上进行管理,各位大佬可以自取。
传送门:https://github.com/KinFaiLeong/Android-TCP
首先是布局文件的代码:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true">
<EditText
android:id="@+id/ip"
android:layout_width="280dp"
android:layout_height="50dp"
android:background="#7CB342"
android:hint="IP:"
android:textColor="#ffffff"
/>
<EditText
android:id="@+id/port"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="端口:"
android:background="#C0CA33"
android:layout_toRightOf="@id/ip"
android:inputType="number"
/>
<Button
android:id="@+id/connect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/port"
android:text="连接服务器"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<TextView
android:id="@+id/receive"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#00ACC1"
android:textColor="#FFFFFF"
android:hint="接收区"
android:textSize="18sp"
android:gravity="center_horizontal"
/>
<EditText
android:id="@+id/send"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#3949AB"
android:hint="发送:"
android:textColor="#ffffff"
android:layout_below="@id/receive"/>
<Button
android:id="@+id/btn_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送"
android:layout_below="@+id/send"
/>
</RelativeLayout>
</RelativeLayout>
为了更方便以后的维护和迭代,我将发送模块、连接接收模块放在了不同的线程之中。
接下来先来看看最简单的 发送线程 代码。
//线程名:Send
public class Send extends Thread {
private String send;
private OutputStream outputStream;
private InputStream inputStream;
private String ip;
private int port;
public Send(String msg,int port,String ip) {
this.send = msg;
this.ip = ip;
this.port = port;
}
@Override
public void run() {
try {
Socket socket = new Socket(ip,port);
send="客户端发来:"+send;
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
outputStream.write((send+"\n").getBytes("utf-8"));
outputStream.flush();
Log.v("AndroidChat","发送成功:"+send);
}
catch (Exception e){
Log.v("AndroidChat","发送失败:"+send+"error"+e.getMessage());
e.printStackTrace();
}
}
Socket,套接字。利用这个建立和服务器之间的连接,会用就行,想深入了解去看其他大大的详解,这里不做赘述。
(这里插入一些题外话,无论代码有多长,建议各位都应当逐个函数的输入,复制粘贴只会走更多弯路。)
接下来是发送接收线程的代码:
package com.example.tcptext;
import android.util.Log;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.net.Socket;
//线程名:ConnectThread
public class ConnectThread extends Thread {
//Socket msg = null;//定义socket
private OutputStream out_ip=null;//定义输出流(ip)
OutputStream outputStream=null;
private InputStream inputStream;
private StringBuffer stringBuffer;
private String ip;
private int port;
private Receive receive;
private String string;
private boolean isRun = true;
private MainActivity.Receive re;
public ConnectThread(String ip, int port, MainActivity.Receive re) {
this.ip = ip;
this.port = port;
this.re = re;
}
@Override
public void run(){
Socket so = null;
try {
so = new Socket(ip, port);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": Hello");
try {
inputStream = so.getInputStream();
out_ip = so.getOutputStream();
Log.v("AndroidChat","开始连接服务器:"+ip+"/"+port);
sleep(1000);
}
catch (IOException | InterruptedException e) {
Log.v("AndroidChat","连接服务器失败"+e.getMessage());
e.printStackTrace();
return;
}
Log.v("AndroidChat","成功连接上服务器");
/*
下面是接收模块,你可以尝试探究如何将这个模块放在接收线程中。
*/
try {
inputStream = so.getInputStream();
final byte[] buffer = new byte[1024];
final int len = inputStream.read(buffer);
System.out.println(new String(buffer,0,len));
Log.v("AndroidChat","接收成功:"+new String(buffer,0,len));
string = new String(buffer,0,len);
re.setString(string);
//上下两行会和MainActivity关联,是回调在显示屏的关键步骤。
MainActivity.callback();
System.out.println(new String(buffer,0,len));
} catch (IOException e) {
e.printStackTrace();
}
}
}
最后是核心模块(MainActivity)
package com.example.tcptext;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Button mBtn_send;
private Button mBtn_connect;
private EditText mEt_send;
private static TextView mTv_recv;
private String ip;
private int port;
private String msg;
private ConnectThread ct;
private Send send;
private Handler handler;
static Receive re = new Receive();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv_recv = findViewById(R.id.receive);
mBtn_connect = findViewById(R.id.connect);
mBtn_send = findViewById(R.id.btn_1);
final EditText mEt_ip = this.findViewById(R.id.ip);
final EditText mEt_port = this.findViewById(R.id.port);
final EditText mEt_send = this.findViewById(R.id.send);
/*
设置一个点击事件用以连接线程
*/
mBtn_connect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ip = mEt_ip.getText().toString();//获取输入的ip
port = Integer.parseInt(mEt_port.getText().toString());//获取输入的端口号
ct = new ConnectThread(ip, port, re);//创建一个线程来处理消息的收发
ct.start();
}
});
/*
设计一个点击事件用以发送消息
*/
mBtn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
msg = mEt_send.getText().toString();
send = new Send(msg, port, ip);
send.start();
}
});
}
/*
这里是和连接接收线程中的 re.setString(string) 联动的,自己理解一下怎么走在脑子里面走一次就差不多了。
*/
static class Receive{
private String string;
public String getString(){
return string;
}
public void setString(String s){
this.string = s;
}
}
/*
下面的callback和连接接收线程中的 MainActivity.callback() 关联,线程运行到那一步后结束回到MainActivity中。
*/
public static void callback(){
System.out.println("连接线程执行结束");
mTv_recv.setText(re.getString());//这里是将接收到的文字显示在接收框内。
}
}
注意:
1.这里的点击按钮必须要输入好IP和端口号否则会闪退,我也不知道是什么原因,有哪位大大知道的,麻烦说一下。
2.每次连接接收完信息后都要点击一次连接服务器或者发送一条消息。各位可以一起探讨一下如何重新开启线程,反正我又百年不用Java,这个问题就留给各位了,哈哈哈。
最后,我分享一下我做这个程序的具体思路。零基础的朋友可以参照一下思路来遨游博海:
1.找到两个标准的TCP应用。这很关键,你必须得有标准的应用来测试才知道这是好的还是坏的。
2.善于利用 System.out.println(“哔哩吧啦”); 这个东西能很好的帮助你检查代码运行到什么地方。
3.将整个应用开发分成三个模块:连接服务器模块,发送信息模块,接收信息模块。每个模块还能再细分步骤:1.固定信息的传递(如连接模块就是固定IP和端口号);2.随机信息的传递。
4.优化你的界面,优化代码,做成自己理想的样子。
2.之后我会创一个Github账号将源码放出来,以便和大家一起学习交流。
3.这里用到的两个标准软件:PC端:TCPCOM android端:TCP连接
最后希望大家一起加油!用心攻破每个难题!