基于 Socket 的 UDP 和 TCP 编程介绍

一、概述

TCP(传输控制协议)和UDP(用户数据报协议)是网络体系结构TCP/IP模型中传输层一层中的两个不同的通信协议。

TCP:传输控制协议,一种面向连接的协议,给用户进程提供可靠的全双工的字节流,TCP套接口是字节流套接口(stream socket)的一种。

UDP:用户数据报协议。UDP是一种无连接协议。UDP套接口是数据报套接口(datagram socket)的一种。

二、TCP和UDP介绍


1)基于TCP的Socket通信流程

基于 Socket 的 UDP 和 TCP 编程介绍_第1张图片
基于TCP的Socket通信流程

三路握手

1.客户端发送一个SYN段(同步序号)指明客户打算连接的服务器端口,以及初始化序号(ISN) 。

2.服务器发回包含服务器的初始序号的SYN报文段作为应答。同时,将确认序号(ACK)设置为客户的ISN加1以对客户的SYN 报文段进行确认。一个SYN将占用一个序号。

3.客户必须将确认序号设置为服务器的ISN加1以对服务器的SYN报文段进行确认


基于 Socket 的 UDP 和 TCP 编程介绍_第2张图片
TCP的三路握手

2) 基于UDP的Socket通信流程


基于 Socket 的 UDP 和 TCP 编程介绍_第3张图片
基于UDP的Socket通信流程


3) UDP和TCP的对比

从上面的流程图比较我们可以很明显的看出UDP没有三次握手过程。简单点说。UDP处理的细节比TCP少。UDP不能保证消息被传送到(它也报告消息没有传送到)目的地。UDP也不保证数据包的传送顺序。UDP把数据发出去后只能希望它能够抵达目的地。

也就是说,当报文发送之后,是无法得知其是否安全完整到达的

TCP优缺点:

优点:

        1.TCP提供以认可的方式显式地创建和终止连接。

        2.TCP保证可靠的、顺序的(数据包以发送的顺序接收)以及不会重复的数据传输。

        3.TCP处理流控制。

        4.允许数据优先

        5.如果数据没有传送到,则TCP套接口返回一个出错状态条件。

        6.TCP通过保持连续并将数据块分成更小的分片来处理大数据块。-------无需程序员知道

缺点: 

        TCP在转移数据时必须创建(并保持)一个连接。这个连接给通信进程增加了开销,让它比UDP速度要慢。

UDP优缺点:

        1.UDP不要求保持一个连接

        2.UDP没有因接收方认可收到数据包(或者当数据包没有正确抵达而自动重传)而带来的开销。

        3.设计UDP的目的是用于短应用和控制消息

        4.在一个数据包连接一个数据包的基础上,UDP要求的网络带宽比TDP更小。

        5.UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点

三、Socket编程

常用的Socket类型有两种:

        流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。

        流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;

        数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。

socket调用库函数主要有:

        创建套接字

                Socket(af,type,protocol)

        建立地址和套接字的联系

                bind(sockid, local addr, addrlen)

        服务器端侦听客户端的请求

                listen( Sockid ,quenlen)


        建立服务器/客户端的连接 (面向连接TCP)

        客户端请求连接

                Connect(sockid, destaddr, addrlen)

        服务器端等待从编号为Sockid的Socket上接收客户连接请求

                newsockid=accept(Sockid,Clientaddr, paddrlen)

        

       发送/接收数据

        发送数据(面向连接TCP)

                send(sockid, buff, bufflen)

        接收收据(面向连接TCP)

                recv( )

        发送数据(面向无连接UDP)

                sendto(sockid,buff,…,addrlen)

        接收收据(面向无连接UDP)

                recvfrom( )

        

        释放套接字

                close(sockid)


TCP/IP应用编程接口(API)

服务器的工作流程:

1.调用socket函数创建一个Socket

    Socket(af,type,protocol)

2.调用bind函数将其与本机地址以及一个本地端口号绑定

    bind(sockid, local addr, addrlen)

3.调用listen在相应的socket上监听

    listen( Sockid ,quenlen)

4.调用accpet接收客户端连接请求,accpet方法将生成一个新的socket

   newsockid=accept(Sockid,Clientaddr, paddrlen)

5.通过recv方法接收来自客户端的数据(可选)

    recv( )

6.通过send方法发送给客户端数据

    send(sockid, buff, bufflen)

7.最后关闭该socket

    close(sockid)

=============================================================

main()

{

    //sock_fd:服务器端监听socket;client_fd:客户端数据传输socket 

    int sock_fd, client_fd; 

    //服务器地址信息

    struct sockaddr_in ser_addr;             

    //客户端地址信息

    struct sockaddr_in cli_addr;

    //缓冲区

    char msg[MAX_MSG_SIZE];     

    //1.创建服务器端连接的SOCKET

    ser_sockfd = socket( AF_INET, SOCK_STREAM, 0 ); 

    if ( ser_sockfd < 0 )   //创建失败

    {

        fprintf( stderr, "socker Error:%sn", strerror( errno ) );

        exit( 1 );

    }

    // 初始化服务器地址*

    addrlen = sizeof(struct sockaddr_in);

    bzero( &ser_addr, addrlen );

    ser_addr.sin_family = AF_INET;

    ser_addr.sin_addr.s_addr = htonl( INADDR_ANY );

    ser_addr.sin_port = htons( SERVER_PORT );

    //2.调用bind函数将其与服务器地址以及一个服务器端口号绑定

    if ( bind( ser_sockfd, (struct sockaddr *) &ser_addr, sizeof(struct sockaddr_in) ) < 0 )

    {      //绑定失败 

        fprintf( stderr, "Bind Error:%sn", strerror( errno ) );

        exit( 1 );

    }

    //3.侦听客户端请求

    if ( listen( ser_sockfd, BACKLOG ) < 0 )

    { //绑定失败

        fprintf( stderr, "Listen Error:%sn", strerror( errno ) );

        //绑定失败的画,要释放服务器端的Socket连接

        close( ser_sockfd );

        exit( 1 );

    }

    while ( 1 )

    {                                                   

        //4.等待接收客户连接请求

        cli_sockfd = accept( ser_sockfd, (struct sockaddr *) &cli_addr, &addrlen );

        if ( cli_sockfd <= 0 )  //

        {

            fprintf( stderr, "Accept Error:%sn", strerror( errno ) );

        }  else  {   //开始服务

             //5.接受数据

            recv( cli_addr, msg, MAX_MSG_SIZE, 0 );

            printf( "received a connection from %sn", inet_ntoa( cli_addr.sin_addr ) );

            printf( "%sn", msg );                  /*在屏幕上打印出来 */

            strcpy( msg, "hi,I am server!" );

            //6.发送的数据

            send( cli_addr, msg, sizeof(msg), 0 ); 

            close( cli_addr );

        }

    }

    //7.程序结束关闭服务器端Socket

    close( ser_sockfd );

}

=============================================================

客户端的工作流程:

1.调用socket函数创建一个Socket

    Socket(af,type,protocol)

2.请求连接服务器

    connect(sockid, destaddr, addrlen)

3.通过socket向服务器端发送请求

       send(sockid, buff, bufflen)

4.接收服务器端传送回来的消息和结果(可选)

    recv( )

5.最后关闭该socket。

    close(sockid)

=============================================================

int GetServerAddr( char * addrname )

{

    printf( "please input server addr:" );

    scanf( "%s", addrname );

    return(1);

}



main()

{

    //客户端SOCKET

    int cli_sockfd;

    int addrlen;

    //服务器的地址

    char seraddr[14];

    //服务器的地址

    struct sockaddr_in ser_addr ;

    //缓冲区

    char msg[MAX_MSG_SIZE];

    GetServerAddr( seraddr );

    //1.创建连接的SOCKET

    cli_sockfd = socket( AF_INET, SOCK_STREAM, 0 );

    if ( ser_sockfd < 0 ) //创建失败

    {

        fprintf( stderr, "socker Error:%sn", strerror( errno ) );

        exit( 1 );

    }

    //初始化服务器地址

    addrlen = sizeof(struct sockaddr_in);

    bzero( &ser_addr, addrlen );

    ser_addr.sin_family = AF_INET;

    ser_addr.sin_addr.s_addr = inet_addr( seraddr );

    ser_addr.sin_port = htons( SERVER_PORT );

    //2.请求连接服务器

    if ( connect( cli_sockfd, (struct sockaddr *) &ser_addr, &addrlen ) != 0 )

    {

        //连接失败

        fprintf( stderr, "Connect Error:%sn", strerror( errno ) );

        close( cli_sockfd );

        exit( 1 );

    }

    strcpy( msg, "hi,I am client!" );

    //3.发送数据

    send( sockfd, msg, sizeof(msg), 0 );   

    //4.接受数据

    recv( sockfd, msg, MAX_MSG_SIZE, 0 );

    printf( "%sn", msg );                  /*在屏幕上打印出来 */

    //5.关闭socket。

    close( cli_sockfd );

}

=============================================================

3、UDP/IP应用编程接口(API)

服务器的工作流程:

1.调用socket函数创建一个Socket

    Socket(af,type,protocol)

2.调用bind函数将socket与本机地址以及一个本地端口号绑定

    bind(sockid, local addr, addrlen)

3.接收客户端的数据

    recvfrom( )

4.给客户端发送数据(可选)

    sendto(sockid,buff,…,addrlen)

5.最后关闭该socket。

    close(sockid)

=============================================================

int main( int argc, char **argv )

{

    int ser_sockfd;

    int len;

    socklen_t addrlen;

    char seraddr[100];

    struct sockaddr_in ser_addr;

    //1.建立socke

    ser_sockfd = socket( AF_INET, SOCK_DGRAM, 0 );

    if ( ser_sockfd < 0 )

    {

        printf( "I cannot socket successn" );

        return(1);

    }

    //填写sockaddr_in 结构

    addrlen = sizeof(struct sockaddr_in);

    bzero( &ser_addr, addrlen );

    ser_addr.sin_family = AF_INET;

    ser_addr.sin_addr.s_addr = htonl( INADDR_ANY );

    ser_addr.sin_port = htons( SERVER_PORT );

    //2.将socket与本机地址以及一个本地端口号绑定

    if(bind(ser_sockfd,(struct sockaddr *)&ser_addr,addrlen)<0)

      {

          printf("connect");

          return 1;

    }

    while(1)

      {

          bzero(seraddr,sizeof(seraddr));

          //3.接收客户端的数据

            len=recvfrom(ser_sockfd,seraddr,sizeof(seraddr),0,(struct sockaddr*)&ser_addr,&addrlen);

          //显示client端的网络地址

        printf( "receive from %sn", inet_ntoa( ser_addr.sin_addr ) );

        //显示客户端发来的字串

        printf( "recevce:%s", seraddr );

        //4.给客户端发送数据,这是是将字串返回给client端

        sendto( ser_sockfd, seraddr, len, 0, (struct sockaddr *) &ser_addr, addrlen );

    }

}

=============================================================

客户端的工作流程:

1.调用socket函数创建一个Socket,填写服务器地址及端口号

    Socket(af,type,protocol)

2.传送数据给服务器端

    sendto(sockid,buff,…,addrlen)

3.接收服务器端返回的数据(可选)

    recvfrom()

4.最后关闭该socket

    close(sockid)

int GetServerAddr( char * addrname )

{

    printf( "please input server addr:" );

    scanf( "%s", addrname );

    return(1);

}

int main( int argc, char **argv )

{

    int cli_sockfd;

    int len;

    socklen_t addrlen;

    char seraddr[14];

    struct sockaddr_in cli_addr;

    char buffer[256];

    GetServerAddr( seraddr );

    // 1.建立socket

    cli_sockfd = socket( AF_INET, SOCK_DGRAM, 0 );

    if ( cli_sockfd < 0 )

    {

        printf( "I cannot socket successn" );

        return(1);

    }

    //填写sockaddr_in

    addrlen = sizeof(struct sockaddr_in);

    bzero( &cli_addr, addrlen );

    cli_addr.sin_family = AF_INET;

    //建立和服务器的连接

    cli_addr.sin_addr.s_addr = inet_addr( seraddr );

    cli_addr.sin_port = htons( SERVER_PORT );

    bzero( buffer, sizeof(buffer) );

    // 从标准输入设备取得字符串

    len = read( STDIN_FILENO, buffer, sizeof(buffer) );

    //2. 将字符串传送给server端

    sendto( cli_sockfd, buffer, len, 0, (struct sockaddr *) &cli_addr, addrlen );

    //3. 接收server端返回的字符串

    len = recvfrom( cli_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *) &cli_addr, &addrlen );

    printf( "receive: %s", buffer );

    //4.关闭该socket

    close( cli_sockfd );

}

=============================================================

你可能感兴趣的:(基于 Socket 的 UDP 和 TCP 编程介绍)