UNIX网络编程——基本SCTP套接字编程

1、一到一形式:(与TCP的典型用法几乎一样)

       一到一形式套接字是一个类型为SOCK_STREAM,协议为IPPROTO_SCTP的网际套接字。

UNIX网络编程——基本SCTP套接字编程_第1张图片


2、一到多形式:

       服务器程序无需管理大量的套接字描述符,单个套接字描述符将代表多个关联。在一到多形式的套接字上,用“关联标识”来标识单个连接。

       一到多形式套接字是一个类型为SOCK_SEQPACKET,协议为IPPROTO_SCTP的网际套接字。

UNIX网络编程——基本SCTP套接字编程_第2张图片

UNIX网络编程——基本SCTP套接字编程_第3张图片


3、SCTP一到多式流回射客户/服务器


(1)SCTP一到多式流分回射服务器

stcp_sndrcvinfo结构体:(UNIX网络编程P175)


struct sctp_sndrcvinfo
{
	u_int16_t  sinfo_stream;            //指定新的默认流,所有外出消息将被发送到该流中
	u_int16_t  sinfo_ssn;                  
	u_int16_t  sinfo_flags;                
	u_int32_t  sinfo_ppid;                //置于所有外出消息的SCTP净荷协议标识字段的默认值
	u_int32_t  sinfo_context;           //指定新的默认上下文,用于检索无法发送到对端的消息
	u_int32_t  sinfo_timetolive;       //指定新的默认生命周期
	u_int32_t  sinfo_tsn;                 
	u_int32_t  sinfo_cumtsn;         
	sctp_assoc_t sinfo_assoc_id;    
}

#include	"unp.h"

int
main(int argc, char **argv)
{
	int sock_fd,msg_flags;
	char readbuf[BUFFSIZE];
	struct sockaddr_in servaddr, cliaddr;
	struct sctp_sndrcvinfo sri;
	struct sctp_event_subscribe evnts;     //SCTP通知结构p176
	int stream_increment=1;      //决定增长外来消息的流号
	socklen_t len;
	size_t rd_sz;

	if (argc == 2)
		stream_increment = atoi(argv[1]);
        sock_fd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);    //创建一到多式的SCTP套接字

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);      //捆绑通配地址,表示将在建立的任何关联中使用所有可用的本地地址
	servaddr.sin_port = htons(SERV_PORT);

	Bind(sock_fd, (SA *) &servaddr, sizeof(servaddr));
	
	bzero(&evnts, sizeof(evnts));
	evnts.sctp_data_io_event = 1;                    //仅仅预定sctp_data_io_event,开启每次recvmsg调用返回sctp_sndrcvinfo
	Setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS,   //设置SCTP_EVENTS,修改SCTP套接字的通知预定
		   &evnts, sizeof(evnts));

	Listen(sock_fd, LISTENQ);

	for ( ; ; ) {
		len = sizeof(struct sockaddr_in);
		rd_sz = Sctp_recvmsg(sock_fd, readbuf, sizeof(readbuf),
			     (SA *)&cliaddr, &len,
			     &sri,&msg_flags);           //阻塞在等待来自任何一个远程对端的消息之上

		if(stream_increment) {      //如果设置了stream_increment标识变量
			sri.sinfo_stream++;
			if(sri.sinfo_stream >= sctp_get_no_strms(sock_fd,(SA *)&cliaddr, len)) 
				sri.sinfo_stream = 0;
		}

		Sctp_sendmsg(sock_fd, readbuf, rd_sz, 
			     (SA *)&cliaddr, len,
			     sri.sinfo_ppid,
			     sri.sinfo_flags,
			     sri.sinfo_stream,
			     0, 0);         //使用来自sri结构的净荷协议ID、标识以及可能改动过的流号发送回消息本身
	}
}


(2)SCTP一到多式流分回射客户程序:

#include	"unp.h"

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], 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] != '[') {       // 检测用户输入是否符合[#]text
            printf("Error, line must be of the form '[streamnum]text'\n");
            continue;
        }
        sri.sinfo_stream = strtol(&sendline[1],NULL,0);   //客户把用户在输入请求中的请求的流号转换成sri结构的sinfo_stream
        out_sz = strlen(sendline);
        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),
                 (SA *)&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("%.*s",rd_sz,recvline);
    }
}

#define    SCTP_MAXLINE    800

void  sctpstr_cli_echoall(FILE *fp, int sock_fd, struct sockaddr *to, socklen_t tolen)
{
    struct sockaddr_in peeraddr;
    struct sctp_sndrcvinfo sri;
    char sendline[SCTP_MAXLINE], recvline[SCTP_MAXLINE];
    socklen_t len;
    int rd_sz,i,strsz;
    int msg_flags;

    bzero(sendline,sizeof(sendline));
    bzero(&sri,sizeof(sri));            // 初始化用于建立各个流的sri结构,客户的数据发送和数据接收将通过这些流进行
    while (fgets(sendline, SCTP_MAXLINE - 9, fp) != NULL) 
        {
        strsz = strlen(sendline);
        if(sendline[strsz-1] == '\n') {
            sendline[strsz-1] = '\0';
            strsz--;
        }
        for(i=0;i 2) {
		printf("Echoing messages to all streams\n");
		echo_to_all = 1;
	}
        sock_fd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);
	Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);     //把通过命令行传递的服务器地址从表达式转换成数值格式

	bzero(&evnts, sizeof(evnts));
	evnts.sctp_data_io_event = 1;                    //仅仅预定sctp_data_io_event,开启每次recvmsg调用返回sctp_sndrcvinfo
	Setsockopt(sock_fd,IPPROTO_SCTP, SCTP_EVENTS,
		   &evnts, sizeof(evnts));               //设置SCTP_EVENTS,修改SCTP套接字的通知预定

	if(echo_to_all == 0)
		sctpstr_cli(stdin,sock_fd,(SA *)&servaddr,sizeof(servaddr));
	else
		sctpstr_cli_echoall(stdin,sock_fd,(SA *)&servaddr,sizeof(servaddr));
	Close(sock_fd);
	return(0);
}


你可能感兴趣的:(Linux)