58做二手车网站应该怎么推广,沈阳关键词优化公司,做三折页宣传册 网站,今晚现场直播文章目录 Socket是什么引入面试题, 使你更深刻的理解四元组 Socket网络通信大体流程实战演示TCP连接建立过程需要用到的linux 查看网络的一些命令测试的程序一些准备工作启动服务端, 并没有调用accept启动客户端开启服务accept Socket是什么
通俗来说,Socket是套接字,是一种编… 文章目录 Socket是什么引入面试题, 使你更深刻的理解四元组 Socket网络通信大体流程实战演示TCP连接建立过程需要用到的linux 查看网络的一些命令测试的程序一些准备工作启动服务端, 并没有调用accept启动客户端开启服务accept Socket是什么
通俗来说,Socket是套接字,是一种编程接口,类似于电话插口,通过Socket可以进行网络通信, 但是这种很不容易让人理解.
其实本质来说Socket就是四原组,包括 客户端ip: 客户端端口 服务端ip: 服务端端口, 其实就是一个对应关系, 通过这个四原组就可以唯一的确定一个数据包来自哪里,要发送到哪里. 从而可以使得不同服务器上的不同进程进行网络通信, 而数据不会乱掉, 这就是Socket.
Socket是TCP协议层的, 是内核级别的, 这个怎么理解? 在我们使用Socket编程时,服务需要执行accept()方法进行监听, 但是即使我们不执行这个方法, 也会进行三次握手,直接建立tcp连接, 内核会直接帮我们做这件事情, 这个后面有程序验证.
引入面试题, 使你更深刻的理解四元组
现在有一个客户端,IP地址用AIP代替, 还有一个服务端, IP地址是CIP 而通信其实是两个进程间的通信,我们知道服务端进程在启动的时候需要绑定监听一个端口, 比如是XPORT. 而客户端在发起建立连接的时候其实也会随机起一个端口, 比如是BPORT, 那双方建立连接的时候就会有一个四元组. 这个对应关系既在客户端存在,也在服务端存在 问题一: 服务端在建立连接后是否需要为客户端随机分配一个端口 不需要的, 因为当建立连接后, 不论是客户端还是服务端, 内核都会为这个连接分配资源, 在资源中都存储了这个对应关系(四元组), 所以不论是对于客户端还是服务端, 只要这个对应关系有, 这个对应关系是一个唯一标识, 就没有必要再分配一个端口号了.双方就可以进行通信. 问题二: 同一个客户端, 能不能启多个不同的进程去连接同一个服务端的某一个进程 是可以的, 比如客户端启了三个进程,占用的端口分别是BPORT,CPORT,DPORT, 那其实就会形成三个不同的四原组. 这三个都是唯一的, 自然不会影响. 问题三: 一个服务端的某个进程, 连接了很多不同的客户端, 那是怎么区分每个数据包是来自哪个客户端呢 其实在每个数据包中, 都会有这个对应关系, 也就是这个四元组, 这样就很容器区分了 问题四:默认linux机器能使用的端口是65535个, 那假如一个客户端起了很多的进程, 连接的都是同一个服务器的80端口, 把65535个端口都用了, 那现在这个客户端能不能再使用端口去连接相同服务器的其他端口 或者 不同服务器的其他端口 都是可以的, 因为客户端可以重复使用这些端口, 只要生成的四元组是唯一的, 比如以下, 某个客户端的B,C,D端口已经连接了某个服务端的X端口, 现在又用客户端的B,C,D端口连接了服务端的Y端口, 这六组对应关系都是唯一的, 就能够确定数据来源哪, 发送到哪, 那自然是没问题的 问题五: 那客户端可以使用相同的端口,但是服务端一个进程已经用了80端口, 再起一个程序去占用80端口就会报异常, 这是为什么呢 因为服务端是ServerSocket, ServerSocket还有些特殊, 需要先开启监听Listen, 开启监听后等待客户端的连接, 那假如服务端有两个程序A,B都使用了80端口开启监听等待连接, 此时有个客户端想要和A程序通信, 发起三次握手中第一次连接请求, 直接发给了80端口, 此时能分清是要和哪个程序建立连接吗? 此时是分不清的. 再举个生活中的通俗例子, 你有一个电话号码, 可以用这个电话号码打给市场监督管理局, 这个市场监督管理局的电话一定是唯一的, 不然你就会分不清, 但是你可以用同样的电话号码打给住建局, 那你其实就是客户端, 可以用相同的号码, 而公家单位市场监管局,住建键局这些,他们是服务端, 电话号码都是唯一的, 不能使用相同的.
Socket网络通信大体流程
接下来大致理一下不同机器上两个应用程序通过Socket通信流程
服务端开启监听, 在内核中生成一个LISTEN并绑定端口客户端开启连接后, 两个机器内核开始进行三次握手, 建立连接建立连接之后会给其分配资源, 比如说分配buffer, 未来我们读写数据就是和这个buffer交互 还会在内核中保存这个四元祖, 也就是这个映射关系为对应程序分配TCP文件描述符 , 将来应用程序就可以通过这个文件描述符找到对应的四元组, 从而找到buffer,进行数据的读写.
实战演示TCP连接建立过程
需要用到的linux 查看网络的一些命令
lsof -p 进程id --------- 查看某个进程的文件描述符,包括tcp这些
netstat -natp ------- 可以查看一个tcp连接建立的过程
tcpdump ------ 可以对tcp连接进行抓包测试的程序
客户端 - ServerClientTest
public class ServerClientTest {public static void main(String[] args) {try {Socket client new Socket(192.168.68.2, 9090);client.setTcpNoDelay(true);client.setSendBufferSize(20);OutputStream outputStream client.getOutputStream();BufferedReader reader new BufferedReader(new InputStreamReader(System.in));while(true){String line reader.readLine();if(line ! null){byte[] bytes line.getBytes();for (byte b : bytes) {outputStream.write(b); //注意这里面没有调用flush}}}} catch (IOException e) {e.printStackTrace();}}}
服务端 - ServerSocketTest
public class ServerSocketTest {public static void main(String[] args) {ServerSocket server null;try {server new ServerSocket();server.bind(new InetSocketAddress(9090));} catch (IOException e) {e.printStackTrace();}System.out.println(server up use 9090);while(true){try {//阻塞,暂时不执行accept,分水岭System.in.read();//真正开启监听Socket client server.accept();System.out.println(client port: client.getPort());new Thread(() - {while(true){try {InputStream in client.getInputStream();BufferedReader reader new BufferedReader(new InputStreamReader(in));char[] data new char[1024];int num reader.read(data);if(num0){System.out.println(client read some data :new String(data));}else if(num0){System.out.println(client read data nothing......);}else{System.out.println(client read data error......);}} catch (IOException e) {e.printStackTrace();}}}).start();} catch (IOException e) {e.printStackTrace();}finally {try {server.close();} catch (IOException e) {e.printStackTrace();}}}}
}需要注意,在 服务端启动程序后,如果不进行任何输入, 程序会阻塞在这里, 不会调用accept()方法 一些准备工作
对9090这个端口进行抓包 查看当前机器的所有的TCP连接,并没有9090端口的
启动服务端, 并没有调用accept 启动服务端,但是还没有调用accept, 内核会主动注册一个LISTEN,绑定端口号 有了这个LISTEN之后, 客户端就可以连进来了 同时使用jps 命令查看刚刚启动的服务端的进程id是3907 使用lsof -p 3907, 看看这个进程的文件描述符情况,可以看到有一个监听状态的文件描述符
启动客户端 注意此时,服务端还没有调用accept方法,启动服务端后没有做任何操作, 还阻塞在这行代码
使用tcpdump查看抓包, 发现已经经过三次握手 使用netstat查看所有的TCP以及连接状态, 发现9090端口已经和一个客户端建立了连接, 但是这个Socket还没有分配给任何程序去使用 但是内核里面已经有它了
开启服务accept
这里随便输入了一个回车, 跳过了 System.in.read(); 这行代码 程序里面真正有了一个tcp的文件描述符 内核中的socket也真正分配给了对应程序去使用 至此, 两个进程建立TCP连接通信的过程已经完毕.
到这里, 你应该真正能明白Socket最主要就是四元组, 明白Socket是TCP协议层的, 是内核级别的.
今天的分享就到这里了,有问题可以在评论区留言,均会及时回复呀. 我是bling,未来不会太差,只要我们不要太懒就行, 咱们下期见.