Android 一步步实现TCP客户端

开门见山

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)+"\r\n");	
						}	
					});	
				}
			}
			catch (IOException e) {
				
			}
			
		}
	}

}

为了简单,我并没有再新建一个接收类,而是将接收直接写在了ConnectThread最后的死循环中。可以看出,内部类就是将之前的ConnectThread类直接剪贴到MainActivity类中,地位等同MainActivity类中成员方法和成员变量,我暂且将其称之为成员类。上述代码中,我们使用runOnUiThread()方法实现了对UI的操作,使得消息能够显示到UI。

至此,一个简单的Android TCP客户端就实现了。


下面是效果

客户端:

服务端:

源码下载:https://download.csdn.net/download/lyndon_li/10639745

posted @ 2018-08-31 23:04  liyongjun  阅读(470)  评论(0编辑  收藏  举报