作者:Grey

原文地址:Java IO学习笔记四:Socket基础

准备两个Linux实例(安装好jdk1.8),我准备的两个实例的ip地址分别为:

io1实例:192.168.205.138

io2实例:192.168.205.149

安装必要工具:

yum install -y strace lsof  pmap tcpdump

准备服务端代码

import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket; /**
* BIO Socket Server
*/
public class SocketServerBIOTest {
private static final int PORT = 9090;
private static final int BACK_LOG = 2; public static void main(String[] args) {
ServerSocket server = null;
try {
server = new ServerSocket();
server.bind(new InetSocketAddress(PORT), BACK_LOG);
System.out.println("server started , port : " + PORT);
} catch (IOException e) {
e.printStackTrace();
}
try {
// 接受客户端连接
while (true) {
// 先阻塞,这样客户端暂时无法连接进来
System.in.read(); // 这个方法也是阻塞的,如果没有客户端连接进来,会一直阻塞在这里,除非设置了超时时间
Socket client = server.accept(); System.out.println("client " + client.getPort() + " connected!!!");
// 客户端连接进来后,开辟一个新的线程去接收并处理
new Thread(() -> {
try {
InputStream inputStream = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
char[] data = new char[1024];
while (true) {
int num = reader.read(data);
if (num > 0) {
System.out.println("client read some data is :" + num + " val :" + new String(data, 0, num));
} else if (num == 0) {
System.out.println("client read nothing!");
continue;
} else {
System.out.println("client read -1...");
System.in.read();
client.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
}

死循环中,由于第一句:

System.in.read();

导致

 Socket client = server.accept();

无法执行,即:服务端此时是无法接收客户端的。

准备客户端代码:

import java.io.*;
import java.net.Socket; /**
* Socket Client
*/
public class SocketClientTest { public static void main(String[] args) { try {
Socket client = new Socket("192.168.205.138", 9090);
OutputStream out = client.getOutputStream();
InputStream in = System.in;
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while (true) {
String line = reader.readLine();
if (line != null) {
byte[] bb = line.getBytes();
for (byte b : bb) {
out.write(b);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

在实例io1中启动服务端代码:

javac SocketServerBIOTest.java && java SocketServerBIOTest

在io1中开启抓包工具:

tcpdump -nn -i ens33 port 9090

在io2中执行客户端代码:

javac SocketClientTest.java && java SocketClientTest

由于我们在服务端加了一段:

System.in.read()

方法,导致服务端其实没办法执行accept()

但是在服务端查看抓包信息:

[root@io socket]# tcpdump -nn -i ens33 port 9090
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
15:19:56.021974 IP 192.168.205.149.56944 > 192.168.205.138.9090: Flags [S], seq 391962776, win 29200, options [mss 1460,sackOK,TS val 16515471 ecr 0,nop,wscale 7], length 0
15:19:56.022035 IP 192.168.205.138.9090 > 192.168.205.149.56944: Flags [S.], seq 2744580571, ack 391962777, win 28960, options [mss 1460,sackOK,TS val 16517545 ecr 16515471,nop,wscale 7], length 0
15:19:56.022349 IP 192.168.205.149.56944 > 192.168.205.138.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 16515472 ecr 16517545], length 0

内核已经为客户端和服务端建立了连接并完成了三次握手,在服务端使用netstat查看:

[root@io socket]# netstat -ntap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...
tcp6 0 0 192.168.205.138:9090 192.168.205.149:56944 ESTABLISHED -
...

显示已经建立了连接,只不过还没有分配:PID/Program name。

在客户端输入一些信息,

[root@io2 socket]# javac SocketClientTest.java && java SocketClientTest
asdfasdfasdfasf

在服务端再次执行netstat

[root@io socket]# netstat -ntap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...
tcp6 15 0 192.168.205.138:9090 192.168.205.149:56944 ESTABLISHED -
...

也显示出接收到了数据。

再服务端再次开启抓包:

tcpdump -nn -i ens33 port 9090

再次在客户端输入一些数据:

[root@io2 socket]# javac SocketClientTest.java && java SocketClientTest
asdfasdfasdfasf
dfasdfasdfasdas

可以看到抓包信息:

[root@io ~]# tcpdump -nn -i ens33 port 9090
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
15:26:48.632564 IP 192.168.205.149.56944 > 192.168.205.138.9090: Flags [P.], seq 391962792:391962793, ack 2744580572, win 229, options [nop,nop,TS val 16928082 ecr 16757410], length 1
15:26:48.632609 IP 192.168.205.138.9090 > 192.168.205.149.56944: Flags [.], ack 1, win 227, options [nop,nop,TS val 16930156 ecr 16928082], length 0
15:26:48.632791 IP 192.168.205.149.56944 > 192.168.205.138.9090: Flags [P.], seq 1:15, ack 1, win 229, options [nop,nop,TS val 16928082 ecr 16930156], length 14
15:26:48.632825 IP 192.168.205.138.9090 > 192.168.205.149.56944: Flags [.], ack 15, win 227, options [nop,nop,TS val 16930156 ecr 16928082], length 0

以上实验主要说明了一个问题:

虽然在应用层面,服务端没有调用accept() 去接收客户端,但是,内核其实已经完成了客户端和服务端的三次握手以及数据传输。

接下来,我们触发服务端:

Socket client = server.accept();

这段逻辑

服务端可以显示客户端的数据

^C[root@io socket]# java SocketServerBIOTest
server started , port : 9090 client 56944 connected!!!
client read some data is :30 val :asdfasdfasdfasfdfasdfasdfasdas

服务端执行nestat -ntap,

[root@io ~]# netstat -ntap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...
tcp6 0 0 192.168.205.138:9090 192.168.205.149:56944 ESTABLISHED 2266/java
...

可以看到已经分配了一个PID,通过:

lsof -p 2266

查看这个java进程相关的文件描述符

[root@io ~]# lsof -p 2266
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
...
java 2266 root 6u IPv6 26479 0t0 TCP 192.168.205.138:websm->192.168.205.149:56944 (ESTABLISHED)
...

6u就对应了服务端和客户端连接的一个Socket。

以上演示的是一个BIO的模型,在具体的编程中,服务端和客户端都有很多配置的参数。详见:socketOpt

源码:Github

Java IO学习笔记四:Socket基础的更多相关文章

  1. Java IO学习笔记四

    内存操作流 之前的所有的流操作都是针对文件的,但是有时候只是想要实现数据间转换,此时如果我们想要创建一个文件然后再删除文件,那样显得有点麻烦,因此此时的内存操作流就显得很适合这类的操作,因为它只是在内 ...

  2. Java IO学习笔记:概念与原理

    Java IO学习笔记:概念与原理   一.概念   Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络)读入到内存 中,形成了 ...

  3. Java IO学习笔记二

    Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...

  4. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  5. Java IO学习笔记七:多路复用从单线程到多线程

    作者:Grey 原文地址:Java IO学习笔记七:多路复用从单线程到多线程 在前面提到的多路复用的服务端代码中, 我们在处理读数据的同时,也处理了写事件: public void readHandl ...

  6. Java IO学习笔记六:NIO到多路复用

    作者:Grey 原文地址:Java IO学习笔记六:NIO到多路复用 虽然NIO性能上比BIO要好,参考:Java IO学习笔记五:BIO到NIO 但是NIO也有问题,NIO服务端的示例代码中往往会包 ...

  7. Java IO学习笔记五:BIO到NIO

    作者:Grey 原文地址: Java IO学习笔记五:BIO到NIO 准备环境 准备一个CentOS7的Linux实例: 实例的IP: 192.168.205.138 我们这次实验的目的就是直观感受一 ...

  8. Java IO学习笔记八:Netty入门

    作者:Grey 原文地址:Java IO学习笔记八:Netty入门 多路复用多线程方式还是有点麻烦,Netty帮我们做了封装,大大简化了编码的复杂度,接下来熟悉一下netty的基本使用. Netty+ ...

  9. Java IO学习笔记总结

    Java IO学习笔记总结 前言 前面的八篇文章详细的讲述了Java IO的操作方法,文章列表如下 基本的文件操作 字符流和字节流的操作 InputStreamReader和OutputStreamW ...

随机推荐

  1. ajax提交session超时跳转页面使用全局的方法来处理

    来自:http://www.jb51.net/article/43770.htm 如果是ajax提交,超时时从服务器发出的跳转命令就不会起作用,所以如果是session超时,而且是在ajax请求,就在 ...

  2. PHP中文转拼音扩展

    Pinyin 基于 CC-CEDICT 词典的中文转拼音工具,更准确的支持多音字的汉字转拼音解决方案. 安装 使用 Composer 安装: $ composer require "over ...

  3. 数据库增量日志监听canal

    概述 canal是阿里巴巴旗下的一款开源项目,纯Java开发.基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB). 起源:早期,阿里巴巴B2B公司 ...

  4. 使用DevExpress的GridControl实现多层级或无穷级的嵌套列表展示

    在我早期的随笔<在GridControl表格控件中实现多层级主从表数据的展示>中介绍过GridControl实现二级.三级的层级列表展示,主要的逻辑就是构建GridLevelNode并添加 ...

  5. MySQL中MyISAM为什么比InnoDB查询快

    大家都知道在MySQL中,MyISAM比InnoDB查询快,但很多人都不知道其中的原理. 今天我们就来聊聊其中的原理,另外也验证下是否MyISAM比InnoDB真的查询快. 在探索其中原理之前,我们先 ...

  6. rabbitmq介绍以及初步使用

    什么是MQ? ​ MQ(Message Queue):翻译为消息队列,通过典型的生产者和消费者模型,生产者不断向消息队列中生产消息,消费者不断地从队列中获取消息.因为消息的生产和消费都是异步的,而且只 ...

  7. 3. java基础语法

    3.1 注释(理解) 注释是对代码的解释和说明文字,可以提高程序的可读性,因此在程序中添加必要的注释文字十分重要.Java中的 注释分为三种: 单行注释.单行注释的格式是使用//,从//开始至本行结尾 ...

  8. ruby基础(四)

    ruby基础知识 模块 模块是ruby的特色功能之一.如果说类是事物的实体以及行为,那么模块表现的 就是事物的行为部分,模块和类有以下两点不同: 模块不能拥有实例 模块不能被继承 模块的使用方法 mo ...

  9. Linux学习之路-Linux-at及cron命令【7】---20171215

    Linux学习之路-Linux-at及cron命令[7]---20171215 DannyExia000人评论986人阅读2017-12-24 17:28:03   ntpdate 命令 [root@ ...

  10. Linux是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的性能稳定的操作系统,可免费使用并自由传播。

    Linux是一个基于POSIX和Unix的多用户.多任务.支持多线程和多CPU的性能稳定的操作系统,可免费使用并自由传播. Linux是众多操作系统之一 , 目前流行的服务器和 PC 端操作系统有 L ...