一、TCP/IP的结构体系
从上往下依次是:应用层、传输层、网际层、网络接口层
应用层:为网络中的各种网络应用提供服务,在这一层的协议主要有FTP(文件传输协议,端口号为21,用来传输文件)、SMTP(简单邮件传输协议,端口号为25,用来发送邮件)、HTTP(超文本传输协议,端口号为80)、Telent...
传输层:在应用程序的端到端的连接和可靠传输,在这一层的协议主要有TCP、UDP
网际层:保证数据在从源主机到目标主机的可靠传输,在这一层的协议主要有IP(网络互连协议)
网络接口层:保证数据通过物理设备在期间可靠传输,在这一层的协议主要有ARP(地址解析协议,IP地址解析为物理地址)、RARP(反向地址解析协议,是将物理地址解析为IP地址)
二、TCP协议
TCP协议为传输控制协议,提供了可靠的面向连接的服务;传输消息前必须进行连接,结束后释放,可靠、有序、无丢失、无重复;TCP协议采用字节流传输数据
TCP层位于IP层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。
三、TCP报文段格式
下面介绍一下报文段首部各个字段的含义:
源端口号以及目的端口号:
各占2个字节,端口是传输层和应用层的服务接口,用来寻找发送端和接收端的进程,一般来讲,通过端口号和IP地址,可以唯一确定一个TCP连接,在网络编程中,通常被称为一个socket接口。
序号:
占4个字节,用来标识从TCP发送端向TCP接收端发送的数据字节流
确认序号:
占4字节,包含发送确认的一端所期望收到的下一个序号,因此,确认序号应该是上次已经成功收到数据字节序号加1;只有ACK标志位为1时,确认序号字段才有效
数据偏移:
占4位,用于指出TCP首部长度,若不存在选项,则这个值为20字节,数据偏移的最大值是60字节
保留字节:
占6位,暂时可忽略,值全为0
标志位:
URG(紧急) : 为1时表明紧急指针字段有效
ACK(确认):为1时表明确认号字段有效
PSH(推送):为1时接收方应尽快将这个报文段交给应用层
RST(复位):为1时表明TCP连接出现故障必须重建连接
SYN(同步):在连接建立时用来同步序号
FIN (终止): 为1时表明发送端数据发送完毕要求释放连接
接收窗口:
占2个字节,用于流量控制和拥塞控制,表示当前接收缓冲区的大小。在计算机网络中,通常是用接收方的接收能力的大小来控制发送方的数据发送量。TCP连接的一端根据缓冲区大小确定自己的接收窗口值,告诉对方,使对方可以确定发送数据的字节数。
检验和:
占2个字节,范围包括首部和数据两部分
紧急指针:
占2个字节,只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号
四、TCP编程
socket编程叫做套接字,两个主机之间逻辑连接的断点
服务端和客户端的大致流程:
服务端:创建scoket实例->端口绑定(采用bind)->等待客户端的连接(采用accept)->读取客户端发来的消息->关闭
客户端:创建scoket实例->创建和服务端的连接(采用connect)->向服务端发送消息->关闭
public class TCPServer{//服务端
public static void main(String[] args) throws IOException {
//创建serverSocket实例
ServerSocket serverSocket = new ServerSocket();
//端口绑定
serverSocket.bind(new InetSocketAddress(6667));
//接收客户端的连接,每有一个客户端连接都会创建一个socket实例
Socket socket = serverSocket.accept();
//从客户端读取发送过来的消息
BufferedInputStream stream = new BufferedInputStream(socket.getInputStream());
byte[] bytes = new byte[1024];
int read = stream.read(bytes, 0, 1024);
String msg = new String(bytes, 0, read);
System.out.println("server:"+msg);
//给客户端回复消息
BufferedOutputStream outputStream = new BufferedOutputStream(socket.getOutputStream());
String result="ACK:"+msg;
outputStream.write(result.getBytes());
outputStream.flush();
//关闭操作
socket.close();
serverSocket.close();
}
}
public class TCPCline{
public static void main(String[] args) throws IOException {
//创建一个socket实例
Socket socket = new Socket();
//和服务端进行连接
socket.connect(new InetSocketAddress("127.0.0.1",6667));
//向服务端发送消息
BufferedOutputStream outputStream = new BufferedOutputStream(socket.getOutputStream());
String msg="hello";
outputStream.write(msg.getBytes());
outputStream.flush();
//接收服务端发送过来的消息
InputStreamReader streamReader = new InputStreamReader(socket.getInputStream());
BufferedReader bufferedReader = new BufferedReader(streamReader);
String result=bufferedReader.readLine();
System.out.println(result);
//关闭操作
outputStream.close();
socket.close();
}
}
五、UDP
UDP:用户数据包协议,它是TCP/IP协议簇中无连接的运输协议。
UDP协议格式
UDP的可靠性如何保证?
UDP实现ACK机制,发送一个数据包给接收方,当接收方接收到消息,接收方再给发送方发送一个ACK消息,表示收到数据。如果发送方在一定时间内未收到ACK消息,则将消息在此发送。
TCP与UDP的区别?
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP对系统资源要求较多,UDP对系统资源要求较少。
TCP的可靠性如何保证?
1、将数据截断为合理的长度
应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的数据报长度将保持不变。
2、超时重发
当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
3、对于收到的请求,给出确认响应
当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒 。 (之所以推迟,可能是要对包做完整校验)
4、 校验出包有错,丢弃报文段,不给出响应,TCP发送数据端,超时时会重发数据
TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。
5、 对失序数据进行重新排序,然后才交给应用层
既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
6、对于重复数据,能够丢弃重复数据
既然IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。
7、TCP可以进行流量控制,防止较快主机致使较慢主机的缓冲区溢出
TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。TCP使用的流量控制协议是可变大小的滑动窗口协议。
UDP编程
服务端:
public class UDPServer2_21 {
public static void main(String[] args) throws SocketException{
DatagramSocket ds = new DatagramSocket();
String msg="Hello";
DatagramPacket dp = new DatagramPacket(msg.getBytes(), msg.length(), new InetSocketAddress("127.0.0.1", 666));
try {
ds.send(dp);//发送消息
} catch (IOException e) {
e.printStackTrace();
}
ds.close();
}
}
客户端:
public class UDPClient {
public static void main(String[] args) throws SocketException {
//创建实例,并绑定端口号
DatagramSocket socket = new DatagramSocket(666);
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, 1024);
try {
socket.receive(datagramPacket);
} catch (IOException e) {
e.printStackTrace();
}
String string=new String(bytes,0,1024);
System.out.println(">>>"+string);
socket.close();
}
}