Java 网络编程之 Socket
一、课程目标
网络模型
TCP 协议与 UDP 协议区别
Http 协议底层实现原理。
二、什么是网络模型
网络编程的本质是两个设备之间的数据交换,当然,在计算机网络 中,设备主要指计算
机。数据传递本身没有多大的难度,不就是把一个设备中的数据发送给两外一个设备,然
后接受另外一个设备反馈的数据。
现在的网络编程基本上都是基于请求/ / 响应方式的,也就是一个设备发送请求数据给
另外一个,然后接收另一个设备的反馈。
在网络编程中,发起连接程序,也就是发送第一次请求的程序,被称作客户端
(Client) ,等待 其他程序连接的程序被称作服务器 (Server) 。客户端程序可以在需要的时
候启动,而服务器为了能够时刻相应连接,则需要一直启动。例如以打电话为例,首先拨
号的人类似于客户端,接听电话的人必须保持电话畅通类似于服务器。
连接一旦建立以后,就客户端和服务器端就可以进行数据传递了,而且两者的身份是
等价的。
在一些程序中,程序既有客户端功能也有服务器端功能,最常见的软件就是 BT 、
e emule 这类软件了。
下面来谈一下如何建立连接以及如何发送数据。
2. 1 、IP地址与域名
在现实生活中,如果要打电话则需要知道对应人的电话号码,如果要寄信则需要知道收信
人的地址。在网络中也是这样,需要知道一个设备的位置,则需要使用该设备的 IP 地
址,具体的连接过程由硬件实现,程序员不需要过多的关心。
IP 地址是一个规定,现在使用的是 IPv4 ,既由 4 个 0- - 255 之间的数字组成,在计算
机内部存储时只需要 4 个字节即可。在计算机中, IP 地址是分配给网卡的,每个网卡有一
个唯一的 IP 地址,如果一个计算机有多个网卡,则该台计算机则拥有多个不同的 IP 地
址,在同一个网络内部, IP 地址不能相同。 IP 地址的概念类似 于电话号码、身份证这样
的概念。
由于 IP 地址不方便记忆,所以有专门创造了域名 (Domain Name) 的概念,其实就是给
IP 取一个字符的名字,例如 163.com 、m sina.com 等。IP 和域名之间存在一定的对应关
系。如果把 P IP 地址类比成身份证号的话,那么域名就是你的姓名。
其实在网络中只能使用 IP 地址进行数据传输,所以在传输以前,需要把域名转换为
IP ,这个由称作 DNS 的服务器专门来完成。
所以在网络编程中,可以使用 IP 或域名来标识网络上的一台设备。
2.2、端口的概念
为了在一台设备上可以运行多个程序,人为的设计了端口 (Port) 的概念,类似的例子是公
司内部的分机号码。
规定一个设备有 216 个,也就是 65536 个端口,每个端口对应一个唯一的程序。每个
网络程序,无论是客户端还是服务器端,都对应一个或多个特定的端口号。由于 0- - 1024
之间多被操作系统 占用,所以实际编程时一般采用 1024 以后的端口号。
使用端 口号,可以找到一台设备上唯一的一个程序。
所以如果需要和某台计算机建立连接的话,只需要知道 IP 地址或域名即可,但是如
果想和该台计算机上的某个程序交换数据的话,还必须知道该程序使用的端口号。
小结
网络编程就是使用 IP 地址,或域名,和端口连接到另一台计算机上对应的程序,按照
规定的协议( ( 数据格式) ) 来交换数据,实际编程中建立连接和发送、接收数据在语言级已经
实现,做的更多的工作是设计协议,以及编写生成和解析数据的代码罢了,然后把数据转
换成逻辑的结构显示或控制逻辑即可。
对于初学者,或者没有接触过网络编程的程序员,会觉得网络编程涉及的知识很高深,
很难,其实这是一种误解,当你的语法熟悉以后,其实基本的网络编程现在已经被实现的
异常简单了。
三、网络模型图
四、 Socket 入门
4.1、什么是Socket?
Socket 就是为网络服务提供的一种机制。
通讯的两端都有 Sokcet
网络通讯其实就是 Sokcet 间的通讯
数据在两个 Sokcet 间通过 IO 传输。
4.2、TCP与UDP在概念上的区别:
udp:
a 、是面向无连接 , 将数据及源的封装成数据包中 , 不需要建立建立连接
b 、每个数据报的大小在限制 64k 内
c 、因无连接, ,是不可靠协议
d 、不需要建立连接 , 速度快
tcp :
a 、建议连接,形成传输数据的通道 .
b 、在连接中进行大数据量传输,以字节流方式
c 、通过三次握手完成连接 , 是可靠协议
d 、必须建立连接 m 效率会稍低
4.3 、 UDP 协议
通过 UDP 协议
4.3.1.UDP服务器端代码
class UdpServer {
public static void main(String[] args) throws IOException {
System.out.println("udp 接受数据启动.......");
DatagramSocket ds = new DatagramSocket(8080);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
// 阻塞效果
ds.receive(dp);
System.out.println(" 来源: "+dp.getAddress().getHostAddress()+", port: "+dp.getPort());
String str = new String(dp.getData(), 0, dp.getLength()); System.out.println(" 客户端发送数据:" + str); ds.close();
}
}
4.3.2 UDP客户端代码
public class UdpClient {
public static void main(String[] args) throws IOException {
System.out.println("udp 发送数据");
DatagramSocket ds = new DatagramSocket();
String str = " 客户端发送数据....";
byte[] strByte = str.getBytes();
DatagramPacket dp = new DatagramPacket(strByte, strByte.length,
InetAddress.getByName("192.168.1.3"), 8080);
ds.send(dp);
ds.close();
}
}
4.3 .TCP 协议
4.3.3 TCP握手协议
在 P TCP/IP 协议中,P TCP 协议采用三次握手建立一个连接。
第一次握手: : 建立连接时,客户端发送 N SYN 包 (SYN=J) 到服务器,并进入 D SYN_SEND 状态,等
待服务器确认;
第二次握手: : 服务器收到 N SYN 包,必须确认客户的 N SYN ( ACK=J+1 ),同时自己也发送一个 SYN
包( SYN=K ),即 K SYN+ACK 包,此时服务器 V V 状态;
第三次握手: : 客户端收到服务器的 SYN +K ACK 包,向服务器发送确认包 ACK(ACK=K+1) ,此包
发送完毕,客户端和服务器进入 D ESTABLISHED 状态,完成三次握手。
完成三次握手 ,客户端与服务器开始传送数据,
四次分手:
于 由于TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它
的数据发送任务后就能发送一个 FIN 来终止这个方向的连接。收到一个 FIN 只意味着这一
方向上没有数据流动,一个TCP 连接在收到一个 FIN 后仍能发送数据。首先进行关闭的一
方将执行主动关闭,而另一方执行被动关闭。
(1 )客户端 A 发送一个 FIN ,用来关闭客户 A 到服务器 B 的数据传送。
(2 )服务器 B 收到这个 FIN ,它发回一个 ACK ,确认序号为收到的序号加 1 。和 SYN 一样,
一个 N FIN 将占用一 个序号。
(3 )服务器 B 关闭与客户端 A 的连接,发送一个FIN 给客户端 A 。
(4 )客户端 A 发回 ACK 报文确认,并将确认序号设置为收到序号加 1 。
1. 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的 LISTEN 状态下的 SOCKET 当收到 SYN 报文的建连请求后,它可以
把 ACK 和 SYN ( ACK 起应答作用,而 SYN 起同步作用)放在 一个报文里来发送。
但关闭连接时,当收到对方的 FIN 报文通知时,它仅仅表示对方没有数据发送给你了;但未
必你所有的数据都全部发送给对方了,所以你可以 未 必会马上会关闭 SOCKET, 也即你可能
送 还需要发送一些数据给对方之后,再发送 FIN 报文给对方来表示你同意现在可以关闭连接
了,所以它这里的 ACK 报 文和 FIN 报文多数情况下都是分开发送的. .
2 .为什么 TIME_WAIT 状态还需要等 2MSL 后才能返回到 CLOSED 状态?
的 这是因为虽然双方都同意关闭连接了,而且握手的 4 个报文也都协调和发送完毕,按理可
以直接回到 CLOSED 状态(就好比从 SYN_SEND 状态到 ESTABLISH 状态那样);但是因为我们
的 必须要假想网络是不可靠的,你无法保证你最后发送的 ACK 报文会一定被对 方收到,因此
对方处于 LAST_ACK 状态下的 SOCKET 可能会因为超时未收到 ACK 报文,而重发 FIN 报文,
所以这个 TIME_WAIT 状态的作用就是用来重发可能丢失的 ACK 报文。
4.3.1 服务器端代码
public class TcpServer {
public static void main(String[] args) throws IOException {
System.out.println("socket 服务器端启动....");
ServerSocket serverSocket = new ServerSocket(8080);
// 获取客户端对象
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
byte[] buf = new byte[1024];
int len = inputStream.read(buf);
String str = new String(buf, 0, len);
System.out.println("str:" + str);
serverSocket.close();
}
}
4.3.2 客户端代码
System.out.println("socket 启动 ....");
Socket s = new Socket("192.168.1.3", 8080);
OutputStream outputStream = s.getOutputStream();
outputStream.write("我是客戶端 ....".getBytes());
s.close();
标签:Java,Socket,ACK,IP,编程,SYN,服务器,连接,客户端
From: https://www.cnblogs.com/salixleaf/p/17141245.html