判断
MQTT与TCP的区别
//TCP是OSI第四层的传输层协议
//核心思想是分组交换
//传输单位是packet
//MQTT是基于TCP的七层应用层协议。
//MQTT的核心思想是简单并适应物联网环境。
//MQTT的传输单位是消息
诞生时间
TCP协议诞生于1974年冷战期间。
MQTT诞生于1999年互联网初期,TCP协议比MQTT协议诞生早了25年。
Ashton提出IoT概念也是在1999年,因此MQTT协议生逢其时。当时MIT Auto-ID Labs的Kevin
Ashton为了把宝洁的供应链上的RFID标签和互联网连接起来,在1999年第一个提出了IoT这个概念。
协议位置
TCP是OSI第四层的传输层协议。
MQTT是基于TCP的七层应用层协议。
协议定位
TCP设计考虑的是面向连接的、可靠的、基于字节流的传输层通信协议。
MQTT则是在低带宽高延迟不可靠的网络下进行数据相对可靠传输的应用层协议。
设计思想
TCP的核心思想是分组交换。
MQTT的核心思想是简单并适应物联网环境。
传输单位
TCP的传输单位是packet,当应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,TCP则把数据流分割成适当长度的报文段,最大传输段大小(MSS)通常受该计算机连接的网络的数据链路层的最大传送单元(MTU)限制。
MQTT的传输单位是消息,每条消息字节上限在MQTT Broker代理服务器上进行设置,可以设置超过1M大小的消息上限。这样,就可以用一条消息就发送上千条采集的数据,或者比较大的设备阴影文件。
技术挑战
TCP需要解决的问题是在IP包传输过程中,处理异构网络环境下的网络拥塞、丢包、乱序、重复包等多种问题。
MQTT解决的问题是,在低带宽高延迟不可靠的网络下和资源有限的硬件环境内,进行相对可靠的数据传输。
服务质量
TCP是一个可靠的流传输服务,通过ACK确认和重传机制,能够保证发送的所有字节在接收时是完全一样的,并且字节顺序也是正确的。
MQTT提供三种可选的消息发布的QoS服务等级。MQTT客户端和MQTT代理服务器通过session机制保证消息的传输可靠性。开发人员可以根据业务需要选择其中一种。
应用案例
TCP用于许多互联网应用程序,如WWW、email、FTP、SSH、P2P、流媒体。MQTT也是基于TCP的。
MQTT可以用于物联网数据传输、IM聊天软件等。
常见问答题
1.MQTT三种消息发布服务质量(QoS)分别是什么?有什么特点?
级别0:尽力而为。
消息发送者会想尽办法发送消息,但是遇到意外并不会重试。
级别1:至少一次。
消息接收者如果没有知会或者知会本身丢失,消息发送者会再次发送以保证消息接收者至少会收到一次,当然可能造成重复消息。
级别2:恰好一次。
保证这种语义肯待会减少并发或者增加延时,不过丢失或者重复消息是不可接受的时候,级别2是最合适的。
2.WebSocket与MQTT有什么区别?
MQTT协议
为大量计算能力有限,且工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议,它具有以下主要的几项特性:
1. 使用**发布/订阅消息模式**,提供**一对多**的消息发布,解除应用程序耦合;
2. 对负载内容屏蔽的消息传输;
3. 使用 TCP/IP 提供网络连接;
4. 有三种消息发布服务质量:
WebSocket协议
基于TCP的一种应用层网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。
取代了网页和服务器采用HTTP轮询进行双向通讯的机制。
3.如何理解B/S架构与C/S架构,分别有什么特点?
B/S
即浏览器/服务器结构。
Browser指的是Web浏览器,极少数事务逻辑在前端实现,但主要事务逻辑在服务器端实现。
B/S架构的系统无须特别安装,只有Web浏览器即可
三层分别为:
第一层表现层:主要完成用户和后台的交互及最终查询结果的输出功能。
第二层逻辑层:主要是利用服务器完成客户端的应用逻辑功能。
第三层数据层:主要是接受客户端请求后独立进行各种运算。
C/S
也叫?客户机/服务器模式。
架构软件有一个特点,就是如果用户要使用的话,需要下载一个客户端,安装后就可以使用
它可以分为客户机和服务器两层:
第一层:? 在客户机系统上结合了界面显示与业务逻辑;
第二层:? 通过网络结合了数据库服务器。
4.云计算有哪些特点?
1、超大规模
2、虚拟化
3、高可靠性
4、云计算特点之通用性
5、高可扩展性
6、云计算的特点之按需服务
7、极其廉价
8、云计算具有潜在的危险性特点
5.TCP/IP协议与Http协议的区别?
TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据
TCP和UDP是高速公路上的“卡车”,它们携带的货物就是像HTTP
编程题常见问题
1.设计云端服务器使用springboot框架利用http协议利用restful风格实现接收发送json格式数据,并返回数据到客户端。请写出controller层实现数据接收的代码
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
/**
* 将"/hello"路径注册为STOMP端点,这个路径与发送和接收消息的目的路径有所不同,这是一个端点,客户端在订阅或发布消息到目的地址前,要连接该端点,
* 即用户发送请求url="/applicationName/hello"与STOMP server进行连接。之后再转发到订阅url;
* PS:端点的作用——客户端在订阅或发布消息到目的地址前,要连接该端点。
* @param stompEndpointRegistry
*/
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
//在网页上可以通过"/applicationName/hello"来和服务器的WebSocket连接
stompEndpointRegistry.addEndpoint("/hello").setAllowedOrigins("*").withSockJS();
}
/**
* 配置了一个简单的消息代理,如果不重载,默认情况下回自动配置一个简单的内存消息代理,用来处理以"/topic"为前缀的消息。这里重载configureMessageBroker()方法,
* 消息代理将会处理前缀为"/topic"和"/queue"的消息。
* @param registry
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//应用程序以/app为前缀,代理目的地以/topic、/user为前缀
registry.enableSimpleBroker("/topic", "/user");
registry.setApplicationDestinationPrefixes("/app");
registry.setUserDestinationPrefix("/user");
}
@RestController
public class GreetingController {
@Autowired
private SimpMessageSendingOperations simpMessageSendingOperations;
@MessageMapping("/hello") //"/hello"为WebSocketConfig类中registerStompEndpoints()方法配置的
@SendTo("/topic/greetings")
public void greeting(@Header("atytopic") String topic, @Headers Map<String, Object> headers) {
System.out.println("connected successfully....");
System.out.println(topic);
System.out.println(headers);
}
/**
* 这里用的是@SendToUser,这就是发送给单一客户端的标志。本例中,
* 客户端接收一对一消息的主题应该是“/user/” + 用户Id + “/message” ,这里的用户id可以是一个普通的字符串,只要每个用户端都使用自己的id并且服务端知道每个用户的id就行。
* @return
*/
@MessageMapping("/message")
@SendToUser("/message")
public Greeting handleSubscribe() {
System.out.println("this is the @SubscribeMapping('/marco')");
return new Greeting("I am a msg from SubscribeMapping('/macro').");
}
/**
* 测试对指定用户发送消息方法
* @return
*/
@RequestMapping(path = "/send", method = RequestMethod.GET)
public Greeting send() {
simpMessageSendingOperations.convertAndSendToUser("1", "/message", new Greeting("I am a msg from SubscribeMapping('/macro')."));
return new Greeting("I am a msg from SubscribeMapping('/macro').");
}
public class Greeting {
private String content;
public Greeting(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
2.利用websocket技术利用代码实现一对多,一对一通信
var websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
var url = "ws://" + window.location.host +"/webSocket/webSocketOneToOne/1,123"
websocket = new WebSocket(url);
} else {
alert('当前浏览器 Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function() {
setMessageInnerHTML("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function() {
setMessageInnerHTML("WebSocket连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function(event) {
console.log("回调信息",event.data)
setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function() {
setMessageInnerHTML("WebSocket连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
closeWebSocket();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function send() {
var message = document.getElementById('text').value;
//message作为发送的信息,role作为发送的对象标识,socketId是此次会话的标识
websocket.send(JSON.stringify({'message':message,'role':'2','socketId':"123"}));
}
@ServerEndpoint(value = "/webSocketOneToOne/{param}")
public class WebSocketOneToOne {
// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount;
//实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key为用户标识
private static Map<String,WebSocketOneToOne> connections = new ConcurrentHashMap<>();
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
private String role;
private String socketId;
/**
* 连接建立成功调用的方法
*
* @param session
* 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(@PathParam("param") String param,Session session) {
this.session = session;
String[] arr = param.split(",");
this.role = arr[0]; //用户标识
this.socketId = arr[1]; //会话标识
connections.put(role,this); //添加到map中
addOnlineCount(); // 在线数加
System.out.println("有新连接加入!新用户:"+role+",当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
connections.remove(role); // 从map中移除
subOnlineCount(); // 在线数减
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message
* 客户端发送过来的消息
* @param session
* 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
JSONObject json=JSONObject.fromObject(message);
String string = null; //需要发送的信息
String to = null; //发送对象的用户标识
if(json.has("message")){
string = (String) json.get("message");
}
if(json.has("role")){
to = (String) json.get("role");
}
send(string,role,to,socketId);
}
/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
//发送给指定角色
public static void send(String msg,String from,String to,String socketId){
try {
//to指定用户
WebSocketOneToOne con = connections.get(to);
if(con!=null){
if(socketId==con.socketId||con.socketId.equals(socketId)){
con.session.getBasicRemote().sendText(from+"说:"+msg);
}
}
//from具体用户
WebSocketOneToOne confrom = connections.get(from);
if(confrom!=null){
if(socketId==confrom.socketId||confrom.socketId.equals(socketId)){
confrom.session.getBasicRemote().sendText(from+"说:"+msg);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketOneToOne.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketOneToOne.onlineCount--;
}
}
|