软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 互联网 -> SCTP协议详解与实例 -> 正文阅读

[互联网]SCTP协议详解与实例


1.SCTP是什么?


只要是接触过编程的人,当你问他传输层都有哪些协议?我想几乎很多人会说TCP,IP协议而很少有人知道SCTP(流控制传输协议)这个和上述俩个协议具有相同地位的协议。
SCTP提供的服务与TCP,UDP类似,或者甚至可以理解为其是TCP与UDP协议各自优点的组合后的产物。

2.SCTP的特点

(1)SCTP连接的建立


SCTP协议建立连接可调用
int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt);
//或者直接发送消息就可建立连接
int sctp_sendmsg(int s, const void *msg, size_t len, struct sockaddr *to,
         socklen_t tolen, uint32_t ppid, uint32_t flags,
         uint16_t stream_no, uint32_t timetolive, uint32_t context);

介绍完建立连接的接口,那么就来谈谈其连接建立的具体过程,废话不多说,先给出其建立连接过程的示意图
[img]http://img.blog.csdn.net/20160331142614471
SCTP连接建立的过程如上图所示:

(1)客户端给服务器发送一个INIT初始化消息,消息中包含有客户端要告诉服务器自己的IP地址的清单,初始化列号(可以和TCP初始化序列一样来理解),用于表示本关联中的所有分组的其实标志,客户请求以及能够支持流的数目等
(2)服务器给客户端回一个INIT ACK来确认刚收到的消息,并且还有和刚才客户端给服务器发的所有种类的服务器自己的信息,除此之外还会还有一个状态cookie
(3)客户端收到状态cookie之后,给服务器回一个COOKIE ECHO 并且此时还可以在此数据包中包含用户数据
(4)服务器收到客户端给其发的COOKIE ECHO之后在给其发个COOKIE ACK 此时同样也可以携带数据

至此一个SCTP关联就建立成功

(2)SCTP断开连接


SCTP断开连接和TCP的接口相同都是调用shutdown(),但是语义却不同其how参数对SCTP来说都是读写都禁掉
其具体流程如下图
[img]http://img.blog.csdn.net/20160401113847061

SCTP不像TCP那样允许”半关闭的关联”。当一端关闭某个关联时,另一段必须停止发送数据。

(3)多宿主


TCP为客户与服务器之间提供连接,从而可以使双方安全的传送数据,而SCTP将连接改为了”关联”。

要解释清楚为什么SCTP将TCP的连接改为”关联”我打算从他们的bind(绑定接口)来说明原因,先以服务器调用bind为例,
TCP的bind接口如下
int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen);

SCTP的bind接口如下
int sctp_bindx(int sockfd, struct sockaddr *my_addrs, int addrcnt, int flags);

上面给出了TCP和STCP对应的绑定套接字的接口,我们逐一的对比其参数
(1)首先二者的第一个参数sockfd含义相同,都是指待绑定的套接字
(2)bind中的参数my_addr指的是要把哪个插座(IP地址和端口号)绑定到sockfd上,这相当与给了sockfd一个身份,之后网络中就可以唯一的标识该sockfd了,而sctp_bindx中对应位置的参数名变为my_addrs,细心的读者会发现这个参数变为了复数了。没错该参数并不是单一的某个sockaddr结构体的地址,而是sockaddr结构体数组的首地址,参数addrcnt表示该数组中元素的个数。既然相比TCP的bind接口,此处为多个地址,我们也不难理解sctp_bindx所做的工作是将这些地址都绑定到sockfd上。这样其和TCP的第一个不同点就出来了。即TCP的bind之后的socket只有一个身份(只绑定了一个地址),而SCTP bind之后socket有多个身份(绑定了多个地址)。关于flags参数我们之后在补充

上述中我们的俩种协议的服务器调用bind接口有所区别,那么SCTP其对应的客户端建立连接时调用的connect接口也类似稍有不同
sctpconnect接口
int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt);

可以看出SCTP在建立连接时addrs也为一个sockaddr结构体数组

那么SCTP相较TCP的绑定端口和连接时使用了地址数组有什么用意呢?没错这就是我们这里最终想要说明的其多宿主的特性。服务器调用多个地址来标识自己,客户端可以和每一个地址建立一个路径。这样当客户端的哪条路劲失效时,可以自动切换到另一条路径上,应用程序甚至都不必知道发生了故障,这样是不是比起TCP的单一路径安全保障性更好一点呢?

(4)多流解决头端阻塞


还是与TCP做比较,TCP虽然是个全双工协议,但是其读写方向各自只有一个流。一个流很多情况下会发生头端阻塞,其大概意思如下图
[img]http://img.blog.csdn.net/20160331134404783
如上图所示,因为TCP每一个方向上只有一个流,那么当该流中的某个部分的报文丢失之后,其后的所有报文都只能等待丢失部分重传之后,并在重新排序之后方可被送入应用的接受缓冲区。这种协议设定对发送数据有严格顺序要求时还可以接受,但如果对发送数据没有这么强的顺序限制时就显的不那么友好了。
一个具体的场景

当我们的一个web页面要展示好多张图片时,就像上图所示。在这种情况下其实图片1到4之间到达的先后顺序是完全不影响的。此时如果你用TCP这种单项流协议,那么图片一有数据丢失,导致你因此也看不了其实已经发来的图二到图4。如果你改用SCTP这种支持多流的协议,那么你的每张图片发送都使用不同的流号,到时候,当图一对应流号的数据有丢失,那么只有这一个流会等待重传,其他几张图片都不会被其影响,这样我们就可以先看到图2到图4了,完全不受图1丢失的影响。不知道读者此时会不会感觉出来点多流在此种场景下的巨大优势

(5) 面向消息


SCTP是面向消息的协议,不像TCP那样没有包的概念,也意味着消息没有边界,边界只能由应用层来设计和划分,而SCTP一个包就是一个消息,这就大大降低了编程者的难度。其次它的发送接受接口还可以传递消息类型,接口具体如下
int sctp_sendmsg(int s, const void *msg, size_t len, struct sockaddr *to,
         socklen_t tolen, uint32_t ppid, uint32_t flags,
         uint16_t stream_no, uint32_t timetolive, uint32_t context);

上述的发送接口中flags参数就是用来标识消息类型的参数。通过这样直接获取消息类型由可以减少我们不必要的拆包操作,真的是方便之极

(6)一到多特性


SCTP的一到多特性其实和UDP的套接字一样,都能通过一个套接字接受多个消息,这意味着程序员可以不用向TCP那样管理大量的套接字了

3.SCTP常用接口


一下接口我们都是针对一到多形式的SCTP

(1)sctp_bindx端口绑定


该接口用来命名一个套接字
int sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, int flags);
//成功返回0,出错返回1

该接口用来给sd绑定一组地址,或从addrs地址组中添加,删除某个地址。具体操作由flags来控制,由于之前我们以介绍过其他参数,这里值介绍flags
flags 说明
SCTP_BINDX_ADD_ADDR 往套接字里添加地址
SCTP_BINDX_REM_ADDR 从套接字中删除地址

(2)sctp_connectx函数


该接口用于与服务器建立连接
int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt);

参数含义上文有介绍这里不在赘余

(3)sctp_sendmsg


如果之前没有调用sctp_connectx那么第一次调用该接口既负责建立连接也负责发送数据
int sctp_sendmsg(int s, const void *msg, size_t len, struct sockaddr *to,
         socklen_t tolen, uint32_t ppid, uint32_t flags,
         uint16_t stream_no, uint32_t timetolive, uint32_t context);

前5个参数与UDP的sendto参数含义相同,我们在此只讨论后4个

ppid参数指定将随数据块传递的净荷协议
flags参数标识消息类型
stream_no参数标识具体的流号
timetolive指定消息的生命期
context保存消息传输过程中可能产生的上下文

(4)sctp_recvmsg


该接口负责接受数据
int sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from,
         socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo,
         int *msg_flags);

由于前5个参数与UDP的recvfrom相同,在此我们同样也只讨论后几个参数

sinfo保存了消息相关的细节
msg_flags和sctp_sendmsg中的flags相对应

4.SCTP实例代码


下面是用SCTP的一到多实现的一个回显服务器
server.h
#pragma once

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>

#define SERVER_PORT 6666
#define BUFFER_SIZE 1024
#define LISTEN_QUEUE 100

class SctpServer
{
    public:
        SctpServer();
        void start(void);
    private:
        //开启监听socket
        void listenSocket(void);
        //循环处理请求
        void loop(void);

        int sockFd_;                            //用来接受的套接字
        int messageFlags_;                      //消息类型
        char readBuf_[BUFFER_SIZE];             //接受缓冲区
        struct sockaddr_in clientAddr_;         //用来保存客户端地址
        struct sockaddr_in serverAddr_;         //用来保存服务端地址
        struct sctp_sndrcvinfo sri_;            //消息相关细节信息
        struct sctp_event_subscribe events_;    //事件集
        int streamIncrement_;                   //流号
        socklen_t len_;                         //地址长度
        size_t readSize_;                       //读到的大小
};

server.cpp
#include "server.h"
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h>

SctpServer::SctpServer()
    :streamIncrement_(1)
{

}

void SctpServer::listenSocket(void)
{
    //创建SCTP套接字
    sockFd_ = socket(AF_INET,SOCK_SEQPACKET,IPPROTO_SCTP);
    bzero(&serverAddr_,sizeof(serverAddr_));
    serverAddr_.sin_family = AF_INET;
    serverAddr_.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr_.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET,"127.0.0.1",&serverAddr_.sin_addr);   

    //地址绑定
    bind(sockFd_,(struct sockaddr *)&serverAddr_,sizeof(serverAddr_));

    //设置SCTP通知事件(此处只设置了I/O通知事件)
    bzero(&events_,sizeof(events_));
    events_.sctp_data_io_event = 1;
    setsockopt(sockFd_,IPPROTO_SCTP,SCTP_EVENTS,&events_,sizeof(events_));

    //开始监听
    listen(sockFd_,LISTEN_QUEUE);
}

void SctpServer::loop(void)
{
    while(true)
    {
        len_ = sizeof(struct sockaddr_in);
        //从socket读取内容
        readSize_ = sctp_recvmsg(sockFd_,readBuf_,BUFFER_SIZE,
                                 (struct sockaddr *)&clientAddr_,&len_,&sri_,&messageFlags_);
        //增长消息流号
        if(streamIncrement_)
        {
            sri_.sinfo_stream++;
        }
        sctp_sendmsg(sockFd_,readBuf_,readSize_,
                     (struct sockaddr *)&clientAddr_,len_,
                      sri_.sinfo_ppid,sri_.sinfo_flags,sri_.sinfo_stream,0,0);
    }
}

void SctpServer::start(void)
{
    listenSocket();
    loop();
}

main.cpp
#include "server.h"

int main(int argc,char **argv)
{
  SctpServer server;
  server.start();
  return 0;
}

client.h
#pragma once

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define SERVER_PORT 6666
#define MAXLINE     1024

void sctpstr_cli(FILE *fp,int sock_fd,struct sockaddr *to,socklen_t tolen);

class SctpClient
{
    public:
        SctpClient():echoToAll_(0)
        {

        }
        ~SctpClient()
        {
            close(sockFd_);
        }
        //启动客户端
        void start(void)
        {
            makeSocket();
        }

    private:

        void makeSocket(void)
        {
            sockFd_ = socket(AF_INET,SOCK_SEQPACKET,IPPROTO_SCTP);
            bzero(&serverAddr_,sizeof(serverAddr_));
            serverAddr_.sin_family = AF_INET;
            serverAddr_.sin_addr.s_addr = htonl(INADDR_ANY);
            serverAddr_.sin_port = htons(SERVER_PORT);
            inet_pton(AF_INET,"127.0.0.1",&serverAddr_.sin_addr);

            bzero(&events_,sizeof(events_));
            events_.sctp_data_io_event = 1;
            setsockopt(sockFd_,IPPROTO_SCTP,SCTP_EVENTS,&events_,sizeof(events_));
            if(echoToAll_ == 0)
            {
                sctpstr_cli(stdin,sockFd_,(struct sockaddr *)&serverAddr_,sizeof(serverAddr_));
            }
        }

        int sockFd_;
        struct sockaddr_in serverAddr_;
        struct sctp_event_subscribe events_;
        int echoToAll_;
};

//循环发送并接受消息
void sctpstr_cli(FILE *fp,int sock_fd,struct sockaddr *to,socklen_t tolen)
{
    struct sockaddr_in peeraddr;
    struct sctp_sndrcvinfo sri;
    char sendline[MAXLINE];
    char recvline[MAXLINE];
    socklen_t len;
    int out_sz,rd_sz;
    int msg_flags;

    bzero(&sri,sizeof(sri));
    while(fgets(sendline,MAXLINE,fp) != NULL)
    {
        if(sendline[0] != '[')
        {
            printf("ERROR\n");
            continue;
        }
        sri.sinfo_stream = sendline[1] - '0';
        out_sz = strlen(sendline);

        //发送消息
        int count = sctp_sendmsg(sock_fd,sendline,out_sz,to,tolen,0,0,sri.sinfo_stream,0,0);
        len = sizeof(peeraddr);
        rd_sz = sctp_recvmsg(sock_fd,recvline,sizeof(recvline),
                             (struct sockaddr *)&peeraddr,&len,&sri,&msg_flags);
        printf("From str:%d seq:%d (assoc:0x%x):",
                sri.sinfo_stream,sri.sinfo_ssn,(u_int)sri.sinfo_assoc_id);
        printf("%d  %s\n",rd_sz,recvline);
    }
}

client.cpp
#include "client.h"

int main(int argc,char **argv)
{
  SctpClient client;
  client.start();
  return 0;
}

......显示全文...
    点击查看全文


上一篇文章      下一篇文章      查看所有文章
2016-04-01 16:55:16  
互联网 最新文章
C++11并发API总结
16.收款(AcceptingMoney)
数据链路层综述
IP协议及IP数据报解析
《浅谈HTTP协议》
计算机网络基础
LoadRunner和RPT之间关于手动关联和参数化的
HTTPS中的对称密钥加密,公开密钥加密,数字
上班需要打卡吗?(开通微信公众号--乘着风
ofbizjmsactivemq
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年1日历
2018-1-20 7:30:43
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --