Android Socket编程android端服务器和客户端的实现

       Android Socket编程android端服务器和客户端的实现

      其实和java实现的差不多,或本质是用java实现的,但由于android有自身的独特性,所以还是有一些要注意的点:


      我这个Demo是以服务器开启,然后客户端连上服务器后就可与服务器进行交互,客户端每向服务器发送一条信息,服务器就向客户端返回相应的信息,两端都在android端实现(网上有很多客户端用android实现的,没找到服务器也在android端实现的,另外网上的服务器基本都有开始没有关闭,我这里也添加了关闭,其中涉及多线程,希望能给大家一点启发,当然不足之处还望告知,我也是初学的学生),其中客户端基本借鉴《疯狂Android讲义》。

这个Demo的功能简单但扩展性很大,以这个为核心可以扩展出很多应用或功能(例如即时聊天,微型QQ等);


先上图:

服务器:



客户端:



服务器端代码(有注释不说了):

package com.example.multithreadserver;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;

/*
 * @Author mowen
 * @Time 2013/6/9
 */

public class MainActivity extends Activity {
	
	private static final int PORT = 9999; 
    private List<Socket> mList = new ArrayList<Socket>();
    private volatile ServerSocket server=null;
    private ExecutorService mExecutorService = null; //线程池
    private String hostip;//本机IP
    private TextView mText1;
    private TextView mText2;
    private Button mBut1=null;   
    private Handler myHandler=null;
	private volatile boolean flag= true;//线程标志位
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.main);
		hostip = getLocalIpAddress();  //获取本机IP
		mText1=(TextView) findViewById(R.id.textView1);
		mText1.setText(hostip);
		mText1.setEnabled(false);
		
		mText2=(TextView) findViewById(R.id.textView2);
		
		mBut1=(Button) findViewById(R.id.but1);
		mBut1.setOnClickListener(new Button1ClickListener());
		//取得非UI线程传来的msg,以改变界面
		myHandler =new Handler(){
			@SuppressLint("HandlerLeak")
			public void handleMessage(Message msg){
			if(msg.what==0x1234){
				mText2.append("\n" + msg.obj.toString());
			}	
			}
		};
		
	}
    //对button1的监听事件
	private final class Button1ClickListener implements View.OnClickListener{
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			//如果是“启动”,证明服务器是关闭状态,可以开启服务器
			if(mBut1.getText().toString().equals("启动")){
				System.out.println("flag:"+flag);
				ServerThread serverThread=new ServerThread();
				flag=true;
				serverThread.start();
				mBut1.setText("关闭");
			}else{
				try {
					flag=false;
					server.close();
					for(int p=0;p<mList.size();p++){
					Socket s=mList.get(p);
					s.close();
					}
					mExecutorService.shutdownNow();
					mBut1.setText("启动");
					System.out.println("服务器已关闭");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
				
		}
	}
	
	//Server端的主线程
	class ServerThread extends Thread {
		
		public void stopServer(){
			try {              
				if(server!=null){                 
					server.close();
					System.out.println("close task successed");  
				}
			} catch (IOException e) {             
				System.out.println("close task failded");        
				}
		}
    public void run() {
        	
	            try {
					server = new ServerSocket(PORT);
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					System.out.println("S2: Error");
					e1.printStackTrace();
				}
	            mExecutorService = Executors.newCachedThreadPool();  //创建一个线程池
	            System.out.println("服务器已启动...");
	            Socket client = null;
	            while(flag) {
	            	try {
	            		System.out.println("S3: Error");
	                client = server.accept(); 
	                System.out.println("S4: Error");
	                //把客户端放入客户端集合中
	                mList.add(client);
	                mExecutorService.execute(new Service(client)); //启动一个新的线程来处理连接
	            	 }catch ( IOException e) {
	    	        	 System.out.println("S1: Error");
	    	            e.printStackTrace();
	    	        }
	            }
	       
        	
        }
    }    
    //获取IPv6的IP地址
	/*public String getLocalIpAddress() {  
        try {  
            for (Enumeration<NetworkInterface> en = NetworkInterface  
                    .getNetworkInterfaces(); en.hasMoreElements();) {  
                NetworkInterface intf = en.nextElement();  
                for (Enumeration<InetAddress> enumIpAddr = intf  
                        .getInetAddresses(); enumIpAddr.hasMoreElements();) {  
                    InetAddress inetAddress = enumIpAddr.nextElement();  
                    if (!inetAddress.isLoopbackAddress()) {  
                        return inetAddress.getHostAddress().toString();  
                    }  
                }  
            }  
        } catch (SocketException ex) {  
            Log.e("WifiPreference IpAddress", ex.toString());  
        }  
        return null;  
    }  */
	//获取本地IP
    public static String getLocalIpAddress() {  
           try {  
               for (Enumeration<NetworkInterface> en = NetworkInterface  
                               .getNetworkInterfaces(); en.hasMoreElements();) {  
                           NetworkInterface intf = en.nextElement();  
                          for (Enumeration<InetAddress> enumIpAddr = intf  
                                   .getInetAddresses(); enumIpAddr.hasMoreElements();) {  
                               InetAddress inetAddress = enumIpAddr.nextElement();  
                               if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress()) {  
                               return inetAddress.getHostAddress().toString();  
                               }  
                          }  
                       }  
                   } catch (SocketException ex) {  
                       Log.e("WifiPreference IpAddress", ex.toString());  
                   }  
                return null; 
    }
    //处理与client对话的线程
	class Service implements Runnable {
		 private volatile boolean kk=true;
         private Socket socket;
         private BufferedReader in = null;
         private String msg = "";
         
         public Service(Socket socket) {
             this.socket = socket;
             try {
                 in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                 msg="OK";
                 this.sendmsg(msg);
             } catch (IOException e) {
                 e.printStackTrace();
             }
             
         }

         public void run() {
        	 
                 while(kk) {
                	 try {
						if((msg = in.readLine())!= null) {
						     //当客户端发送的信息为:exit时,关闭连接
							if(msg.equals("exit")) {
						         mList.remove(socket);
						         //in.close();
						         //socket.close();
						         break;
						         //接收客户端发过来的信息msg,然后发送给客户端。
						     } else {
						    	 Message msgLocal = new Message();
						   	     msgLocal.what = 0x1234;
								 msgLocal.obj =msg+" (客户端发送)" ;
								 System.out.println(msgLocal.obj.toString());
								 System.out.println(msg);
								 myHandler.sendMessage(msgLocal);
						         msg = socket.getInetAddress() + ":" + msg+"(服务器发送)";
						         this.sendmsg(msg);
						         	}
						                
						         }
                 } catch (IOException e) {
						System.out.println("close");
						kk=false;
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
                   
                 }
					   
           
         }
         //向客户端发送信息
         public void sendmsg(String msg) {
             System.out.println(msg);
             PrintWriter pout = null;
             try {
                 pout = new PrintWriter(new BufferedWriter(
                         new OutputStreamWriter(socket.getOutputStream())),true);
                 pout.println(msg);
             }catch (IOException e) {
                 e.printStackTrace();
             }
         }
	 }
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/but1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/textView1"
        android:layout_alignParentRight="true"
        android:layout_marginRight="60dp"
        android:text="启动" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_below="@+id/textView1"
        android:layout_marginTop="44dp"
        android:text="内容" />

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="15dp"
        android:layout_marginTop="48dp"
        android:text="IP" />

</RelativeLayout>





客户端主要借鉴《疯狂Android讲义》:

UI端文件

package org.crazyit.net;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

/**
 */
public class MultiThreadClient extends Activity
{
	// 定义界面上的两个文本框
	EditText input;
	TextView show;
	// 定义界面上的一个按钮
	Button send;
	Handler handler;
	// 定义与服务器通信的子线程
	ClientThread clientThread;
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		input = (EditText) findViewById(R.id.input);
		send = (Button) findViewById(R.id.send);
		show = (TextView) findViewById(R.id.show);
		handler = new Handler() //①
		{
			@Override
			public void handleMessage(Message msg)
			{
				// 如果消息来自于子线程
				if (msg.what == 0x123)
				{
					// 将读取的内容追加显示在文本框中
					show.append("\n" + msg.obj.toString());
				}
			}
		};
		clientThread = new ClientThread(handler);
		// 客户端启动ClientThread线程创建网络连接、读取来自服务器的数据
		new Thread(clientThread).start(); //①
		send.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View v)
			{
				try
				{
					// 当用户按下发送按钮后,将用户输入的数据封装成Message,
					// 然后发送给子线程的Handler
					Message msg = new Message();
					msg.what = 0x345;
					msg.obj = input.getText().toString();
					clientThread.revHandler.sendMessage(msg);
					// 清空input文本框
					input.setText("");
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
			}
		});
	}
}
功能线程类:

/**
 *
 */
package org.crazyit.net;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

/**
 */
public class ClientThread implements Runnable
{
	private Socket s;
	// 定义向UI线程发送消息的Handler对象
	private Handler handler;
	// 定义接收UI线程的消息的Handler对象
	public Handler revHandler;
	// 该线程所处理的Socket所对应的输入流
	BufferedReader br = null;
	OutputStream os = null;

	public ClientThread(Handler handler)
	{
		this.handler = handler;
	}

	public void run()
	{
		try
		{
			System.out.println("T1");
			s = new Socket("10.0.2.15", 9999);
			System.out.println("T2");
			br = new BufferedReader(new InputStreamReader(
				s.getInputStream()));
			os = s.getOutputStream();
			// 启动一条子线程来读取服务器响应的数据
			new Thread()
			{
				@Override
				public void run()
				{
					String content = null;
					// 不断读取Socket输入流中的内容。
					try
					{
						while ((content = br.readLine()) != null)
						{
							// 每当读到来自服务器的数据之后,发送消息通知程序界面显示该数据
							Message msg = new Message();
							msg.what = 0x123;
							msg.obj = content;
							handler.sendMessage(msg);
							System.out.println(msg.obj.toString());
						}
					}
					catch (IOException e)
					{
						e.printStackTrace();
					}
				}
			}.start();
			// 为当前线程初始化Looper
			Looper.prepare();
			// 创建revHandler对象
			revHandler = new Handler()
			{
				@Override
				public void handleMessage(Message msg)
				{
					// 接收到UI线程中用户输入的数据
					if (msg.what == 0x345)
					{
						// 将用户在文本框内输入的内容写入网络
						try
						{
							System.out.println("HHHHHHH"+msg.obj.toString());
							os.write((msg.obj.toString() + "\r\n")
								.getBytes("utf-8"));
						}
						catch (Exception e)
						{
							e.printStackTrace();
						}
					}
				}
			};
			// 启动Looper
			Looper.loop();
		}
		catch (SocketTimeoutException e1)
		{
			System.out.println("网络连接超时!!");
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

布局文件很简单:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	>
<LinearLayout 
	android:orientation="horizontal"
	android:layout_width="match_parent"
	android:layout_height="wrap_content"
	>
<!-- 定义一个文本框,它用于接受用户的输入 -->
<EditText
	android:id="@+id/input"  
	android:layout_width="240dp" 
	android:layout_height="wrap_content" 
	/>
<Button
	android:id="@+id/send"  
	android:layout_width="fill_parent" 
	android:layout_height="wrap_content" 
	android:paddingLeft="8px"
	android:text="@string/send"
	/>
</LinearLayout>
<TextView
	android:id="@+id/show" 
	android:layout_width="match_parent" 
	android:layout_height="match_parent" 
	android:gravity="top"
	android:background="#ffff"
	android:textSize="14dp"
	android:textColor="#f000"
	/>
</LinearLayout>

声明:真机上只需把客户端IP改成真机IP,即服务器上显示的IP即可通信

          虚拟机上同一个虚拟机可通信

          不同虚拟机由于Android虚拟机设计的原因,需要端口重定向等操作才能实现通信

附带两个工程的源码:http://download.csdn.net/detail/mowen1111/5553829


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值