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的

时间: 2024-10-27 11:11:55

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

初识Socket通信:基于TCP和UDP协议学习网络编程

学习笔记: 1.基于TCP协议的Socket网络编程: (1)Socket类构造方法:在客户端和服务器端建立连接 Socket s = new Socket(hostName,port);以主机名和端口号作为参数来创建一个Socket对象. Socket s = new Socket(address,port);以InetAddress对象和端口号作为参数来创建一个Socket对象. 创建Socket对象时可能抛出UnknownHostException或IOException异常,必须捕获它们

Java Socket实现基于TCP和UDP多线程通信

一.通过Socket实现TCP编程 1.1 TCP编程 TCP协议是面向连接,可靠的,有序的,以字节流的方式发送数据.基于TCP协议实现网络通信的类有客户端的Socket类和服务器端的ServerSocket类. 1.2 服务器端套路 1.创建ServerSocket对象,绑定监听端口. 2.通过accept()方法监听客户端请求. 3.连接建立后,通过输入流读取客户端发送的请求信息. 4.通过输出流向客户端发送响应信息. 5.关闭响应的资源. 1.3 客户端套路 1.创建Socket对象,指明

计算机网络——网页上(或其他情况下)的视频传输是基于TCP还是UDP

计算机网络——网页上(或其他情况下)的视频传输是基于TCP还是UDP 1. 综述 链接:百度知道 当然,需要清楚,这里说基于TCP还是UDP是在传输层,应用层的协议估计种类多多. 总结找到的内容,应该说: 1. 网页上的视频是基于HTTP/HTTPS,传输层是TCP 2. QQ视频聊天等是基于UDP 3. 甚至有的应用使用p2p协议,传输层应该也是TCP 4. 通过http进行流化视频有很多种方法 5. 传输视频还有很多其他的应用层协议 一方面,在网页上看视频可以忍受缓冲5s看到更清楚的视频,所

深入浅出TCP与UDP协议

深入浅出TCP与UDP协议 网络协议是每个前端工程师的必修课,TCP/IP协议族是一系列网络协议的总和,而其中两个具有代表性的传输层协议,分别是TCP与UDP,本文将介绍这两者以及他们之间的区别. 一.TCP/IP网络结构模型 计算机与网络设备要相互通信,双方就必须基于相同的方法.比如,如何探测到通信目标.由那一边先发起通信.使用那种语言进行通信.怎样结束通信等规则都需要事先确定.不同的硬件之间的通信,所有的这一切都需要一种规则.而我们就把这种规则称之为协议(protocol). TCP/IP

网络基础:TCP协议、UDP协议、均属于传输层协议;TCP和UDP协议有何不同?

传输层 传输层的主要工作是定义端口,标识应用程序身份,并将数据包交给对应的应用程序实现端口到端口的通信,并且传输层引入了TCP/UDP协议. 1. 如果有大量数据包.数据包大?时间很长,网络中断,怎么控制重新传输?怎么确保数据包正确完整---传输层 传输层封装数据包,通过定义的 TCP.UDP 协议实现按序一个一个发送,保证数据完整正确性: 2. QQ发消息,你必须使用QQ接受消息,才可以正常通信:但是电脑中不是只运行了QQ,还有其他程序,怎么确定由谁来处理消息 传输层定义端口的概念-- HTT

TCP与UDP协议的区别

首先TCP和UDP协议都是运行在运输层的协议. UDP协议:用户数据包协议 1.UDP协议是无连接的.也就说在数据发送之前并不需要建立连接(当然,在发送数据结束的时候也就不存在链接的释放),因此减少了开销和数据发送之前的时延. 2.UDP使用尽最大努力的交付,但是不保证可靠性的交付,因此主机不需要维持复杂的链接状态表. 3.UDP是面向报文.发送方的UDP对于应用程序进程交下来的报文,即不合并,也不拆分,而是保留这些报文的边界.这也就是说,应用层交付给UDP多长的报文,UDP就照样发送,即一次发

UNP(一):网络编程角度下的TCP、UDP协议

此博文是学习UNP(UNIX Network Programming)后的读书笔记,供以后自己翻阅回顾知识. TCP.UDP概述 在前面<计算机网络与TCP/IP>栏目下已经介绍过一些关于TCP.UDP的相关知识TCP/IP(三):传输层TCP与UDP,这里只是简单从UNIX网络编程的角度介绍TCP.UDP协议. 我们都知道UDP 缺乏可靠性.无连接的,面向数据报 的协议,如果想确保数据报到达目的地,必须自己在应用层实现一些特性:对端的确定.本端的超时和重传等.UDP面向报文的特性,使得UDP

运输层协议--TCP及UDP协议

TCP及UDP协议 按照网络的五层分级结构来看,TCP及UDP位于运输层,故TCP及UDP是运输层协议.TCP协议--传输控制协议UDP协议--用户数据报协议 多路复用及多路分解 图多路复用及多路分解 接受主机中的运输层实际上并没有直接将数据交给进程,而是通过一个中间的套接字来传递.由于在任何一个时刻接受主机上可能有多个套接字,所以每个套接字都已一个唯一的标识符. 主机如何将一个收到的运输层报文段定向到合适的套接字? 为达到这一目的,在每个运输层报文段中设置了几个字段,在接收端,运输层检查并标识

TCP与UDP协议分析

1 案例1:TCP与UDP协议分析1.1 问题1.通过抓包分析TCP与UDP的封装格式2.通过抓包分析TCP三次握手1.2 方案1.实验环境由两台主机PC1和PC2组成,PC1使用宿主机,PC2使用VMWare虚拟机,确保两台主机通信正常(需要关闭PC2的防火墙)2.在PC1上运行科来进行抓包3.在PC1上通过远程桌面访问PC2,然后在PC1上分析数据包,如图-1所示.1.3 步骤实现此案例需要按照如下步骤进行. 1.第一次握手,如图-2所示. 2.第二次握手,如图-3所示.3.第三次握手,如图