开门见山
socket = new Socket("211.159.189.50", 2029);
使用这一行代码就能建立一个TCP客户端,并能成功连接服务器
所以,剧终!
哈哈哈!!!
好了,不开玩笑了,下面具体一步一步来:
上面说到使用一句话就能创建一个TCP客户端,并建立与服务器的连接,其实是真的,
不过能成功运行这句话还需要做点辅助工作
辅助工作1:
从Android4.0以后,就不支持在主线程中直接初始化socket了,具体原因请自行Google。
既然不支持在主线程中直接初始化,那我们能怎么办呢?只能创建子线程呗。
ConnectThread.java
package com.example.test2;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class ConnectThread extends Thread{
Socket socket = null; //定义socket
public void run(){
System.out.println(Thread.currentThread().getName()+": Hello");
try {
socket = new Socket("211.159.189.50", 2029);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
在eclipse中新建一个类,并继承Thread类,覆写run方法。那么在new一个ConnectThread 实例的时候,就相当于创建了一个线程,我们在线程中写入我们一直想实现的那句话 socket = new Socket("211.159.189.50", 2029);
然后在MainActivity.java中创建并启动上述线程
MainActivity.java
package com.example.test2;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
ConnectThread ct;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ct = new ConnectThread();
ct.start();
}
}
运行,看到服务器端已有一个设备连接进来,说明Android TCP客户端创建成功
[root@VM_0_14_centos tcp]# ./server2.out
...connection from host 183.194.175.168,port 16009,socket 4
...
辅助工作2:
因为涉及到联网操作,需要添加网络访问权限,具体自行Google
辅助工作3:
光连上有什么用,发个数据啊! 好,下面就向服务器发送数据
起码的样子有一下
相应的两份Java代码更新一下
ConnectThread.java
package com.example.test2;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class ConnectThread extends Thread{
Socket socket = null; //定义socket
OutputStream outputStream = null; //定义输出流(发送)
public void run(){
System.out.println(Thread.currentThread().getName()+": Hello");
try {
socket = new Socket("211.159.189.50", 2029);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//获取输出流
try {
outputStream = socket.getOutputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
MainActivity.java
package com.example.test2;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends Activity {
Button b1;
EditText sendET;
ConnectThread ct;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ct = new ConnectThread();
ct.start();
b1 = (Button)findViewById(R.id.button1);
sendET = (EditText)findViewById(R.id.editText1);
b1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
//发送数据
try {
ct.outputStream.write(sendET.getText().toString().getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
辅助工作4:
光有发送还不够,还要有接收
最简单的,开个新线程去接收
好,子线程接收数据没问题,但是要在子线程中将数据显示在文本框中就不那么容易了
很多人就纳闷了,数据我都接收到了,显示不就调用recvET.setText(buffer);就能显示到文本框了吗
事实并非那么容易
recvET.setText(buffer);这句话你是写在哪里的,是写在接收子线程中的;但是你要知道,在Android中,
是不允许在子线程中进行UI操作的。这点和C#类似,C#中是怎么解决的呢,使用的是Invoke方法和委托代理机制。
那么Android中又是怎样解决这个事情的呢?那就是用runOnUiThread()方法。runOnUiThread()方法的作用是将当前
线程切换到主线程,然后进行UI操作。
有了解决办法,我们就照着这个办法去做,runOnUiThread()方法是Activity类中的方法,要想使用此方法就必须
继承Activity类,那么问题又来了,Java不像C++,是不允许直接多重继承的,在C++中一个类可以直接多重继承好几个类,
如:
class x:public A,public B,...
{
void foo(){printf("X");};
}
但是在Java中是不允许的,下面的写法是错误的
class ConnectThread1 extends Thread Activity{
……
}
回到我们的Android的例程,我们想要在接收子线程中将接收到的消息显示在文本框中,那么就必须要继承两个类Thread
和Activity,但是Java中又不允许直接多重继承,这就有点麻烦了,那能解决吗?肯定能啊。
解决办法,使用内部类
既然Java不允许直接多重继承,那么我们就间接的多重继承呗,怎么间接的多重继承呢,那就是使用内部类
一睹为快,先看代码
package com.example.test2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends Activity {
Button b1;
EditText sendET;
EditText recvET;
ConnectThread ct;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recvET = (EditText)findViewById(R.id.editText2);
ct = new ConnectThread();
ct.start();
b1 = (Button)findViewById(R.id.button1);
sendET = (EditText)findViewById(R.id.editText1);
b1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
//发送数据
try {
ct.outputStream.write(sendET.getText().toString().getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
class ConnectThread extends Thread{
Socket socket = null; //定义socket
OutputStream outputStream = null; //定义输出流(发送)
InputStream inputStream=null; //定义输入流(接收)
MainActivity ma;
public void run(){
System.out.println(Thread.currentThread().getName()+": Hello");
try {
socket = new Socket("211.159.189.50", 2029);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//获取输出流
try {
outputStream = socket.getOutputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try{
while (true)
{
final byte[] buffer = new byte[1024];//创建接收缓冲区
inputStream = socket.getInputStream();
final int len = inputStream.read(buffer);//数据读出来,并且返回数据的长度
runOnUiThread(new Runnable()//不允许其他线程直接操作组件,用提供的此方法可以
{
public void run()
{
// TODO Auto-generated method stub
recvET.append(new String(buffer,0,len)+"
");
}
});
}
}
catch (IOException e) {
}
}
}
}
为了简单,我并没有再新建一个接收类,而是将接收直接写在了ConnectThread最后的死循环中。可以看出,内部类就是将之前的ConnectThread类直接剪贴到MainActivity类中,地位等同MainActivity类中成员方法和成员变量,我暂且将其称之为成员类。上述代码中,我们使用runOnUiThread()方法实现了对UI的操作,使得消息能够显示到UI。
至此,一个简单的Android TCP客户端就实现了。
下面是效果
客户端:
服务端: