TCP UDP 学习

转载:https://blog.csdn.net/oyhy_/article/details/70201855

1、摘要

tcp是面向连接的,可靠的字节流协议。因此,在传输数据之前通信双方必须建立一个tcp连接,建立tcp连接需要在服务器和客户端之间进行三次握手。通信双方数据传输完毕之后连接释放,释放连接需要在通信双方之间进行四次挥手。

 

2、tcp协议端口的连接状态

1)、LISTENING,提供某种服务,侦听远方tcp端口的连接请求,当提供的服务没有被连接时,处于LISTENING状态,端口是开放的,等待被连接

2)、SYN_SENT(客户端状态)

客户端调用connect,发送SYN请求简历一个连接,在发送连接请求后等待匹配连接请求,此时状态为SYN_SENT.

3)、SYN_RECEIVED(服务端状态)

在收到和发送一个连接请求后,等待对方对连接请求的确认,当服务器收到客户端发送的同步信号时,将标志位ACK和SYN置为1发送给客户端,此时服务器端处于SYN_RECEIVED状态,如果连接成功了就变为ESTABLISHED,正常情况下SYN_RECEIVED非常短暂.

4)、ESTABLISHED

ESTABLISHED状态表示两台机器正在传输数据。

5)、FIN-WAIT-1

等待远程TCP连接中断请求,或先前的连接中断请求的确认,主动关闭端应用程序调用close,TCP发出FIN请求主动关闭连接,之后进入FIN-WAIT-1状态。

6)、FIN-WAIT-2

从远程tcp等待连接中断请求,主动关闭端接到ack后,就进入了FIN-WAIT-2,这是在关闭连接时,客户端和服务端两次握手之后的状态,是著名的半关闭状态了,在这个状态下,应用程序还有接收数据的能力,但是已经无法发送数据,但是也有一种可能是,客户端一直处于FIN_WAIT_2状态,而服务器一直处于WAIT_CLOSE状态,而直到应用层来决定关闭这个状态。

7)、CLOSE-WAIT

等待从本地用户发来的连接中断请求,被动关闭端TCP接到FIN后,就发出ACK以回应FIN请求

8)、CLOSING

等待远程TCP对连接中断的确认,处于此种状态比较少见。

9)、LAST_ACK

等待原来的发向远程TCP的链接中断请求的确认。被动关闭端一段时间后,接收到文件结束符的应用程序将调用CLOSE关闭连接,TCP也发送一个 FIN,等待对方的ACK.进入LAST-ACK。

10)TIME-WAIT

  在主动关闭端接收到FIN后,TCP就发送ACK包,并进入TIME-WAIT状态,等待足够的时间以确保远程TCP接收到连接中断请求的确认,很大程度上保证了双方都可以正常结束,但是也存在问题,须等待2MSL时间的过去才能进行下一次连接。

11)、CLOSED

  被动关闭端在接受到ACK包后,就进入了closed的状态,连接结束,没有任何连接状态。

2、为什么连接要三次握手

不管三次握手还是四次握手,这是保证信息来回两个链路可达(信息能从A到B,也能从B到A)的最低要求。

举例:

A:你好我是A,你能听到我讲话吗?

B:听到了,我是B,你能听到我讲话吗?

A:恩,听到了。

建立连接,开始聊天!

 

3、为什么终止连接要四次挥手

a、当主机A确认发送完数据且知道B已经接收完了,想要关闭发送数据接口,就会发FIN给主机B

b、主机B收到A发送的FIN,表示收到了,就会发送ACK回复。

c、但是B可能还在发送数据,没有想要关闭数据接口的意思,所以FIN与ACK不是同时发送的,而是等到B数据发送完了,才会发送FIN给主机A。

d、A收到B发来的FIN,知道B的数据也发送完了,回复ACK,A等2msl以后,没有收到B传来的任何消息,知道B已经收到自己的ACK了,A就关闭连接,B也关闭连接了。

A为什么等待2msl?

在client发送出最后的ack回复,但该ack可能会丢失,server如果没有收到ack,将不断的重复发送FIN给client.所以client不能立即关闭,它必须确认server接收到了该ack,client会在发送出ack之后进入到TIME_WAIT状态。client会设置一个计时器,等待2msl的时间。如果再该时间内再次收到FIN,那么clienti会重发ack并再次等待2msl,client都没有再次收到fin,那么client推断ack已经被成功接收,则结束tcp连接。

举例:

A:喂,我不说了 A->FIN_WAIT1

B:我知道了,等下,上一句还没说完,balabala B->ClOSE_WAIT | a->ESTABLISHED 

B:好了,我也说完了,我不说了 B->LAST_ACK

A;我知道了 A->TIME_WAIT | B->CLOSED

A等待2msl后,保证B收到了消息,否则重说一次“我知道了”,A->CLOSED

 

TCP和UDP的区别和优缺点

1、区别总结

1)、tcp是面向连接的(如打电话要先建立连接);udp是无连接的,即发送数据前不需要建立连接。

2)、tcp提供可靠的服务,也就是说,通过tcp连接传送的数据,无差错,不丢失,不重复,且按序到达;udp尽最大努力交付,即不保证可靠交付,tcp通过校验和重传控制,序号标识,滑动窗口、确认应该实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。

3)、udp具有较好的实时性,工作效率比tcp高,适用于对高速传输和实时性较高的通信或广播通信。

4)、每一条TCP链接只能是点到点的;udp支持一对一,一对多,多对一和多对多的交互通信。

5)、tcp对系统资源要求较多,udp对系统资源要求较少。

6)、TCP和UDP是OSI模型中的运输层中的协议。TCP提供可靠的通信传输,而UDP则常被用于让广播和细节控制交给应用的通信传输。

 

2、为什么udp有时比tcp更有优势

udp以其简单、传输快的优势,在越来越多的场景下取代了tcp,如实时游戏。

1)、网速的提升给UDP的稳定性提供可靠网络保障,丢包率很低,如果使用应用层重传,能够确保传输的可靠性。

2)、TCP为了实现网络通信的可靠性,使用了复杂的拥塞控制算法,建立了繁琐的握手过程,由于tcp内置的系统协议栈中,极难对其进行改进。

采用tcp一旦发生丢包,tcp会将后续的包缓存起来,等前面的包重传并接收到后再继续发送,延时会越来越大,基于udp对实时性要求较为严格的情况下,采用自定义重传机制,能够把丢包产生的延迟降到最低,尽量减少网络问题对游戏性造成影响。

3、udp与tcp的变成步骤不同

tcp编程的服务器端一般步骤是:

1)、创建一个socket,用函数socket();

2)、设置socket属性

3)、绑定ip地址,端口等信息到socket上

4)、开启监听

5)、接收客户端上来的连接

6)、收发数据

7)、关闭网络连接

8)、关闭监听

package com.hsx.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServerDemo2{
    public static void main( String[] args ){

        try{
            ServerSocket serverSocket = new ServerSocket( 10002 );
            while( true ){
                Socket socket = null;
                BufferedReader reader = null;
                PrintWriter writer = null;
                try{
                    socket = serverSocket.accept();
                    reader = new BufferedReader( new InputStreamReader( socket.getInputStream(), "UTF-8" ) );
                    String line = reader.readLine();
                    while( line != null ){
                        System.out.println( line );
                        writer = new PrintWriter( socket.getOutputStream(), true );
                        writer.println( "i received your second msg" );
                        if( "second".equals( line ) ){
                            break;
                        }
                        line = reader.readLine();
                    }
                    // System.out.println( line );
                   // writer = new PrintWriter( socket.getOutputStream(), true );
                  //  writer.println( "i received your msg" );
                }
                catch( IOException e ){
                    e.printStackTrace();
                }
                finally{
                    try{
                        if( socket != null ){
                            socket.close();
                        }
                        if( reader != null ){
                            reader.close();
                        }
                        if( writer != null ){
                            writer.close();
                        }
                    }
                    catch( IOException e ){
                        e.printStackTrace();
                    }
                }
            }
        }
        catch( IOException e ){
            e.printStackTrace();
        }

    }
}

tcp变成的客户端

1)、创建一个socket  

2)、设置socket属性   

3)、绑定ip地址,端口等信息

4)、设置要连接的对方的ip地址和端口等属性  

5)、连接服务器

6)、收发数据   

7)、关闭网络连接

package com.hsx.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * Created by 58 on 2018/9/21.
 */
public class SocketClientDemo implements Runnable{
    private Socket socket;
    BufferedReader reader;
    private PrintWriter writer;
    private String msg;

    public SocketClientDemo( String msg ){
        try{
            this.msg = msg;
            socket = new Socket( "10.9.194.47", 10002 );
        }
        catch( IOException e ){
            e.printStackTrace();
        }
    }

    @Override
    public void run(){
        try{

            String line, buffer = "";
            writer = new PrintWriter( socket.getOutputStream(), true );
            writer.println( msg );
            writer.println( "first" );
            reader = new BufferedReader( new InputStreamReader( socket.getInputStream(), "UTF-8" ) );
            int num = 0;
            while( ( line = reader.readLine() ) != null ){
                buffer += line;
                System.out.println( line );
                if( num++ == 1 ){
                    writer.println( "second" );
                }
            }
        }
        catch( Exception e ){
            e.printStackTrace();
        }
        finally{
            try{
                if( socket != null ){
                    socket.close();
                }
                if( reader != null ){
                    reader.close();
                }
                if( writer != null ){
                    writer.close();
                }
            }
            catch( IOException e ){
                e.printStackTrace();
            }
        }
    }

    public static void main( String[] args ){
        new Thread( new SocketClientDemo( "client msg 1" ) ).start();
      /*  new Thread( new SocketClientDemo("client msg 2") ).start();
        new Thread( new SocketClientDemo("client msg 3") ).start();*/
       /* for( int i = 0; i < 100; i++ ){
            new Thread( new SocketClientDemo( "client msg " + i ) ).start();
        }*/

    }
}

UDP编程服务端的步骤:

1)、创建一个socket

2)、设置socket属性

3)、绑定ip地址、端口信息等到socket上

4)、循环接收数据

5)、关闭网络连接

package com.hsx.socket;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

/**
 * 基于udp的socket server
 */
public class UDPServer{
    public static void main( String[] args ){
        try{
            //1、创城奖datagramsocket 指定端口
            DatagramSocket datagramSocket = new DatagramSocket( 10002 );
            //2、创建数据报,用户接收客户端发送的数据
            //2.1、创建字节数据,指定接收的数据报的大小
            byte[] data = new byte[ 1024 ];
            DatagramPacket datagramPacket = new DatagramPacket( data, data.length );
            //3、接收客户端发送的数据
            System.out.println( "服务端已经开启,等待客户端的连接" );
            //此方法在接收到数据之前会一直阻塞
            datagramSocket.receive( datagramPacket );
            //4、读取客户端发送的数据
            //参数:data要转换的数组0从数组的下边0开始 datagrampacket.getlength()长度为接收到的长度
            String info = new String( data, 0, datagramPacket.getLength() );
            System.out.println( "这是服务器,客户端发来的消息是:" + info );

            /**
             * 向客户端进行响应
             */

            //1、定义客户端的地址、端口、数据
            //1.1、获取客户端的ip地址
            InetAddress inetAddress = datagramPacket.getAddress();
            //获取客户端端口号
            int port = datagramPacket.getPort();
            //将要响应的内容保存到byte数组中
            byte[] data2 = "welcome".getBytes();
            //2、创建数据报,包含响应的数据信息
            DatagramPacket datagramPacket1 = new DatagramPacket( data2, data2.length, inetAddress, port );
            //3、响应客户端
            datagramSocket.send( datagramPacket1 );
            //4、关闭资源
            datagramSocket.close();
            System.out.println( "服务已关闭。" );
        }
        catch( SocketException e ){
            e.printStackTrace();
        }
        catch( IOException e ){
            e.printStackTrace();
        }
    }
}

UDP编程客户端的步骤是:

1)、创建一个socket

2)、设置socket属性

3)、绑定ip地址,端口等新到到socket上

4)、设置对方的ip地址和端口等属性

5)、发送数据

6)、关闭网络连接

package com.hsx.socket;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * udp client
 */
public class UDPClient{
    public static void main( String[] args ){
        //1、定义服务器地址、端口号、数据
        try{
            InetAddress inetAddress = InetAddress.getByName( "10.9.194.47" );
            int port = 10002;
            byte[] data = "username:shaoxing;password:123".getBytes();
            //2、创建数据报,包含发送的信息
            DatagramPacket datagramPacket = new DatagramPacket( data, data.length, inetAddress, port );
            //3、创建DatagramSocket对象
            DatagramSocket datagramSocket = new DatagramSocket();
            //4、向服务器发送数据报
            datagramSocket.send( datagramPacket );

            /**
             * 客户端接收服务端响应信息
             */
            //1、创建数据报,用于接收服务器端响应的数据,数据保存到字节数组中
            byte[] data2 = new byte[ 1024 ];
            DatagramPacket datagramPacket1 = new DatagramPacket( data2, data2.length );
            //2、接收服务器响应的数据
            datagramSocket.receive( datagramPacket1 );
            //3、读取数据
            String reply = new String( data2, datagramPacket1.getLength() );
            System.out.println( "这是客户端,服务端发来的消息是:" + reply );
            //4、关闭资源
            datagramSocket.close();

        }
        catch( Exception e ){
            e.printStackTrace();
        }

    }
}

参考资料:https://blog.csdn.net/xiaobangkuaipao/article/details/76793702

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值