【随笔】通信学习相关

最近学习了一些通信相关的知识,遂来写一写博客总结一下。

电脑中的数据在存储时总是以字节byte(二进制的0/1)存储的,同样的,通过互联网相连的计算机在进行信息的发送与接收时,也总是以字节的形式将相关文件或信息打包处理。简而言之就是,通过互联网进行通信的信息总是字节类型的。

就像数据存储的方式一样(连续存与非连续存),通信也有两种典型的方法(或者我们称之为协议)与之很相像,可以称之为面向连接的通信以及面向非连接的通信。

在阐述连接与非连接之前,先稍微了解一下协议是什么。

稍微上网查找一下就能翻到不少对于协议的定义。
百科上称之为:通信计算机双方必须共同遵从的一组约定。或者说详细点:在计算机网络中用于规定信息的格式以及如何发送和接收信息的一套规则称为网络协议或通信协议。

简单理解一下就是,由于网络传输基于字节类型,当我们想要知道对方传输过来的信息是什么类型(图片还是文字等)、具体哪些部分才是文件的字节、有一些文件很大导致传输可能需要拆成几次发送等、甚至是有无加密等等就很麻烦,这时候就需要两边协商好发送信息的格式,根据协议来编码,通过代码实现将字节按照通信协议转成相应信息,这样在处理信息时才能做到准确。而协商的具体内容就可以说是协议的一部分了。

回到之前提到的两类典型通信协议:面向连接的通信与面向非连接的通信。
其中前者的典例为TCP,后者则是UDP。
这时候就很容易能够理解这两类的特性了。面向连接的通信,顾名思义就是发送端与接收端之间会建立一个通道,相对的,面向非连接的通信,便不会建立这个通道。显然,有通道保障的面向连接的通信,可以保证数据传输的可靠性,也就是说不会丢数据(类似打电话),但是效率低(要等),与之相对的面向非连接的通信,虽然并不会保证传输的可靠性,也就是说存在丢包(数据)的问题(类似发短信),但是效率高(发了就丢,不管是否接收到,不需要等)。于是在使用时,往往根据使用环境的特性决定使用哪种类型的通信协议:对时间敏感时适用TCP,对时间不敏感时适用UDP。

如此,在代码中实现同一WiFi下的两台计算机进行信息传输,需要互相的ip地址以及端口。其中ip可以通过cmd指令(ipconfig)查到,端口需要事先协商好。

代码实现也比较简单,发送信息模型就是:发送地址+发送端+接收地址+接收端;接收信息模型就是:接收地址+接收端。(注意端口不可被同时多次占用)
用java代码实现UDP如下:

public class UDP {
		private SocketAddress localAddr;//接收地址
		private SocketAddress localAdd;//接收地址,两个接收地址是因为端口不一致
		private SocketAddress destAdd;//发送地址
		private DatagramSocket recvSocket;//接收端
		private DatagramSocket dSender;//发送端
		public static UDP udp;
		
		public static UDP setUDP(){
			if(udp == null){
				udp = new UDP();
			}
			return udp;
		}
		
		private UDP(){
			try {
				localAddr = new InetSocketAddress("192.168.31.133", 14000);
				localAdd = new InetSocketAddress("192.168.31.133", 13000);
				destAdd = new InetSocketAddress("192.168.31.204", 14000);
				recvSocket = new DatagramSocket(localAddr);
				dSender = new DatagramSocket(localAdd);
			} catch (Exception e) {
				System.out.println("绑定地址失败");
				e.printStackTrace();
			}
		}
		public void receive(){
			try {		
				while(true){
					byte[] buffer = new byte [recvSocket.getReceiveBufferSize()];
					DatagramPacket packet = new DatagramPacket(buffer, buffer.length); 
					System.out.println("等待接收:");
					recvSocket.receive(packet);
					System.out.println("接收完毕");
					SocketAddress address = packet.getSocketAddress();
					String msg = new String(packet.getData()).trim();
					System.out.println("接收的信息:"+msg+" 发送方:"+address);
				}
			} catch (IOException e) {
				System.out.println("接收失败");
				e.printStackTrace();
			}
		}
		//处理文件
		public void handleRecvData(byte[] bytes){
			//此处按不同的需求处理,不过基本遵循此规律
			byte[] buffer = new byte[bytes.length - 1];//不同协议会有所不同
			System.arraycopy(bytes, 1, buffer, 0, buffer.length);
			String msg = new String(buffer);
			ButtonListener.textHandle(msg);//此处传给相应处理方式
		}

		public void send(byte[] bytes) {
			DatagramPacket packet = new DatagramPacket(bytes, bytes.length, destAdd);
			try {
				dSender.setSendBufferSize(bytes.length);
				dSender.send(packet);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
}

需注意,在代码实现过程中不是只有单实例化这一个解决端口被重复占用问题的方法。
此外,UDP在接收时会由于在收到消息前一直被阻塞的特性导致程序运行不下去,此处解决方法是考虑采用future来停止线程等。
再然后便是丢包的问题。前面提到UDP类型协议容易造成丢包问题,这里的解决方案在解决完阻塞问题之后便能够迎刃而解了,不过也需要事先协商好怎么个确认法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值