网络分层
主机网络层–数据链路层
- 主机网络层定义了一个特定的网络接口比如网卡或者wifi天线,如果通过物理连接向本地网络或者世界其他地方发送IP数据报。
- 主机网络层中有链接不同计算机的硬件(电缆,光纤,无线电波等)自测的部分称为网络物理层。
网络层
- 网络层现已定义了数据位和字节如何组织为更大的分组,称为包,还定义了寻址机制,不同计算机按照这个寻址机制查找对方。其中运用的最广的协议是网际协议(IP)路由,寻址功能
- IP指两个协议,IPV4和IPV6,IPV4采用32位地址,IPV6使用128位地址,另外还增加一些技术特性来帮助完成路由功能,在两个协议中数据包在网际层传输,这些包称为数据报,每个IPV4数据报包含一个长度为20~60字节的首部已经一个包含最多65515字节数据的有效荷载,实际可以比这个小,IPV6则更大,最多可以6G。
- 作用二,支持不同类型的网络层相互对话,比如理由起完成WIFI和Ethernet,Ethernet和DSL, DSL和光纤往返鞥协议之间的转换
传输层
- 因为原始数据报文无法保证可靠的,并且顺序的从源头到目的地所有需要传输层来保证各个报文以发送的顺序结束,并且保证数据的完整性(没有丢失和破坏)。如果丢包会请求重发,实现这个的是在每个报文通讯协议:TCP传输控制协议支持对丢失破坏的数据进行重传,UDP用户数据报协议允许接受方检测到破坏的包,但是不保证包的传输顺序,或者根本没有送达。UDP比TCP快,TCP比UDP更可靠
应用层
- 向用户传输数据的层称为引用层,他下面的三层共同定义了数据如何从一台计算机传输到另外一台计算机。应用层确认了数据传输之后的操作,比如各种HTTP用户web浏览器例如图片显示不会显示为一片数字,还有用于邮件的SMTP,POP,IMAP;用于文本传输的FTP,FSP,TFTP,用于文件的NFS等。
IP,TCP和UDP
IP
- 理论上每台计算机都有一个4字节的数字IPV4标识,一般写成点分四段格式,比如192.168.1.1,每个数字都是一个无符号字节,范围是0~255,所有理论上IPV4能表达的不同个数为255^4 大约42亿,但并不按人口分配,所有有的地区亚洲,欧洲已经不够用,所有出现IPV6
- IPV6使用16字节地址,用冒号分割的8个区块,每个区块四个十六进制数字。比如2001:0db8:85a3:08d3:1319:8a2e:0370:7344这样就有足够多的IP来标识每台计算机
- DNS设计是用来将域名替换IP,更加方便人类的记忆以及使用,单访问一个域名www.zhenai.com的时候DNS会将域名解析成对应的Internet地址,比如192.168.1.1,所有单我们用java范文网络需要同时处理数字地址和对应主机名,java.net.InetAddress类中提供。
- 端口:计算机由IP来区分,但是一台计算机可能同时需要做N件事,所有我们用端口(port)来实现区分。每台有IP地址的计算机由好几千个逻辑端口(每个传输层协议有65535个端口)。这些只是内存中的抽象,不表示任何物理实物,和USB等端口不一样,每个端口有1~65535之间的数字表示。
Internet地址分块
- IP由ISP(Internet服务提供商)分配,当公司需要组件一个基于IP的网络连接到Internet时候,ISP会给他们分配地址快,每个地址块有固定前缀,
网络地址转换
- 因为现在IPV4越来越少,所以现在大多数网络都使用了网络地址转换(NAT),大多数节点都是本地地址,这些地址在使用链接ISP的时候,会通过NAT转换成可路由的IP,比如,局域网我们看到的最多的是192.168.1.X这个其实是我们你内网的一个IP,是不可以对外路由的,当需要发送消息出去我们通过路由器会将这个地址修改外网地址216.254.85.72,当需要接收时候则将接受地址修改为我们的192.168.1.X。不过如果使用IPV6,这些就都没有意义了,包括NAT,因为IPV6 足够多,大可每台机器一个
防火墙
- 位于Internet和本地网络之间的一些硬件和软件会检测所有进出的数据,用来保证他的合法性,这就是防火墙,具体的检测方式各个网络要求不同则实现不同。
代理服务器
- 代理服务器和防火墙有关,如果防火墙阻止范文,代理服务器可以起到中间人的作用,我们请求代理服务器,由代理服务器去请求我们需要的web服务器页面,然后将相应转发给我,这样就可以跳过防火墙。优势在于这样访问,外部的主机只能看到代理服务器,并不知道真正请求的IP,
DNS
- 每个打的局域网都有一个DNS域名服务器,比如公司分配的局域网中的DNS只知道本地网络上的主机,如果需要访问局域网以外的地址,本地域名服务器会访问远程域名服务器,然后将结果转发给请求者。
- 缓存,由于DNS的开销可能相当大,比如频繁范文不可达的主机,可能需要几秒钟,所以InetAddress类会缓存查询的解决,下次查询直接读缓存,这样弊端在于如果域名变化这种极端情况,会再一定时间内改域名不可访问。
URI
- 统一资源标识符(Iniform Resource Identifier,URI)是采用一种特定预发标识一个资源的字符串。所标识的资源可能是文件,也可能是邮寄地址,新闻消息,图书,等内容。
URL
- URL是一个URI,除了标识一个资源,还会为资源提供一个特定的网络位置,客户端可以用它来获取这个资源的一个表示。与之不同的URI可以表示的信息是这个资源是什么,但是无法告诉你资源位置,已经如何得到这个资源。比如书名:《哈利波特》和“A图书馆B层C书架D位置”之间的区别,前者只是书名字,后者是该书本的位置
- URL中指定位置包括:范问协议(FTP,HTTP),服务器主机域名,文件在该服务器路径,比如:https://www.tapd.cn/20085821/prong/stories/view/1120085821001011775
从URL获取数据
- java.net.URL类中有几个方法可以从URL获取到数据
public final InputStream openStream() throws java.io.IOException
public URLConnection openConnection(Proxy proxy)
throws java.io.IOException
public final InputStream openStream() throws java.io.IOException
public final Object getContent() throws java.io.IOException
public final Object getContent(Class[] classes)
throws java.io.IOException
分解URL
- URL由五步法组成:协议,授权机构,路径,片段标识,查询字符串
- 例如:http://oa.zhenai.com/append/itapply/toITApplyTencentCtlApply.do?processInfoId=105#123其中模式Http, 授权机构是oa.zhenai.com,路径是append/itapply/toITApplyTencentCtlApply.do,片段标识是123,查询字符串是processInfoId=105,不过并发所有URL都有以上属性。
HTTP
HTTP协议
- 是web浏览器和web服务器之间通讯的协议标准,Http指定了客户端与服务器如何建立连接,如何从无武器请求数据,服务器如何响应请求,已经最后如何关闭,具体有如下步骤:
- 默认情况客户端打开80端口与服务器的一个TCP连接,URL中可以指定其他端口
- 客户端向服务器发送消息,请求指定路径上的资源。这个请求包括一个首部,可选的还可以有一个空行,后面是这个请求的数据
- 服务器向客户端发送响应,响应以响应码开头,后面是包含元数据的首部,一个空行已经请求的文档或者错误信息。
- 服务器关闭连接
- 以上是HTTP1.0的过程,在1.1中我们可以通过一个TCP连接连续发送多个请求和响应,也就是在第一步和第四部之前,2,3步骤反复执行。
- 一般客户端请求如下,get请求为例子:
GET https://www.zhenai.com/n/myZhenai HTTP/1.1
Host: www.zhenai.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://www.zhenai.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: sid=v9EVJ8hYWVYZsn0SVz3f; login_health=3f2b2be6d4ad6456edf3aa4152b72895a1af48e0204cf916040dce55e61b8be59179306966300ec32c4851e8c589ec054fb4aff05d7a5270f487aafde733dd0f; p=%5E%7Eworkcity%3D10101204%5E%7Esex%3D0%5E%7Emt%3D1%5E%7Enickname%3D%E6%AD%8C%E6%A8%82%5E%7Edby%3D17dece17d471cb1d%5E%7Elh%3D2000003756%5E%7Eage%3D25%5E%7E; preLG_2000003756=2019-01-25+16%3A24%3A07; dgpw=1; Hm_lvt_2c8ad67df9e787ad29dbd54ee608f5d2=1549953761; abt_params=LANDING%7C903050%7C9%7C0%7C0; _abTest_ext13_=0; __detect__grid__=1; token=2000041337.1550116596108.a7342e5c42a2a90a444c181cf68f9700; gr_user_id=d81cf4b0-28b1-4a5f-9a10-1c7e835659fc; ZHENAI_OA_REFERER=http%3A%2F%2Foa.zhenai.com%2Fappend%2Fitapply%2FtoITApplyTencentCtlApply.do%3FprocessInfoId%3D105
zone: 1012261-tapd-baiduocpc
- 第一行指定请求类别,资源路径,HTTP的版本1.1
- HOST:指定服务器名
- User_agent:用来让服务器指定使用的是什么浏览器,并且允许服务器发送为特定浏览器类型而优化的文件。
- Accetp:用来告诉服务器客户端可以处理那些数据类型
- Connection:keep-alive:
- 此处说明服务器不必在发送数据响应后关闭连接,可以保持打开,在同一个socket上等待来着客户端的新请求,之所以这么做,是因为,在Web会话中打开和关闭一个链接所花费的时间远远大于传输时间的时间,特别是当传输的数据量比较小的时候问题 格外的严重。而且对于使用HTTPS链接时候,简历一个安全的Socket链接比常规的更负责,因此通过这个参数可以减少不断的销毁重新建的过程,提供请求效率。
客户端Socket
使用Socket
-
Internet上数据按照有限大小的包进行传输,称为数据报(datagram),每个数据报包含一个首部(header)和一个有效载荷(payload)。首部包含目的地地址,端口,源头地址,端口,检查数据完整性,已经用于保证可靠传输的其他信息。数据载荷包含数据本身。在传输过程会遇到各种问题,要保证数据完整到达,是需要蒋书记分解,生成各自首部,即系入站包首部,跟踪那些包收到而哪些没有等繁重工作。因此Socket就是来帮我们解决,Socket运行程序员将网络连接看成是另外一个可以读的字节流,Socket帮我们掩盖了网络的底层细节,如错误检测,包大小,包分解,重传,网络地址等。
-
Socket是两台数据之间的一个链接可以完成7个操作:
- 链接远程机器
- 发送数据
- 接受数据
- 关闭连接
- 绑定端口
- 监听入站数据
- 在绑定端口上接受来自远程计算机的链接
一旦完成第一步建立了链接,本地和远程主机都可以送这个socket得到输入流,输出流,使用这两个流操作来相互发送数据,链接是双工的。下例子测试一下Socket编程
public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
try(Socket socket = new Socket("time.nist.gov", 13)){
socket.setSoTimeout(15000);
StringBuilder stringBuilder = new StringBuilder();
InputStream in = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(in, "ASCII");
for (int c = inputStreamReader.read(); c != -1; c = inputStreamReader.read()){
stringBuilder.append((char) c);
}
System.out.println(stringBuilder);
StringBuilder bufferReaderBuilder = new StringBuilder();
String s;
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
while ( (s = bufferedReader.readLine()) != null){
bufferReaderBuilder.append(s);
}
System.out.println(bufferReaderBuilder);
in.close();
inputStreamReader.close();
bufferedReader.close();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
- 以上输出和telnet time.nist.gov 13 输出结果是一样的,值得注意的是其中用的ASCII编码并不通用协议
Socket写入服务器
- 和读服务器中数据一样,只是获取的是outputStream如下
public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
try(Socket socket = new Socket("dict.org", 2628)){
socket.setSoTimeout(15000);
OutputStream outputStream = socket.getOutputStream();
Writer writer = new OutputStreamWriter(outputStream, "UTF-8");
writer.write("DEFINE eng-lat gold\r\n");
writer.flush();
StringBuilder stringBuilder = new StringBuilder();
InputStream in = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(in, "ASCII");
for (int c = inputStreamReader.read(); c != -1; c = inputStreamReader.read()){
stringBuilder.append((char) c);
}
System.out.println(stringBuilder);
}catch (Exception e) {
e.printStackTrace();
}
}
- Socket链接成功之后会自动保护一下熟悉,并可以通过java中的方法获取,比如:远程地址,端口,本地地址,端口
- Socket还提供isClose,isConnected方法确认是否关闭状态
- Socket,java支持九个可设置的选项
@Native public final static int TCP_NODELAY = 0x0001;
@Native public final static int SO_BINDADDR = 0x000F;
@Native public final static int SO_REUSEADDR = 0x04;
@Native public final static int SO_BROADCAST = 0x0020;
@Native public final static int SO_LINGER = 0x0080;
@Native public final static int IP_TOS = 0x3;
@Native public final static int SO_TIMEOUT = 0x1006;
@Native public final static int SO_SNDBUF = 0x1001;
@Native public final static int SO_RCVBUF = 0x1002;
@Native public final static int SO_KEEPALIVE = 0x0008;
@Native public final static int SO_OOBINLINE = 0x1003;
//在 java.net.SocketOptions类中
- Socket异常信息一般都是IOEXCEPTION,如果抛这个异常,并不知道发生啥,他下面的子类异常可以反映出具体原因:
public class BindException extends socketException;
public class ConnectionException extends SocketException;
public class NoRouteToHostException extends SocketException;
其中BindException标识你没有足够权限使用这个端口,单链接被远程机器拒绝,而拒绝的原因通常是由于主机繁忙或者有进程在监控这个端口,此时会抛出ConnectionException异常,最后一点,NotRouteToHostException异常标识链接已经超时。
服务器Socket
ServerSocket
ServerSocket server = new ServerSocket(13);//创建一个端口是13的本地socket服务
Socket connection = server.accept()//改操作会一直阻塞程序直到有一个客户端的连接了13 端口
OutputStream outputStream = socket.getOutputStream();
Writer writer = new OutputStreamWriter(outputStream);//从socket中获取输出流,从而可以将需要传递的数据从服务器输出到管道中。