android httpurlconnection 超时 重发_Android 基于UDP通信的设计

最近在搞Android,对UDP通信也有了一定的认识,希望写篇文章好好总结下,也能对这块的学习能有更清晰的认识。

何为UDP?

国际惯例,先上度娘上的解释:

UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。UDP在IP报文的协议号是17。

UDP是网络协议的一种,网络协议是通信计算机双方必须共同遵从的一组约定。如怎么样建立连接、怎么样互相识别等。只有遵守这个约定,计算机之间才能相互通信交流。它的三要素是:语法、语义、时序。

此外,说到UDP一般是会和TCP作比较来更好认识的:

  • UDP是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户机和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
  • TCP-传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。至于如何建立联系,可以自己去学习思考。

因此,UDP和TCP的区别包括:

  1. TCP面向连接(比如打电话需要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即无法保证可靠交付;
  3. UDP对系统资源要求较少,而TCP对系统资源要求相对较多;
  4. UDP支持一对一,一对多,多对一和多对多的交互通信,而每一条TCP连接只能是点到点的;
  5. UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。

基于上述特点,UDP通常在音频、视频和普通数据在传送时使用较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。比如我们聊天QQ就有使用UDP协议。

UDP通信简单示例

f9c11f0b741080e5356e48fefbf52bbe.png

相信大家都知道,进行网络通信,必须编写创建发送端和接收端,UDP亦如此。其中UDP数据包的发送,接收都要通过 一个java.net.DatagramSocket对象实现。DatagramSocket类表示发送和接送数据的套接字。我们看下该类的构造方法:

DatagramSocket

在发送前,首先要创一个DatagramPacket即数据 报对象,该对象中包含了要发送的数据,发送者的地址和目标地址,接着调用 DatagramSocket对象发送即可。发送数据报时,首要指定本地IP和端口创建发送的Socket对象。

代码如下:

public class DatagramSender {
public static void main(String args[]) throws Exception {
//①.创建要用来发送的本地地址对象
SocketAddress localAddr = new InetSocketAddress("192.168.1.147",13000);
 //②.创建发送的Socket对象
DatagramSocket dSender = new DatagramSocket(localAddr);
 int count=0;
while(true){
 // 创建要发送的数据,字节数组
count++;
//③.要发送的数据
 byte buffer[] = (count+"-hello").getBytes();
 //④.发送数据的目标地址和端口
SocketAddress destAdd = new InetSocketAddress("192.168.1.149",
14000);
 //⑤.创建要发送的数据包,指定内容,指定目标地址
DatagramPacket dp = new DatagramPacket(buffer, buffer.length,
destAdd);
 dSender.send(dp);//⑥.发送
 System.out.println("数据己发送: "+count);
 Thread.sleep(1000);
}
 }
}

最后,调用DatagramSocket发送对象UDP数据包。 调用了其 send方法,可立即将数据报发送到网络中

至于在这发送前,必然要有接收端的存在,代码如下:

public class DatagramReciver {
public static void main(String args[]) throws Exception {
//①.创建要用来发送的本地地址对象
SocketAddress localAddr = new
InetSocketAddress("192.168.1.149", 14000);
//②.接收的服务器UDP端口
 DatagramSocket recvSocket = new DatagramSocket(localAddr);
 while(true){
 //③.指定接收缓冲区大小
 byte[] buffer = new byte[20];
 //④.创建接收数据包对象,指定接收大小
 DatagramPacket packet = new DatagramPacket(buffer,
buffer.length);
 //⑤.阻塞等待数据到来,如果收到数据,存入packet中的缓冲区中
 System.out.println("UDP服务器等待接收数
据:"+recvSocket.getLocalSocketAddress());
 recvSocket.receive(packet);
//得到发送方的ip和端口
 SocketAddress address = packet.getSocketAddress();
 //转换接收到的数据为字符串
 String msg=new String(packet.getData()).trim();
 //接收到后,打印出收到的数据长度
 System.out.println("recv is:"+msg+" from:"+address);
 }
 }
}

在UDP中 的接收也会阻塞,与 TCP/IP Socket 的阻塞不同的它:它只是在读取一个数据包的时候阻塞。 recvSocket.receive(packet)这句代码会一直等待,到有数据包到达。

另外,我们还要用到DatagramPacket类,其实将数据字节填充到UDP包。用法很简单,DatagramSocket收发DatagramPacket即可。与TCP不同,UDP的socket并没有客户端和服务端的区别而统一应用此对象。同样的看下其构造方法:

DatagramPacket(byte[] buf, int length) //构造DatagramPacket,并接收长度为length的数据包
	DatagramPacket(byte[] buf, int lenght, InetAddress address int port)  //将长度为length的数据包发送到主机上指定端口号
	DatagramSocket(byte[] buf, int offset, int lenght, InetAddress address int port)  //  构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号
   DatagramPacket(byte[] buf, int offset, int length)   //构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量 
   DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)   //构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号
   DatagramPacket(byte[] buf, int length, SocketAddress address)   //构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号

UDP向底层IP数据添加少部分内容:源端口、目的端口、IP数据部分长度和可选校验和,这些总共占用8字节。端口、地址、数据长度和数据等信息都可以从DatagramPacket中提取或者向其设置。

安卓与PC间

Android端代码:

public class MainActivity extends Activity {
	DatagramSocket socket;//在收发信息的过程中只需要创建一个DatagramSocket对象即可

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		try {
			socket = new DatagramSocket(9999);
		} catch (SocketException e1) {

			e1.printStackTrace();
		}
		Button btn = (Button) this.findViewById(R.id.sendBtn);
		final EditText et = (EditText) this.findViewById(R.id.sendtext); 
		TextView tv = (TextView) this.findViewById(R.id.receivetext); // 获取文本框的内容

		// 发送信息的函数
		btn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				*new Thread(new Runnable() {
					public void run() {//在Android通信协议必须要放在线程里面进行**
						String str = et.getText().toString();
						Log.v("发送", str);
						sendMsg(str);
					}
				}).start();
			}
		});

		// 接收信息的函数
		try {
			receiveMsg();  //receiveMsg()函数也已经封装好,这里只需要拿来用即可,要体现java的封装思想**
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 定义发送消息的函数sendMsg()
	 */
	**public void sendMsg(String msg)** {
		Log.v("v", "开始发送");
		try {
			**socket = new DatagramSocket(9999); //用DatagramSocket创建一个sokcet对象发送数据包,9999是本机的端口号
			InetAddress server = InetAddress.getByName("192.168.31.207"); 
			 //获取本地主机的IP的地址,一般情况下使用InetAddress.getByName("IP")的形式,但是也可以用 getAllName()或者getLocalHost()的形式,得到本地主机的地址
			byte data[] = msg.getBytes();  //将字符串转换成字节流,因为底层的传输都是字节传输
			DatagramPacket pack = new DatagramPacket(data, data.length, server,
					6024); // 创建DatagramPacket 对象数据包,这里的6024是我们要发送信息主机的端口号
			socket.send(pack);**
			Log.v("f", "发送成功!");
		} catch (Exception e) {
			Log.v("f", "发送失败!");
			e.printStackTrace();
		}
	}

	public void receiveMsg() throws Exception** {
		// 创建线程,同理,接收信息也要放在线程里面接收
		new Thread(new Runnable() {
			public void run() {
				if (socket != null) {
					try {
						String str;
						while (true) {
							// 创建一个空的字节数组
							byte[] data = new byte[1024];
							// 将字节数组和数组的长度传进DatagramPacket 创建的对象里面
							DatagramPacket pack2 = new DatagramPacket(data,
									data.length);**
							Log.v("s", "pack2");
							Log.v("s", "开始接收");
							**socket.receive(pack2);  // socket对象接收pack包,程序启动的时候,socket会一直处于阻塞状态,直到有信息传输进来**
							String ip = pack2.getAddress().getHostAddress();  //这段代码的作用获取发送数据的IP地址
							int port = pack2.getPort();  //获取发送数据端的端口号
							System.out.println(ip);
							System.out.println(port);
							str = new String(pack2.getData(), 0, pack2.getLength()); // 将字节数组转化成字符串表现出来
							Log.v("s", "接收成功: " + str);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}).start();

	}
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

PC端代码:

public class UDPServer{ 
	private SendandRecieveMsg srm; 
	public void UDPsetFrame(){
		srm = new SendandRecieveMsg();
		Frame frame = new Frame();
		frame.setTitle("QQ");  
		frame.setSize(800, 800);  
		frame.setLayout(new FlowLayout());  //设置流式布局方式
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);  //设置可见
		TextField tf = new TextField(20);  //创建文本框, 20是指定的列数
		Button senBtn = new Button("发送"); 
		TextArea ta = new TextArea(20,30);//创建文本域
		frame.add(tf);  //添加进frame窗体里面
		frame.add(senBtn); 

		frame.add(ta);  
		ActionLis ml = new ActionLis(tf,srm);  //将文本框里面内容传进去
		senBtn.addActionListener(ml);//给发送按钮加上监听器
		//关闭窗口调用的函数
		frame.addWindowListener(new WindowAdapter(){
			public void windowClosing(WindowEvent e){
				System.exit(0);  //这个函数的意思就是把整个窗体停用下来
			}
		});
		while(true){
			try {
				**srm.UDPReceiveMsg(ta);   //接收信息的socket不断接收信息,而且必须要放在最后,不断while会阻断其他程序的进行,UDPReceiveMsg()函数已经封装进SendandRecieveMsg类中**
			} catch (SocketException e) {	
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] arrys) throws IOException{
		new UDPServer().UDPsetFrame();  //主函数,创建对象并启动
	}

public class ActionLis implements ActionListener**{
	private TextField tf;
	private SendandRecieveMsg srm;
	//构造函数
	public ActionLis() {
	}
	public ActionLis(TextField tf, SendandRecieveMsg srm) {  //构造函数将文本tf和srm传进来
		this.tf = tf;	
		this.srm = srm;
	}
	public void actionPerformed(ActionEvent e) {
		if(e.getActionCommand().equals("发送")){
			srm.UDPSendMsg(tf.getText().trim());   //发送信息个Android端
			System.out.println("发送的内容是:" + tf.getText().trim());
		}
	}
}


public class SendandRecieveMsg**{
	DatagramSocket socket;  //定义DatagramSocket类对象
	public SendandRecieveMsg(){
		try {
			**socket = new DatagramSocket(6024);  //这里6024是我们PC的程序的端口号**
		} catch (SocketException e) {
			
			e.printStackTrace();
		}
	}
	/**
	 * 定义发送信息函数
	 */
	**public void UDPSendMsg(String msg)**{
    	if(socket != null) {
    		try {
				**InetAddress server = InetAddress.getByName("192.168.31.175");
	    		byte data[] = msg.getBytes();
	    		DatagramPacket pack = new DatagramPacket(data, data.length, server, 9999); //这是对方网络软件的端口
	    		socket.send(pack);  //这段代码在Android端已经介绍过,所以在这里就不再详述**
	    		System.out.println("发送成功");
			} catch (Exception e) {
				System.out.println("发送失败");
				e.printStackTrace();
			}  
    		
    	}
	}
	
	
	/**
	 * 定义接收信息函数
	 */
	**public void UDPReceiveMsg(TextArea ta) throws SocketException**{
		String str = null;
		while(true) {
			//创建字节数组
			byte[] data = new byte[100];
			//将字节数组和数组的长度传进DatagramPacket里面
			DatagramPacket pack1 = new DatagramPacket(data, data.length);
			try {
				System.out.println("启动");
				**//socket对象接收pack包
				socket.receive(pack1);
				String ip = pack1.getAddress().getHostAddress();  //获取IP地址
				int port = pack1.getPort();//这段代码在Android端已经介绍过,所以在这里就不再详述**
				str = new String(pack1.getData(), 0, pack1.getLength());  
				str = ip + ":" + port + ":" + str;
				ta.insert(str, pos);  //将消息插到文本里面
		    	if(str.equals("bye")){
					socket.close();  //关闭接口
				}
				} catch (Exception e) {
					System.out.println("发生异常");
					e.printStackTrace();
			  }  
		}
	}
}

小插曲:

除此之外,很有幸能在28号胡风范学长听到其在创新工场DeeCamp冬令营的收获的分享。其中有趣的是,面对对他羡慕不已的我们,师兄还在说自己是菜鸟,我们更是苦不堪言呐哈哈哈。师兄建议我们一定要打好基本功,再牛的项目也是由哪些基础的东西支撑起来的。再者就是合作的重要性,就比如我昨天刚结束的美赛,即使只是三个人,我也深刻感受到协作的重要性,也许我们三个人拉出来都能独挡一面,但是一个人的思维往往难以达到全面,即使是硬本事也难真正的全面发展。除此之外,多和牛人在一起是促使你发展的很好的一个选择。

另外经过这段时间的学习,我也发现从前的我还是稍稍有点畏首畏尾了,大胆尝试的心总是没有,怕自己技术不行,这不敢做那不敢做,导致一些到了眼前的机会甚至是自己喜欢的一些东西丧失了,因此,今后,没有什么不行的!!!

怀挺!

f917da0fd9d0d0e25cb758aca509e748.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android中的HttpURLConnection是一个用于发送接收HTTP请求的类。它是Java标准库中的一部分,并且在Android开发中被广泛使用。 使用HttpURLConnection可以执行以下操作: 1. 创建连接:使用URL对象创建HttpURLConnection对象,并设置连接的URL。 2. 设置请求方法:使用setRequestMethod()方法设置请求的方法,如GET、POST等。 3. 设置请求头:使用setRequestProperty()方法设置请求头信息,如Content-Type、User-Agent等。 4. 设置请求体:对于POST请求,可以使用OutputStream将数据写入请求体。 5. 发送请求:使用connect()方法建立与服务器的连接,并发送请求。 6. 获取响应码:使用getResponseCode()方法获取服务器的响应码。 7. 获取响应数据:根据响应码,可以使用getInputStream()或getErrorStream()方法获取服务器返回的数据流。 8. 解析响应数据:根据服务器返回的数据格式,可以使用相应的解析方式进行解析,如JSON解析、XML解析等。 以下是一个简单的示例代码,演示了如何使用HttpURLConnection发送GET请求并获取响应数据: ```java try { URL url = new URL("http://www.example.com/api/data"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { InputStream inputStream = connection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; StringBuilder response = new StringBuilder(); while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); connection.disconnect(); // 处理响应数据 String responseData = response.toString(); // ... } else { // 处理错误情况 } } catch (IOException e) { e.printStackTrace(); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值