首页 > 代码库 > android实现基于TCP和UDP协议的即时通讯,含android端和服务器端

android实现基于TCP和UDP协议的即时通讯,含android端和服务器端


这几天学习了下在android中实现即时通讯的方法,一开始,自然是从基本的网络协议中开始尝试了,这样能够最大化的私人订制自己的应用,还能学习到更多的知识,好处多多,接下来就简单介绍下两种协议的不同点吧

TCP协议:提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复用。通过面向连接、端到端和可靠的数据包发送。就如给悬崖上的两人通信时,他必须先把桥建好,确认桥是没问题的情况下,才把信件交过去,以后大家每次通信时,都确认下桥没什么问题,再通过这座桥来回通信了。

UDP协议:不为IP提供可靠性、流控或差错恢复功能,在正式通信前不必与对方先建立连接,不管对方状态就直接发送。这个就是飞鸽传书了~

虽然UDP可靠性不如TCP协议,但是通信效率高于TCP。在网速极差的情况下优先考虑UDP协议,网速好的话TCP还是很方便使用的。

在Java中使用TCP可以通过java.net.Socket;这个类

<span style="font-family:Microsoft YaHei;font-size:18px;">建立连接</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">//实例化一个Socket对象
socket = new Socket();
//与对应的ip、端口进行连接,先要把桥建好
socket.connect(new InetSocketAddress(ip, port), 3000);</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">发送信息</span>
</pre><pre name="code" class="java"><span style="font-family:Microsoft YaHei;font-size:18px;">InputStream ois = socket.getInputStream();
DataInputStream dis = new DataInputStream(new BufferedInputStream(ois));
//读取服务器发过来的信息,如果没信息将会阻塞线程
msg =  dis.readUTF();</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">发送信息</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">//获得输出流
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
//发送数据
dos.writeUTF(msg);</span>

接下来上源码,为三个Thread的子类,分别对应上面三个

<span style="font-family:Microsoft YaHei;font-size:18px;">public class SocketThread extends Thread{

	private Socket socket;
	private Client client;
	private String ip;
	private int port;
	private boolean isStart=false;
	private MessageListener mMessageListener;
	
	/**
	 * 
	 * 使用TCP协议,连接访问
	 * @param ip 目标机器的IP
	 * @param port 端口
	 * @param mMessageListener 收到服务器端数据时将回调该接口内的
	 *  public void Message(String msg)方法
	 */
	public SocketThread(String ip, int port,
			MessageListener mMessageListener) {
		this.ip = ip;
		this.port = port;
		this.mMessageListener = mMessageListener;
	}

	public void run() {
		try {
			//实例化一个Socket对象
			socket = new Socket();
			//与对应的ip、端口进行连接,先要把桥建好
			socket.connect(new InetSocketAddress(ip, port), 3000);
			if (socket.isConnected()) {
				System.out.println("Connected..");
				client = new Client(socket,mMessageListener);
				//打开对应的输入/输出流监听
				client.start();
				isStart=true;
			}
		} catch (IOException e) {
			e.printStackTrace();
			isStart=false;
		}
	}

	// 直接通过client得到读线程
	public ClientInputThread getClientInputThread() {
		return client.getIn();
	}

	// 直接通过client得到写线程
	public ClientOutputThread getClientOutputThread() {
		return client.getOut();
	}

	//返回Socket状态
	public boolean isStart(){
		return isStart;
	}
	
	// 直接通过client停止读写消息
	public void setIsStart(boolean isStart) {
		this.isStart = isStart;
		client.getIn().setStart(isStart);
		client.getOut().setStart(isStart);
	}
	
	//发送消息
	public void sendMsg(String msg){
		client.getOut().sendMsg(msg);
	}
	
	public class Client {

		private ClientInputThread in;
		private ClientOutputThread out;

		public Client(Socket socket,MessageListener mMessageListener) {
			//用这个监听输入流线程来接收信息
			in = new ClientInputThread(socket);
			in.setMessageListener(mMessageListener);
			//以后就用这个监听输出流的线程来发送信息了
			out = new ClientOutputThread(socket);
		}

		public void start() {
			in.setStart(true);
			out.setStart(true);
			in.start();
			out.start();
		}

		// 得到读消息线程
		public ClientInputThread getIn() {
			return in;
		}

		// 得到写消息线程
		public ClientOutputThread getOut() {
			return out;
		}
	}
}</span>

<span style="font-family:Microsoft YaHei;font-size:18px;">public class ClientInputThread extends Thread {
	private Socket socket;
	private String msg;
	private boolean isStart = true;
	private InputStream ois;
	private DataInputStream dis;
	private MessageListener messageListener;// 消息监听接口对象

	public ClientInputThread(Socket socket) {
		this.socket = socket;
		try {
			ois = socket.getInputStream();
			dis = new DataInputStream(new BufferedInputStream(ois));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 提供给外部的消息监听方法
	 * 
	 * @param messageListener
	 *            消息监听接口对象
	 */
	public void setMessageListener(MessageListener messageListener) {
		this.messageListener = messageListener;
	}

	public void setStart(boolean isStart) {
		this.isStart = isStart;
	}

	@Override
	public void run() {
		try {
			while (isStart) {
				//读取信息,如果没信息将会阻塞线程
				msg =  dis.readUTF();
				// 每收到一条消息,就调用接口的方法Message(String msg)
				Log.v("收到消息", msg);
				messageListener.Message(msg);
			}
			ois.close();
			if (socket != null)
				socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	BufferedReader reader=null;
	public String getInputStreamString() {
		/*
		 * To convert the InputStream to String we use the
		 * BufferedReader.readLine() method. We iterate until the BufferedReader
		 * return null which means there's no more data to read. Each line will
		 * appended to a StringBuilder and returned as String.
		 */
		if (ois != null) {
			reader = new BufferedReader(new InputStreamReader(ois));
		}
		StringBuilder sb = new StringBuilder();

		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} 

		return sb.toString();
	}
}</span>

<span style="font-family:Microsoft YaHei;font-size:18px;">public class ClientOutputThread extends Thread {
	private Socket socket;
	private DataOutputStream dos;
	private boolean isStart = true;
	private String msg;

	public ClientOutputThread(Socket socket) {
		this.socket = socket;
		try {
			dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void setStart(boolean isStart) {
		this.isStart = isStart;
	}

	// 这里处理跟服务器是一样的
	public void sendMsg(String msg) {
		this.msg = msg;
		synchronized (this) {
			notifyAll();
		}
	}

	@Override
	public void run() {
		try {
			while (isStart) {
				if (msg != null) {
					dos.writeUTF(msg);
					dos.flush();
					msg=null;
					synchronized (this) {
						wait();// 发送完消息后,线程进入等待状态
					}
				}
			}
			dos.close();// 循环结束后,关闭输出流和socket
			if (socket != null)
				socket.close();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">//定义接收到消息时的,处理消息的接口
public interface MessageListener {
	public void Message(String msg);
}</span>

主界面,感觉很丑,将就吧

<span style="font-family:Microsoft YaHei;font-size:18px;"><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="com.example.chatclient.MainActivity" >


    <ScrollView
        android:id="@+id/svMessage"
        android:layout_width="fill_parent"
        android:layout_height="100dp" >

        <TextView
            android:id="@+id/tvMessage"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="消息内容:\n" />
    </ScrollView>

    <EditText
        android:id="@+id/etMessage"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/svMessage" />

    <TextView
        android:id="@+id/tvSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/svMessage"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/etMessage"
        android:text="发送消息"
        android:textSize="25sp" />
"

</RelativeLayout></span>

MainActivity代码

<span style="font-family:Microsoft YaHei;font-size:18px;">public class MainActivity extends Activity {

	EditText etMessage;
	TextView tvSend, tvMessage;
	SocketThread client;
	MyHandler myHandler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		setup();
	}

	public void setup() {
		etMessage = (EditText) findViewById(R.id.etMessage);
		tvSend = (TextView) findViewById(R.id.tvSend);
		tvMessage = (TextView) findViewById(R.id.tvMessage);
		tvSend.setOnClickListener(onClick);
		myHandler = new MyHandler();

		//初始化
		client = new SocketThread("10.21.56.226", 8888,new MessageListener() {

					//收到消息后调用此方法
					@Override
					public void Message(String msg) {
						// TODO Auto-generated method stub
						// tvMessage.append(msg);
						Bundle bundle = new Bundle();
						bundle.putString("input", msg);
						Message isMessage = new Message();
						isMessage.setData(bundle);
						//使用handler转发
						myHandler.sendMessage(isMessage);
					}
				});
		//正式启动线程
		client.start();

	}

	OnClickListener onClick = new OnClickListener() {
		public void onClick(android.view.View v) {
			String message = etMessage.getText().toString();
			Log.v("发送消息", message);
			if (client.isStart()) {
				client.sendMsg(message);
			}
		};
	};

	private class MyHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			Log.v("处理收到的消息", "  ");
			tvMessage.append(msg.getData().getString("input"));
		}
	}
}</span>


服务器端的代码,主要是使用ServerSocket监听一个端口,来与客户端链接和收发信息

<span style="font-family:Microsoft YaHei;font-size:18px;">public class ChatServer {
    boolean started = false;
    ServerSocket ss = null;

    List<Client> clients = new ArrayList<Client>();

    public static void main(String[] args) {
	new ChatServer().start();
    }

    public void start() {
	try {
	    //ServerSocket监听8888端口
	    ss = new ServerSocket(8888);
	    started = true;
	} catch (BindException e) {
	    System.out.println("start....");
	    System.out.println("有问题");
	    e.printStackTrace();
	    System.exit(0);
	} catch (IOException e) {
	    e.printStackTrace();
	}

	try {

	    while (started) {
		Socket s = ss.accept();
		Client c = new Client(s);
		System.out.println("a client connected!");
		new Thread(c).start();
		clients.add(c);
		// dis.close();
	    }
	} catch (IOException e) {
	    e.printStackTrace();
	} finally {
	    try {
		ss.close();
	    } catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	    }
	}
    }

    class Client implements Runnable {
	private Socket s;
	private DataInputStream dis = null;
	private DataOutputStream dos = null;
	private boolean bConnected = false;

	public Client(Socket s) {
	    this.s = s;
	    try {
		dis = new DataInputStream(s.getInputStream());
		dos = new DataOutputStream(s.getOutputStream());
		bConnected = true;
	    } catch (IOException e) {
		e.printStackTrace();
	    }
	}

	public void send(String str) {
	    try {
		dos.writeUTF(str);
	    } catch (IOException e) {
		clients.remove(this);
		System.out.println("关闭一个连接");
		// e.printStackTrace();
	    }
	}

	public void run() {
	    try {
		while (bConnected) {
		    String str = dis.readUTF();
		    System.out.println(str);
		    for (int i = 0; i < clients.size(); i++) {
			Client c = clients.get(i);
			c.send(str);
			// System.out.println(" a string send !");
		    }
		    /*
		     * for(Iterator<Client> it = clients.iterator();
		     * it.hasNext(); ) { Client c = it.next(); c.send(str); }
		     */
		    /*
		     * Iterator<Client> it = clients.iterator();
		     * while(it.hasNext()) { Client c = it.next(); c.send(str);
		     * }
		     */
		}
	    } catch (EOFException e) {
		System.out.println("Client closed!");
	    } catch (IOException e) {
		e.printStackTrace();
	    } finally {
		try {
		    System.out.println("close All !");
		    if (dis != null)
			dis.close();
		    if (dos != null)
			dos.close();
		    if (s != null) {
			s.close();
			// s = null;
		    }

		} catch (IOException e1) {
		    e1.printStackTrace();
		}

	    }
	}

    }
}</span>
接下来先运行服务器代码,再运行手机端就可以了,多台手机可以互相发送信息了。
晚点再写UDP的





android实现基于TCP和UDP协议的即时通讯,含android端和服务器端