高级SCTP套接字

一、自动关闭的一到多式服务器程序

依赖客户端关闭关联有时客户端不发送数据,服务器不得不将资源分配给从不使用这些资源的客户,会造成对于SCTP实现的拒绝服务器攻击。

  1. 自动关闭允许SCTP端点指定某个关联可以保持的最大秒数;
  2. 选择的值要适合。
  3. 空闲判断:关联在任何方向都没有用户数据在传输
  4. 设置方式:开启自动关闭功能必须显示使用SCTP_AUTOCLOSE套接字选项;
if(argc == 2)
        stream_increment = atoi(argv[1]);
   sock_fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
   close_time = 120;
   setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &close_time, sizeof(close_time));
   bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

二、部分递送

应用进程要求SCTP传输过大的消息时,SCTP采用部分发送措施。

  1. 所接收消息的缓冲区空间耗用量必须满足或超过某个门槛;
  2. SCTP栈最多只能从该消息开始处顺序递送到首个缺失的断片;
  3. 开启部分传输,其他所有消息必须阻塞到当前消息已被完整地接收并递送给接收端应用进程之后才能开始递送;
  4. SCTP的KAME实现使用的门槛是套接字接收缓冲区的一半大小。
#include	"unp.h"
 
static uint8_t *sctp_pdapi_readbuf=NULL;
static int sctp_pdapi_rdbuf_sz=0;
 
uint8_t *
pdapi_recvmsg(int sock_fd,
	      int *rdlen,
	      SA *from,
	      int *from_len,
	      struct sctp_sndrcvinfo *sri,
	      int *msg_flags)
{
	int rdsz,left,at_in_buf;
	int frmlen=0;
	// 准备缓冲区
	// 若由全局静态指针指向的接收缓冲区尚未分配,则动态分配并设置与其关联的状态
	if (sctp_pdapi_readbuf == NULL) {
		sctp_pdapi_readbuf = (uint8_t *)Malloc(SCTP_PDAPI_INCR_SZ);
		sctp_pdapi_rdbuf_sz = SCTP_PDAPI_INCR_SZ;
	}
	// 读入消息
	// 调用 sctp_recvmsg 读入消息,该消息可能是某个消息的第一个断片
	at_in_buf = Sctp_recvmsg(sock_fd, sctp_pdapi_readbuf, sctp_pdapi_rdbuf_sz,
				 from, from_len,
				 sri, msg_flags);
	// 处理读入错误
	// 若 sctp_recvmsg 返回错误或 EOF 则直接返回
	if(at_in_buf < 1) {
		*rdlen = at_in_buf;
		return(NULL);
	}
	// 继续接收剩余的断片
	// 若消息标志表明 sctp_recvmsg 接收的不是一个完整的消息则继续接收
	while((*msg_flags & MSG_EOR) == 0) {
		// 计算缓冲区中剩余的空间
		left = sctp_pdapi_rdbuf_sz - at_in_buf;
		// 检查是否需要增长缓冲区
		// 当接收缓冲区中剩余空间小于最小的阈值,则调用 realloc 增长缓冲区大小
		if(left < SCTP_PDAPI_NEED_MORE_THRESHOLD) {
			sctp_pdapi_readbuf = realloc(sctp_pdapi_readbuf, sctp_pdapi_rdbuf_sz + SCTP_PDAPI_INCR_SZ);
			if(sctp_pdapi_readbuf == NULL) {
				err_quit("sctp_pdapi ran out of memory");
			}
			sctp_pdapi_rdbuf_sz += SCTP_PDAPI_INCR_SZ;
			left = sctp_pdapi_rdbuf_sz - at_in_buf;
		}
		// 接收剩余断片
		// 调用 sctp_recvmsg 读入本消息剩余断片
		rdsz = Sctp_recvmsg(sock_fd, &sctp_pdapi_readbuf[at_in_buf], 
			     left, NULL, &frmlen, NULL, msg_flags);
		// 增加缓冲区索引,继续一轮循环测试是否已经读入本消息所有的断片
		at_in_buf += rdsz;
	}
	// 返回获取的消息数据
	*rdlen = at_in_buf;
	return(sctp_pdapi_readbuf);
}



//服务器main()函数的使用:
for(;;){
len = sizeof(struct sockaddr_in):
bzero(&sri,sizeof(sri));
readbuf = pdapi_recvmsg(sock_fd,&rd_sz,(struct sockaddr *)&client,&len,&sri,&msg_flags);
if(readbuf == NULL)
continue;

三、通知

SCTP程序都忽略除新数据的收取以外所有可能发生的事件。下面是给出如何接收并解释SCTP通知事件的概貌。把服务器程序改为预订所有事件,当收到一个通知时调用这个新函数。注意,我们的服务器程序并没有把通知用于任何特定的目的。

#include	"unp.h"

//显示来自SCTP的任何通知
void print_notification(char *notify_buf)
{
	union sctp_notification *snp;
	struct sctp_assoc_change *sac;
	struct sctp_paddr_change *spc;
	struct sctp_remote_error *sre;
	struct sctp_send_failed *ssf;
	struct sctp_shutdown_event *sse;
	struct sctp_adaption_event *ae;
	struct sctp_pdapi_event *pdapi;
	const char *str;

	snp = (union sctp_notification *)notify_buf;//存放通知的输入缓冲区类型强制转换成整体型的联合类型。
	switch(snp->sn_header.sn_type) {
	//如果函数在缓冲区中发现"关联改变” 通知,则显示已发生的关联变动的类型。
	case SCTP_ASSOC_CHANGE:
		sac = &snp->sn_assoc_change;
		switch(sac->sac_state) {
		case SCTP_COMM_UP:
			str = "COMMUNICATION UP";
			break;
		case SCTP_COMM_LOST:
			str = "COMMUNICATION LOST";
			break;
		case SCTP_RESTART:
			str = "RESTART";
			break;
		case SCTP_SHUTDOWN_COMP:
			str = "SHUTDOWN COMPLETE";
			break;
		case SCTP_CANT_STR_ASSOC:
			str = "CAN'T START ASSOC";
			break;
		default:
			str = "UNKNOWN";
			break;
		} /* end switch(sac->sac_state) */
		printf("SCTP_ASSOC_CHANGE: %s, assoc=0x%x\n", str,
		       (uint32_t)sac->sac_assoc_id);
		break;
//如果是发现对端地址通知,则显示经译码的地址事件和变动后的地址。
	case SCTP_PEER_ADDR_CHANGE:
		spc = &snp->sn_paddr_change;
		switch(spc->spc_state) {
		case SCTP_ADDR_AVAILABLE:
			str = "ADDRESS AVAILABLE";
			break;
		case SCTP_ADDR_UNREACHABLE:
			str = "ADDRESS UNREACHABLE";
			break;
		case SCTP_ADDR_REMOVED:
			str = "ADDRESS REMOVED";
			break;
		case SCTP_ADDR_ADDED:
			str = "ADDRESS ADDED";
			break;
		case SCTP_ADDR_MADE_PRIM:
			str = "ADDRESS MADE PRIMARY";
			break;
		default:
			str = "UNKNOWN";
			break;
		} /* end switch(spc->spc_state) */
		printf("SCTP_PEER_ADDR_CHANGE: %s, addr=%s, assoc=0x%x\n", str,
		       Sock_ntop((struct sockaddr *)&spc->spc_aaddr, sizeof(spc->spc_aaddr)),
		       (uint32_t)spc->spc_assoc_id);
		break;

//如果函数发现远程错误,则显示该错误和发生它的关联的ID 。
	case SCTP_REMOTE_ERROR:
		sre = &snp->sn_remote_error;
		printf("SCTP_REMOTE_ERROR: assoc=0x%x error=%d\n",
		       (uint32_t)sre->sre_assoc_id, sre->sre_error);
		break;

//如果函数解码出发送失败“ 通知,它就知道消息未能发送到对端。这意味看:
//关联正在关闭之中,马上就会得到一个关联通知 (如果还没有到达的话);
// 服务器在使用部分递送扩展,并有一个消息未成功发送(由设置在传送上的限制造成)。待发送的数据实际上存放在ssf_data成员中。
	case SCTP_SEND_FAILED:
		ssf = &snp->sn_send_failed;
		printf("SCTP_SEND_FAILED: assoc=0x%x error=%d\n",
		       (uint32_t)ssf->ssf_assoc_id, ssf->ssf_error);
		break;
//如果函数解码出适配层指示符 , 则显示在关联建立消息 (INIT或INIT-ACK ) 中传递的32位。
	case SCTP_ADAPTION_INDICATION:
		ae = &snp->sn_adaption_event;
		printf("SCTP_ADAPTION_INDICATION: 0x%x\n",
		    (u_int)ae->sai_adaption_ind);
		break;
//如果有 “部分递送“通知到达,则显示通知的事件。目前唯一 的事件是部分递送被取消。
	case SCTP_PARTIAL_DELIVERY_EVENT:
	    pdapi = &snp->sn_pdapi_event;
	    if(pdapi->pdapi_indication == SCTP_PARTIAL_DELIVERY_ABORTED)
		    printf("SCTP_PARTIAL_DELIEVERY_ABORTED\n");
	    else
		    printf("Unknown SCTP_PARTIAL_DELIVERY_EVENT 0x%x\n",
			   pdapi->pdapi_indication);
	    break;
	    
//如果函数解码出该通知 , 则表示对端已经发出一 个雅致的SHUT DOWN 消息。
	case SCTP_SHUTDOWN_EVENT:
		sse = &snp->sn_shutdown_event;
		printf("SCTP_SHUTDOWN_EVENT: assoc=0x%x\n",
		       (uint32_t)sse->sse_assoc_id);
		break;
	default:
		printf("Unknown notification event type=0x%x\n", 
		       snp->sn_header.sn_type);
	}
}


//服务器mian()函数

//修改事件设置以接收所有通知
bzero(&events,sizeof(evnts));
events.sctp_data_io_event = 1;
evnts.sccp_associac1on_event = 1; 
evnts.sctp_address_event = 1; 
evnts.sctp_send_failure_event=1;
evnts.sctp.peer_error_event = 1;
evnts.sctp.shutdown_event = 1;
evnts.sctp.partial_delivery_event = 1;
evnts.sctp.adaption_layer_event = 1;
setsockopt(sock_fd,IPPROTO,SCTP_EVENTS,&events,sizeof(event));

listen(sock_fd,LISTENQ);
for(;;){
 len = sizeof(struct sockaddr_in);
         rd_sz = sctp_recvmsg(sock_fd, readbuf, sizeof(readbuf),
                                 (struct sockaddr*)&cliaddr, &len, &sri, &msg_flags);
//服务器检查 msg_flags , 如果发现数据是通知,那就调用新的实用函数print_ notification 显示这个通知,然后循环回去读入下一个消息。
if(msg_flags & MSG_NOTIFICATION){
printf_notification(readbuf);
continue;

四、无序的数据

  1. SCTP通常提供可靠的有序数据传输服务,不过也提供可靠的无序数据传输服务。

  2. 指定MSG_UNORDERED标志发送消息没有顺序限制。

  3. 无序的数据可以在任何SCTP流中发送,不用赋予流序列号;

//修改客户端
out_sz = strlen(sendline);
sctp_sendmsg(sock_fd,sendline,out_sz,to,tolen,MSG_UNORDERED,sri.sinfo_stream,0,0);

五、绑定地址子集

  1. TCP和UDP传统上只能捆绑单个地址,而不能捆绑一个地址子集。

  2. bind系统调用允许应用进程捆绑单个地址或通配地址。

  3. SCTP提供sctp_bindx函数调用允许应用进程捆绑多个地址,所有地址必须拥有相同的端口,若调用过bind,那么所用端口是调用bind()时指定的端口。

#include	"unp.h"

int
sctp_bind_arg_list(int sock_fd, char **argv, int argc)
{
	struct addrinfo *addr;
	char *bindbuf, *p, portbuf[10];
	int addrcnt=0;
	int i;
//sctp_bind_arg_list函数首先会分配sctp_bindx调用的地址列表参数所需的空间。
//sctp_bindx能够混和接受IPv4和IPv6地址。
//为每个地址分配足以装下sockaddr_storage结构的空间,尽管地址列表参数是多个实际套接字地址结构的紧凑列表。这么做导致一定的内存空间浪费,不过总比处理参数表两次以计算出精确的内存空间大小简单些 。
	bindbuf = (char *)calloc(argc, sizeof(struct sockaddr_storage));
	p = bindbuf;
	
//把portbuf设咒成端口的ASCII表达形式,以便调用getaddrinfo的外包函数之一host_serv 。
//把每个地址和这个端口传递给host_serv, 同时传递AF_UNSPEC作为地址族以允许IPv4或IPv6地址,传递SOCK_SEQPACKET作为套接字类型指明使用SCTP。
//仅仅复制host_serv返回的第一个套接字地址结构。
//本函数的参数是各个地址的数串表达式,而不是可能关联多个地址的名字表达式,这么处理是安全的。
//随后释放由host_serv内包的getaddrinfo分配的空间,递增地址计数,并把指针移到紧凑的套接字地址结构数组的下 一 个元素。
	sprintf(portbuf, "%d", SERV_PORT);
	for( i=0; i<argc; i++ ) {
		addr = host_serv(argv[i], portbuf, AF_UNSPEC, SOCK_SEQPACKET);
		memcpy(p, addr->ai_addr, addr->ai_addrlen);
		freeaddrinfo(addr);
		addrcnt++;
		p += addr->ai_addrlen;
	}
	//该函数会将指针重置到所捆绑缓冲区的顶端,并以刚才准备的地址列表调用sctp_bindx
	sctp_bindx(sock_fd,(struct sockaddr *)bindbuf,addrcnt,SCTP_BINDX_ADD_ADDR);
//如果函数能运行至此,则清理所用缓冲区并返回成功。
	free(bindbuf);
	return(0);

//服务器mian()函数
if(argc<2)
err_quit("Error,use %s [list of addresses to bind]\n",argv[0]);
sock_fd = socket(AF_INET6,SOCK_SEQPACKET,IPPROTO_SCTP);

if(sctp_bind_arg_list(sock_fd,argv+1,argc-1))
err_sys("Can't bind the address set");
bzero(&events,sizeof(events));
events.sctp_data_io_event = 1;
}

六、确定对端和本端地址信息

SCTP是 多宿协议,找出一个关联的本地端点和远程端点所用的地址需要使用不同于单宿协议的机制。本节中我们把前面的客户程序改为接收通信开工通知,然后使用该通知显示关联的本端和对端的地址。

//客户端main()函数修改
bzero(&evnts,sizeof(evnets));
events.sctp_data_io_event = 1;
events.sctp_association_event = 1;
setsocket(sock_fd,IPPROTO_SCTP,SCTP_EVENTS,&events,sizeof(events));
sctpstr_cli(stdin,sock_fd,(struct sockaddr*)&servaddr,sizeof(servaddr));


//设置时间通知并调用回射函数,sctp_strcli()函数修改

//客户会设致套接字地址结构长度变量,调用接收函数获取由服务器回射的应答消息。
do{
	len = sizeof(peeraddr);
	rd_sz = sctp_recvmsg(sock_fd,recvline,sizeof(recvline),(struct sockaddr*)&peeraddr,&len,&sri,&msg_flags);
//客户会查看刚读入的消息是不是个通知。
	if(msg_flags & MSG_NOTIFICATION)
	check_notification(sock_fd,recvline,rd_sz);
//如果刚读入的是一个通知,那就继续循环,直到读入真正的数据。
	}while(msg_flags & MSG_NOTIFICATION);
//显式消息并同到处理循环顶部,等待用户输入。
	printf("From str:%d seq:%d (addoc:0x%x):",sri.sinfo_stream,sri.sinfo_ssn,(u_int)sri.sinfo_assoc_id);
	printf("%.*s",rd_sz,recvline);





//检查是否为期望的通知
void check_notification(int sock_fd,char *recvline,int rd_len)
{
	union sctp_notification *snp;
	struct sctp_assoc_change *sac;
	struct sockaddr_storage *sal,*sar;
	int num_rem, num_loc;
//该函数把接收缓冲区类型强制转换成通用的通知指针,以便找出通知类型。
//如果本通知是所关注的类型(即关联变动类通知),那就测试否为一个新的或重新激活的关联(SCTP_COMM_UP或SCTP_RESTART)。我们忽略所有其他通知。
	snp = (union sctp_notification *)recvline;
	if(snp->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
		sac = &snp->sn_assoc_change;
		if((sac->sac_state == SCTP_COMM_UP) ||
		   (sac->sac_state == SCTP_RESTART)) {
//调用sctp_getpaddrs汇集远程地址列表,然后显示地址数目,
//并调用图显示实用函数sctp_print_addresses显示各个地址
//调用sctp_freepaddrs释放由sctp_getpaddrs分配的资源。
			num_rem = sctp_getpaddrs(sock_fd,sac->sac_assoc_id,&sar);
			printf("There are %d remote addresses and they are:\n",
			       num_rem);
			sctp_print_addresses(sar,num_rem);
			sctp_freepaddrs(sar);
//调用sctp_getladdrs收集本地地址列表,并显示地址数目和各个地址本身。
//在通知处理函数使用完这些地址后调用sctp_freeladdrs释放由sctp_getladdrs分配的资源。
			num_loc = sctp_getladdrs(sock_fd,sac->sac_assoc_id,&sal);
			printf("There are %d local addresses and they are:\n",
			       num_loc);
			sctp_print_addresses(sal,num_loc);
			sctp_freeladdrs(sal);
		}
	}

}



//显示由sctp_getpaddrs或sctp_getladdrs返回的地址列表。
void sctp_print_addresses(struct sockaddr_storage *addrs, int num)
{
	struct sockaddr_storage *ss;
	int i,salen;

//根据调用者指定的地址数目遍历每个地址。
	ss = addrs;
	for(i=0; i<num; i++){
	//调用sock_ntop显示地址。该函数能够显示系统支持的任何奋接字地址结构格式。
		printf("%s\n", sock_ntop((SA *)ss, salen));

//确定地址大小
//地址列表是一个紧凑的套接字地址结构数组,而不是单一sockaddr_storage结构的数组。
//因为sockaddr_storage结构太大,用它在内核和进程之间传递地址过于浪费内存空间。
//如果套接字地址结构自带长度成员,那就直接使用其值作为本结构的长度;否则就根据地址族选择长度,若不是已知地址族则显示一个出错消息并退出.	
#ifdef HAVE_SOCKADDR_SA_LEN
		salen = ss->ss_len;
#else
		switch(ss->ss_family) {
		case AF_INET:
			salen = sizeof(struct sockaddr_in);
			break;
#ifdef IPV6
		case AF_INET6:
			salen = sizeof(struct sockaddr_in6);
			break;
#endif
		default:
			err_quit("sctp_print_addresses: unknown AF");
			break;
		}
#endif
//根据所确定的地址大小前向移动地址指针,指向下一个待处理的地址。
		ss = (struct sockaddr_storage *)((char *)ss + salen);
	}
}


七、给定IP地址找出关联ID

从一个对端IP地址转换成一个关联ID。

#include	"unp.h"

sctp_assoc_t sctp_address_to_associd(int sock_fd, struct sockaddr *sa, socklen_t salen)
{
	struct sctp_paddrparams sp;
	int siz;
//初始化所用的sctp_paddrparams结构。
	siz = sizeof(struct sctp_paddrparams);
	bzero(&sp,siz);
//把作为参数传入的套接字地址结构按照同时传入的长度复制到sctp_paddrpararns结构中。
	memcpy(&sp.spp_address,sa,salen);
//通过获取SCTP_PEER_ADDR_PARAMS套接字选项值取得对端地址参数
	sctp_opt_info(sock_fd,0,
		   SCTP_PEER_ADDR_PARAMS, &sp, &siz);
		   //把关联ID返回给调用者。关联ID不允许为0, SCTP可以使用0值关联ID作为没有关联的指示
	return(sp.spp_assoc_id);
}

八、心搏和地址不可达

SCTP提供保存存活选项的心搏机制。SCTP的心搏机制默认开启。应用进程可以禁止心搏,不过要是没有心搏的话,SCTP将无法检测一个被认定不可达的对端地址再次变为可达。没有用户干预,这些地址就不能回到活跃状态。

sctp_paddrparams结构中的心搏间隔字段是spp_hbinterval。其倌为SCTP_NO_HB即0表示禁止心搏。其值为SCTP_ISSUE_HB0xffffffff表示 一 经请求立即心搏。任何具他值以毫秒为单位设置心搏间隔。该值加上当前重传计时器的值,再加上 一 个随机的抖动值就构成了心搏的间隔时间。



//针对对端地址设置确切的心搏间隔,或请求立即心搏一次,或禁止。
#include	"unp.h"

int heartbeat_action(int sock_fd, struct sockaddr *sa, socklen_t salen,
			  u_int value)
{
	struct sctp_paddrparams sp;
//清零sctp_paddrparams结构并复制心搏间隔
	int siz;
bzero(&sp,sizeof(sp));

	sp.spp_hbinterval = value;
//把实施心搏的对端地址复制到sctp_paddrparams结构中,以便SCTP实现了解我们希望它向哪个地址发送心搏请求。
	memcpy((caddr_t)&sp.spp_address,sa,salen);
	//调用setsockopt执行用户请求的行为。
	setsockopt(sock_fd,IPPROTO_SCTP,
		   SCTP_PEER_ADDR_PARAMS, &sp, sizeof(sp));
	return(0);
}

九、关联剥离

一到多式接口相比一到一接口的优势:

  1. 只需维护单个描述符;

  2. 允许编写简单的迭代服务器程序;

  3. 允许应用进程在四路握手的第三、四分组发送数据,只需要使用sendmsg或sctp_sendmsg隐式建立关联;

  4. 无需跟踪传输状态;

一到多的缺陷:

  • 难以编写并发服务程序,该缺陷促成增设sctp_peeloff函数。
    • 该函数取 一 个一 到多式套接字描述符和一 个关联ID, 返回一 个新的仅仅附以给定关联的一 到一 式套接字描述符 (再加上已经排队在该关联上的通知和数据)。
      • 原始的一到多式套接字继续开放,它代表的其他关联均不受此影响。
      • 该套接字然后可以递交给某个专门的线程或子进程加以处理,从而实现并发服务器。
#include	"unp.h"
 
int
main(int argc, char **argv)
{
	int sock_fd,msg_flags,connfd,childpid;
	sctp_assoc_t assoc;
	char readbuf[BUFFSIZE];
	struct sockaddr_in servaddr, cliaddr;
	struct sctp_sndrcvinfo sri;
	struct sctp_event_subscribe evnts;
	socklen_t len;
	size_t rd_sz;
 
    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);
 
	bind(sock_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
	
	bzero(&evnts, sizeof(evnts));
	evnts.sctp_data_io_event = 1;
	setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS,
		   &evnts, sizeof(evnts));
 
	listen(sock_fd, LISTENQ);
 
	for ( ; ; ) {
		len = sizeof(struct sockaddr_in);
		rd_sz = Sctp_recvmsg(sock_fd, readbuf, sizeof(readbuf),
			     (struct sockaddr *)&cliaddr, &len,
			     &sri,&msg_flags);
		sctp_sendmsg(sock_fd, readbuf, rd_sz, 
			     (struct sockaddr *)&cliaddr, len,
			     sri.sinfo_ppid,
			     sri.sinfo_flags,
			     sri.sinfo_stream,
			     0, 0);
		// 将地址转换成关联 ID
		// 若无法获取该关联 ID 则跳过不再尝试派生子进程继续处理
		assoc = sctp_address_to_associd(sock_fd,(struct sockaddr *)&cliaddr,len);
		if((int)assoc == 0){
			err_ret("Can't get association id");
			continue;
		}
		// 剥离关联
		// 调用 sctp_peeloff 函数将与该客户的关联剥离到自己的一到一式套接字中
		connfd = sctp_peeloff(sock_fd,assoc);
		if(connfd == -1){
			err_ret("sctp_peeloff fails");
			continue;
		}
		// 派生子进程继续处理该新套接字上的后续操作
		if((childpid = fork()) == 0) {
			close(sock_fd);
			str_echo(connfd);
			exit(0);
		} else {
			close(connfd);
		}
	}
 
}

十、定时控制

  1. SCTP有许多用户可调的控制量,它们影响SCTP端点需多久才能声称某个关联或某个对端地址已经失效。
  2. 这些控制量影响SCTP的失效检测速度或重传尝试次数,可以认为它们是缩短或延长端点失效检测时间的控制手柄。
  3. 项目3

在这里插入图片描述

十一、何时用sctp代替tcp

  1. SCTP直接支持多宿;

  2. SCTP可以消除头端阻塞;

  3. 保持应用层消息边界;

  4. 提供无序消息服务;

  5. 提供部分可靠服务,允许SCTP发送端为每个消息指定一个生命期(sctp_sndrcvinfo结构的sinfo_timetolive字段),当源端点和目的端点都支持本特性时,时间敏感的过期数据可改由传输层而不是应用进程丢弃;

  6. 以一到一式接口提供了从TCP到SCTP的简易移植手段;

  7. 支持许多TCP特性,包括正面确认、重传丢失数据、重排数据、窗口式流量控制、慢启动、拥塞避免、选择性确认;未支持半关闭状态和紧急数据(使用分离的SCTP流传输紧急数据可以替代该特性);

  8. 提供许多供应用进程配置和调整传输服务,以便基于关联匹配其需求的挂钩;

  9. SCTP不提供的TCP特性之 一 是半关闭状态。

10.SCTP不提供的TCP特性之二是紧急数据 。 使用分离的SCTP流传输紧急数据多少类似TCP的紧急数据的语义,不过难以准确复制这个特性 。

  1. 面向字节流传输服务的应用不能使用SCTP;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值