从事业务开发太久,突然被问到Socket是什么这个问题?竟然一下子系统回答不上来,咳,可怕。
1.Socket是什么
了解Socket首先必须要对基础网络传输协议有一定的了解,比如OSI七层和TCP/IP五层标准网络架构有一定的了解。
TCP/IP五层:应用层,传输层,网络层,数据链路层,物理层;
OSI七层:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层;
其中除了,表示层和会话层外,每一层都有自己的协议,也就是数据格式。
应用层 文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet。
表示层 数据格式化,代码转换,数据加密 没有协议
会话层 解除或建立与别的接点的联系 没有协议
传输层 提供端对端的接口 TCP,UDP。
网络层 为数据包选择路由 IP,ICMP,RIP,OSPF,BGP,IGMP
数据链路层 传输有地址的帧以及错误检测功能 SLIP,CSLIP,PPP,ARP,RARP,MTU
物理层 以二进制数据形式在物理媒体上传输数据 ISO2110,IEEE802,IEEE802.2。
这里给出一个问题,为什么一会七层,一会又五层,两者关系是啥?
上面只是网络传输架构,以及具体的协议格式,那么具体实现尼?
来了,Socket是对TCP/IP是对整个网络传输协议架构的封装,它的出现只是使得我们更加方便的使用TCP/IP协议栈而已。它提供了针对传输层TCP或者UDP编程的接口。
2.Socket例子
2.1 一个TCP连接
一个TCP连接由客户端Ip+端口号,服务端Ip+端口号确认!任何端不能创建一个相同的TCP连接!!否则,出错,Address already in use!
2.2 服务端-- 连接监听端
package socket;
import java.io.*;
import java.net.*;
public class TcpServer {
public static void main(String[] args) throws Exception {
//ServerSocket源码解析,指定了监听的端口号。
ServerSocket server = new ServerSocket(9091);
try {
Socket client = server.accept();
try {
BufferedReader input =
new BufferedReader(new InputStreamReader(client.getInputStream()));
boolean flag = true;
int count = 1;
while (flag) {
System.out.println("客户端要开始发骚了,这是第" + count + "次!");
count++;
String line = input.readLine();
System.out.println("客户端说:" + line);
if (line.equals("exit")) {
flag = false;
System.out.println("客户端不想玩了!");
} else {
System.out.println("客户端说: " + line);
}
}
} finally {
client.close();
}
} finally {
server.close();
}
}
}
从程序中可以看到,服务端使用ServerSocket监听一个端口号,客户端通过这个端口号和服务端建立连接。
当然,服务端默认使用本机ip,也可以指定一个IP(因为服务端可能存在多网卡,多ip)。
InetAddress bindip = InetAddress.getByName("192.168.1.168");
ServerSocket server = new ServerSocket(9091, 0, bindip);
2.3 客户端 – 连接发起端
package socket;
import java.io.*;
import java.net.*;
import java.util.Scanner;
public class TcpClient {
public static void main(String[] args) throws Exception {
Socket client = new Socket("127.0.0.1", 9091);
try {
PrintWriter output =
new PrintWriter(client.getOutputStream(), true);
Scanner cin = new Scanner(System.in);
String words;
while (cin.hasNext()) {
words = cin.nextLine();
output.println(words);
System.out.println("写出了数据: " + words);
}
cin.close();
} finally {
client.close();
}
}
}
但上面程序可以看到,客户端建立连接通信时候,需要指定服务端的IP和端口号。
但是客户端并未指定自己这端的端口号以及IP,注意,Socket客户端在和服务端连接时,会随机分配一个端口号以及使用本机IP。
3.Socket应用
Socket是一个最基本的一个TCP传输层协议的接口。
上面示例只是最简单一个Socket通信例子,要达到生产级别的应用,还远不止如此!
3.1 Dubbo中的应用
Dubbo是基于TCP协议的,底层的通信原理肯定也会使用Socket,只不过通过TCP协议传输的内容有自己格式规约(Dubbo自己的协议)。
当然,并不是指Dubbo改造TCP协议,而是Dubbo在TCP协议报文里传输的内容指定了一个固定格式。
一次Dubbo调用?
Consumer调用Provider的接口
1.通过注册中心Register随机找到一个Provider所在的机器IP+端口号。
IP+端口号是在Provider上报到Register注册中心的,上报的数据格式可能是Map,key是接口,value是当前机器的Ip+port。
2.按Dubbo协议组装传输的内容格式,其中包含了调用的具体接口名、方法名、参数序列化后的二进制内容。
3.通过Socket和Provider的机器建立连接,并传输Dubbo协议内容。
4.Provider监听本机上的对应IP+端口号,获取传入的内容。解析获得Provider调用的接口名、方法名、同样的序列化方式反序列化得到入参。
5.通过Java反射运行Provider上指定的方法,得到结果后,同样反序列化给Consumer端。
以上是远程调用的一个本质原理,其中涉及到序列化,反射,甚至要考虑多线程。Dubbo作为一个高可用,高扩展的RPC框架,其中内容远不止如此简单。
3.2 XxlJob上的应用
XxlJob是一个分布式任务调度平台。它包含了定时任务控制台、客户端。
控制台是对客户端具体定时任务的调度。
客户端是对具体定时任务的执行。
这里就不说具体怎么使用了。只问一个问题,客户端是在其他机器上部署的,控制台是怎样找到客户端并执行具体定时任务的?
客户端简单实现
@Slf4j
@Component
@JobHandler(value = "subscribeTagTask")
public class SubscribeSelectTask extends IJobHandler {
private final AManager aManager;
private final SManager sManager;
@Override
public ReturnT<String> execute(String s) throws Exception {
return ReturnT.SUCCESS;
}
public SubscribeSelectTask(AManager aManager,
SManager sManager) {
this.aManager = aManager;
this.sManager = sManager;
}
}
这里简单说下:
1.客户端启动的时候,会对配有JobHandler注解的定时任务进行扫描,并上报当前定时任务运行的机器的IP+Port到控制台。
同时对该IP+Port进行监听,等待控制台调度消息。
2.控制台将上报内容存储到已经配好的这个任务。
3.控制台在达到调度时间的进行调度时候,会从多个客户端里面随机或者轮询(可配置)选择一个客户端IP+Port,建立TCP连接。
并传输调度指令。
4.客户端监听到这个消息后,解析控制台发的调度指令,找到对应的定时任务,怎么找到尼,或许是按照JobHandler配置的Value
通过反射找到指定的定时任务,并通过反射执行。
TIPS:上面XXLJOB的客户端其实也是Socket对应的服务端,会进行Socket连接监听。
以上就是大致的原理,具体细节要深入源码,如有不对,敬请指正。
参考文章
https://blog.youkuaiyun.com/github_34606293/article/details/78230456
https://blog.youkuaiyun.com/wzy_1988/article/details/17131381