zoukankan      html  css  js  c++  java
  • 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)+"
    ");	
    						}	
    					});	
    				}
    			}
    			catch (IOException e) {
    				
    			}
    			
    		}
    	}
    
    }
    

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

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


    下面是效果

    客户端:

    服务端:

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

  • 相关阅读:
    sql中保留2位小数
    C# 操作字符串,在某些特定的字符后面或前面添加其它字符
    Windows Server 2008 R2中上传和下载文件
    winform中显示标题,点击打开链接
    正则表达式
    winform重绘
    js获取元素的页面坐标
    剑指offer-从上往下打印二叉树
    剑指offer-栈的压入、弹出序列
    剑指offer-包含min函数的栈
  • 原文地址:https://www.cnblogs.com/liyongjun/p/9657581.html
Copyright © 2011-2022 走看看