九歌
普通会员
普通会员
  • 铜币0枚
  • 威望0点
  • 贡献值0点
阅读:3284回复:10

sctp 流控制传输协议实验

楼主#
更多 发布于:2005-05-30 14:14
实验题目:流控制传输协议实现
系统环境:debian linux sctp 2.6.11.7
               内核需要重新编译,添加SCTP功能
开发环境:gcc-3.3

实验目的:
1.实现基本sctp信息传递。(1-to-1)
2.实现sctp一对多消息传递。(1-to-many)
3.实现sctp多宿主机功能。(sctp more IP)

SCTP基本特点:
1 引言

  在过去的20年内,作为一种可靠的数据传输方式,TCP提供了许多应用服务,但随着IP网的多业务化,尤其是VoIP的发展,TCP出现了很多局限性,例如对于VoIP信令及异步基于事务应用的处理。因此,IEFT的信令传输工作组(SIGTRAN)提出了一种面向多媒体通信的流控制传输协议(SCTP),用于在IP网络上传输PSTN信令消息,即通常所说的SS7 over IP。目前,IEFT将SCTP传输层协议作为主要研究目的,与TCP和UDP共筑于IP层之上。  同TCP一样,SCTP提供面向连接的、点到点的可靠传输,它继承了TCP强大的拥塞控制、数据包丢失发现等功能,任何在TCP上运行的应用都可被移至SCTP上运行。

  不同于TCP的是,SCTP提供了许多对于信令传输很重要的功能,同时,对于其他一些对性能和可靠性有额外需要的应用,它能提供传输优势来满足这些需要。SCTP和TCP最大的区别在于SCTP对多宿(multihoming)和部分有序(partial ordering)的支持。SCTP的多宿使得每个端点可被多个传输地址访问到,选择不同传输地址会导致两个端点间不同的数据路径,理想情况是在每一条路径都建立一条独立的拥塞控制。所以,SCTP的多主机拥塞控制仍需改进。

2 SCTP的特点

  SCTP处于SCTP用户应用层与IP网络层之间,它运用“关联”(association)这个术语定义交换信息的两个对等SCTP用户间的协议状态 。SCTP也是面向连接的,但在概念上,SCTP“关联”比TCP连接更为广泛:TCP的连接只有一个源地址和一个目的地址,SCTP提供一种方式使得每个SCTP端点能为另一个对等端点提供一组传输地址,即传输地址= 一组IP地址+端口号。

在继承TCP特点的基础上,SCTP提供了一些额外的功能:

1. 在多个“流”(stream)中实现用户数据的有序发送

“流”在TCP中指一系列的字节,而在SCTP中是指发送到上层协议的一定系列的用户消息,这些消息的顺序与流内其他消息相关。SCTP用户在建立关联时,可以规定关联支持的流的数目。这个数目是与源端商定的,用户消息与流数目关联。在链路中,SCTP为每个送到对等端的消息分配一个流序号。在接收端,SCTP确保在给定流中消息按顺序发送。同时,当一个流正在等待下一个非顺序的用户消息时,其他流的发送会继续。
2. 根据已发现的路径MTU(最大传输单元)大小进行用户数据分片

  为了确保发送到下层的SCTP数据包与路径MTU一致,SCTP对用户消息分片。在接收端,分片被重组后传给上层SCTP用户。

3. 选择性确认(SACK)和拥塞控制

  选择性确认用于数据包丢失发现,TCP中确认序号返回的是发送方已成功收到数据字节序号(不包含确认序号所指的字节),而SCTP反馈给发送端的是丢失的并且要求重传的消息序号。

  SCTP运用了TCP中的拥塞控制技术,包括慢启动,拥塞避免和快速重传。因此,当和TCP应用共存时,SCTP应用可接收属于SCTP的网络资源部分。

4. 块(chunk)绑定

  即多个用户消息可选择地绑定到一个SCTP包上,通过将消息放到一个或多个SCTP数据结构——“块”中,SCTP保留了应用程序的消息框架边界。不同类型的块可绑定到一个SCTP包中,但是控制块必须放在任何一个数据块之前。

5. 路径管理

  SCTP 路径管理功能主要负责从远端提供的一组传输地址中选择目的传输地址,它根据两个方面来选择目的地址:SCTP用户指示和当前可达的合格目的地。当其他流控制不能提供可达性信息时,路径管理功能定时地扫描链路的可达性,并向SCTP报告远端传输地址所发生的变化。SCTP 路径管理功能模块同时还负责在建立链路时,向远端报告可用的本地地址,并把远端返回的传输地址告诉SCTP用户。

6. 支持多宿

  当SCTP传送数据包给目的IP地址时,如果此IP地址是不可达的,SCTP可以将消息重路由给一个交替的IP地址。这样,在关联的一端甚至两端,可容忍网络级错误。

7. 防范拒绝服务(DoS)攻击

  DoS的攻击方式有很多种,最基本的DoS攻击就是利用合理的服务请求来占用过多的服务资源,从而使合法用户无法得到服务的响应。SYN Flooding攻击是DoS攻击的一种实例,是目前效果最好的一种黑客攻击方式。为了抵抗SYN Flooding对目标主机攻击,SCTP在关联初始化阶段实施了一种安全的“Cookie”机制。

8. 支持多种传输模式

严格有序传输(像TCP),部分有序传输(像per-stream)和无序传输(像UDP)。

实验原理:
1. 利用sctp实现一对一建立连接的S/C数据传输程序。此模式非常类似传统的TCP/IP程序设计流程,但是sctp数据帧的格式与传统TCP/IP数据帧的格式不同。


实验过程:
1. Sctp建立连接的一对一数据传输。


//服务器端程序原代码
//此服务器从客户端接收一个字符串,然后将其中的小写字符转换为大写字符后再反馈给客//户端程序。
//服务器端程序的端口号设为8000

//server.c //基本头文件与传统TCP程序相似,其中指需要添加
#include <stdio.h> // netinet/sctp.h头文件
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <netdb.h>

#define PORT 8000 //设服务器程序端口号为8000

int main()
{
struct sockaddr_in sin; //定义接收数据的sockaddr结构
struct sockaddr_in pin; //定义反馈数据的sockaddr结构

int sock_descriptor; //定义一个接收、监听套接字变量
int temp_sock_descriptor; //定义一个反馈信息套接字变量
int address_size;

char buf[16384]; //建立一个数据缓冲区

int i, len;

sock_descriptor = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
//创建一个sctp(1-to-1)套接字ID
if(sock_descriptor == -1){
perror("call to socket");
exit(1);
}

bzero(&sin, sizeof(sin) ); //初始化接收数据的sockaddr结构
sin.sin_family = PF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
printf("hostIP: %d",sock_descriptor);
sin.sin_port = htons(PORT);

if(bind(sock_descriptor, (struct sockaddr *)&sin, sizeof(sin)) == -1){
perror("call to bind"); //建立一个sctp(1-to-1)套接字
exit(1);
}

if(listen(sock_descriptor, 20) == -1){ //监听套接字
perror("call to listen");
exit(1);
}

printf("Accepting connections ...\n");

while(1){
temp_sock_descriptor = accept(sock_descriptor, (struct sockaddr *)&pin, &address_size);
//建立一个反馈信息套接字ID
if(temp_sock_descriptor == -1){
perror("call to accept");
exit(1);
}

if(recv(temp_sock_descriptor, buf, 16384, 0) == -1){
perror("call to recv"); //从监听套接口收到数据存入缓冲区
exit(1);
}

printf("received from client : %s\n", buf);

len = strlen(buf);
for(i = 0; i < len; i++) buf = toupper(buf);
if(send(temp_sock_descriptor, buf, len, 0) == -1){
perror("call to send"); //将反馈信息通过反馈套接口发送出去
exit(1);
}

close(temp_sock_descriptor);
}

return 0;
}




//客户端程序
//客户端程序将用户输入的一个字符串发送给服务器程序,然后等待接收由服务器返回的信息,如果用户没//有输入字符串,程序将会发送一个默认的字符串给服务器程序。
//设服务器的IP地址是192.168.0.132 端口号为 8000
//client.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <netdb.h>

#define PORT 8000

char * host_name = "192.168.0.132";

int main(int argc, char * argv[])
{
char buf[8192];
char message[256];
int socket_descriptor;
struct sockaddr_in pin;
struct hostent * server_host_name;

char * str = "Hello SCTP !"; //设定程序默认字符串

if(argc < 2){
printf("Usage:'test \"not test string\"\n");
printf("We will send a \"Hello SCTP\" test string\n");
}else{
str = argv[1];
}

if((server_host_name = gethostbyname(host_name)) == 0){
perror("Error resolving loca host\n");
exit(1);
}

bzero(&pin, sizeof(pin));
pin.sin_family = PF_INET;
pin.sin_addr.s_addr = htonl(INADDR_ANY);
pin.sin_addr.s_addr = ((struct in_addr *)(server_host_name -> h_addr)) -> s_addr;
pin.sin_port = htons(PORT);

if((socket_descriptor = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1){
perror("Error opening socket\n"); //建立一个sctp套接字ID
exit(1);
}
printf("\n%d\n", socket_descriptor);
if(connect(socket_descriptor, (void *)&pin, sizeof(pin)) == -1){
perror("Error connecting to socket\n"); //连接服务器端程序
exit(1);
}

printf("Sending message %s to server...\n", str);

if(send(socket_descriptor, str, strlen(str), 0) == -1){
perror("Error in send\n"); //向服务器端发送数据
exit(1);
}

printf("..sent message.. wait for reponse...\n");

if(recv(socket_descriptor, buf, 8192, 0) == -1){
perror("Error in receiving response from server\n");
exit(1); //从服务器端接收数据
}

printf("\nResponse from server:\n\n%s\n", buf);

close(socket_descriptor);

return 0;
}

sctp协议(1-to-1)编程的过程和系统调用与传统的TCP协议程序设计十分相似。

主要区别在于:
tcp_socket = socket(PF_INET, SOCK_STREAM, 0); //传统的TCP套接口建立
sctp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP); //sctp套接口

其次:
在底层帧的结构上sctp与TCP存在很大差异

由此sctp协议1-to-1模式的应用相对于1-to-many模式显得不够灵活,也没有体现出sctp独有的新特性。所以在实际应用中sctp 1-to-1模式并不普遍。


2. 利用sctp实现一对多建立连接的消息传输程序。此模式类似UDP数据传输流程,但他与UDP数据传输模式不同的是sctp是建立连接的。



2. Sctp建立连接的一对多消息传输。
用sctp实现一对多消息传输首先要明确几个概念,sctp的消息传送与UDP的消息传送虽然在形式上比较相似但是本质是完全不同的,首先sctp是建立在连接基础之上,而UDP不需要建立连接,sctp消息传输是可靠的,UDP消息传输是不可靠的。

此程序是将客户端发送来的消息内容替换,再反馈给客户端,循环执行。

//server.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>

#define PORT 8000 //设服务器端口号为8000

int main()
{
int sctp_socket, i =0;
struct sockaddr_in server_addr; //定义一个套接字结构
struct msghdr recv_mesage; //定义一个消息结构
struct iovec recv_iov; //定义一个消息数据单元结构
int ret;

char a[54]; //定义一个接收缓冲区

if( (sctp_socket = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0 ){
perror("sctp socket:"); //建立一个sctp一对多套接口ID
return -1;
}else
printf("sctp socket OK!\n");

bzero( &server_addr, sizeof(server_addr) ); //初始化套接字结构
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
if( bind(sctp_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0){
perror("sctp bind:"); //将套接字结构和套接字ID进行关联
return -1;
}else
printf("sctp bind OK!\n");

if( listen(sctp_socket, 10) < 0 ){ //监听此套接口
perror("sctp listen:");
return -1;
}else
printf("sctp listen OK!\n");

while(1){

bzero( &a, sizeof(a) ); //初始化接收数据缓冲区

bzero( &recv_iov, sizeof(recv_iov) ); //初始化数据单元结构

recv_iov.iov_base = (void *)a;
recv_iov.iov_len = sizeof(a);

bzero( &recv_mesage, sizeof(recv_mesage) ); //初始化消息结构
recv_mesage.msg_name = &server_addr;
recv_mesage.msg_namelen = sizeof(server_addr);
recv_mesage.msg_iov = &recv_iov;
recv_mesage.msg_iovlen = 1; //设置消息单元数量

if((ret = recvmsg(sctp_socket, &recv_mesage, 0)) < 0 ){
perror("sctp recvmsg:"); //接收消息
return -1;
}else{
printf("sctp recvmsg OK!, ret = %d\n", ret);
printf("NO: %d message is c: %s\n\n", i, (char *)recv_mesage.msg_iov -> iov_base);
}
recv_mesage.msg_iov -> iov_base = "ok!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
if((ret = sendmsg(sctp_socket, &recv_mesage, 0)) < 0 ){
perror("sctp sendmsg:"); //反馈消息
return -1;
}else{
printf("sctp sendmsg OK!, ret = %d\n", ret);
printf("NO: %d message is c: %s\n\n", i++, (char *)recv_mesage.msg_iov -> iov_base);
}
}

return 0;
}


此程序向服务器端发送一条消息,再等待并接收服务器端的反馈消息,循环执行。

//client.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>

#define PORT 8000 //设置服务器端端口号为 8000

int main()
{
int sctp_socket, i = 0;
struct sockaddr_in client_addr; //定义一个套接字结构
struct msghdr send_mesage; //定义一个消息结构
struct iovec send_iov, recv_iov; //定义一个发送消息数据单元结构和一个接收消息数据单元结构
int ret;

char * msg_a = "Hello SCTP !!!!!!!!!!!!!!!!!!!!!\a"; //声明要发送的字符串
char msgbuf[1024]; //定义一个接收数据缓冲区

if( (sctp_socket = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0 ){
perror("sctp socket:"); //建立一个sctp套接字ID
return -1;
}else
printf("sctp socket OK!\n");

while(1){

bzero( &send_iov, sizeof(send_iov) ); //初始化消息数据单元
send_iov.iov_base = (void *)msg_a;
send_iov.iov_len = strlen(msg_a);

bzero( &client_addr, sizeof(client_addr) ); //初始化套接字结构
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = ((struct in_addr *)(gethostbyname("192.168.0.132") -> h_addr)) -> s_addr;
client_addr.sin_port = htons(PORT);

bzero( &send_mesage, sizeof(send_mesage) ); //初始化消息结构
send_mesage.msg_name = (void *)&client_addr;
send_mesage.msg_namelen = sizeof(client_addr);
send_mesage.msg_iov = &send_iov;
send_mesage.msg_iovlen = 1; //设置消息数据单元数量

if( (ret = sendmsg(sctp_socket, &send_mesage, 0)) < 0 ){
perror("sendmsg:"); //发送消息
return -1;
}else{
printf("send ret = %d\n", ret);
printf("sendmessage %d msg_c: %s\n\n", i,(char *)send_mesage.msg_iov -> iov_base);
}

bzero(msgbuf, sizeof(msgbuf)); //初始化接收缓冲区
bzero(&recv_iov, sizeof(recv_iov));
recv_iov.iov_base = (void *)msgbuf;
recv_iov.iov_len = 1024;
send_mesage.msg_iov = &recv_iov;
send_mesage.msg_iovlen =1;
if( (ret = recvmsg(sctp_socket, &send_mesage, 0)) < 0 ){
perror("recvmsg:"); //接收服务器反馈消息
return -1;
}else{
printf("recv ret = %d\n", ret);
printf("recvmessage %d msg_c: %s\n\n", i++,(char *)send_mesage.msg_iov -> iov_base);
}
sleep(1);
}

close(sctp_socket);

return 0;
}






应用sctp协议开发一对多网络传输程序的关键是对sctp消息的理解和运用,sctp消息中封装了关于消息传输所需的信息,其中包括对方主机IP地址,消息中数据的长度,消息数据单元的数量等等。

消息结构代码原型:
struct msghdr
{
void *msg_name; //与此消息相关联的套接字结构指针
socklen_t msg_namelen; //与此消息相关联的套接字结构长度

struct iovec *msg_iov; //此消息所要挟带的数据单元
size_t msg_iovlen; //此消息所要挟带的数据单元长度

void *msg_control; //此消息的相关控制结构
size_t msg_controllen; //此消息的相关控制结构长度

int msg_flags; //此消息的状态标志
};

//消息结构中的数据单元结构代码原型
struct iovec
{
void iov_base; //消息所要传送的数据指针,可以是任何类型数据
size_t iov_len; //消息所要传送的数据长度
}
消息数据单元可以是一个结构数组,一次可以将许多不同的数据加载到同一个消息结构当中。接收时可以通过算法将不同类型数据分开存储,也可以存为一个整体数据快。

iovec结构中存有将要被发送出去的数据或接收数据的缓冲区,而消息结构msghdr中除了数据单元外还有套接口的信息和控制信息。我们可以一次定义多个指向不同IP地址和端口的消息结构,同时在服务器端也可以用同样的方法定义并关联多个消息结构,这样一来就可以设计出根据不同的变化把同一个消息
发送至不同的对端,也可以利用sctp消息实现服务器的单机、多机热备份(冗余应用)。

总结: 利用sctp开发网络程序的基本步骤。

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

sctp 1-to-1
服务器端:
sctp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
bind();
listen();
sccept();
close();
客户端:
sctp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
connect();
close();
sctp 1-to-many
服务器端:
sctp_socket = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
bind(); //可以关联多个不同网络端口(本地,其他主机)
listen(); //可同时监听多个不同的网络端口(本地,其他主机)
recvmsg();
sendmsg();
close();
客户端:
sctp_socket = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
sendmsg();
recvmsg();
close(); [ 2005-05-30 14:16:16 九歌 修改 ]
九歌
普通会员
普通会员
  • 铜币0枚
  • 威望0点
  • 贡献值0点
1C#
发布于:2005-05-30 14:21
Re:sctp 流控制传输协议实验
文章是网上找到的,写得比较清晰。
程序是我写的,如果诸位发现有错
误请及时通知我,感激不尽!

[email protected]
wfeng
写手
写手
  • 铜币0枚
  • 威望0点
  • 贡献值0点
2C#
发布于:2005-05-30 15:13
Re:sctp 流控制传输协议实验
你亚又上来装亚,哈哈 -------------------- 最是那一低头的温柔,恰似一朵水莲花不胜凉风的娇羞。最是那不经意的回首,定格住几度梦中萦绕的闲愁
[img]http://www.wait4c.com/bbs/userfiles/11154/2006031615575276668.jpg[/img]
0000
作家
作家
  • 铜币143枚
  • 威望34点
  • 贡献值1点
3C#
发布于:2005-05-30 16:15
Re:sctp 流控制传输协议实验
今天太热。。。作个记号先。 --------------------            [a=http://northlight.digital-pulse.net/]寻找失落的路灯 @ North.Light[/a]
   有沒有事比死更容易? 有沒有事比活更難?
               ~     幸福是一雙溫暖的紅唇    ~
.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/
ApH
ApH
知名人士
知名人士
  • 铜币0枚
  • 威望0点
  • 贡献值0点
4C#
发布于:2005-05-30 18:58
Re:sctp 流控制传输协议实验
是毕设么?

前一段时间在图书馆上自习的时候看到有个同学在看《Unix环境高级编程》,好激动,不会就是你吧!
suchasplus
作家
作家
  • 铜币16枚
  • 威望11点
  • 贡献值1点
5C#
发布于:2005-05-30 21:07
Re:sctp 流控制传输协议实验
 最近看到好多做毕设的
有点担心不会做  呵呵
unix高级编程我也看了
权当是了解unix  呵呵
编程还早  socket就不会~~~


毕设~


上两天看见有人拿cisco3550做毕设
那才叫激动  呵呵 -------------------- There are Pretenders among us.Geniuses with the ability to become anyone they want to be.
The history of these days will be written in blood... By crushing the armies of our enemy, by seizing the weapons they thought to turn against us, we were fighting for our very existence!
ApH
ApH
知名人士
知名人士
  • 铜币0枚
  • 威望0点
  • 贡献值0点
6C#
发布于:2005-05-31 18:58
Re:sctp 流控制传输协议实验
最近看到好多做毕设的有点担心不会做  呵呵unix高级编程我也看了权当是了解unix  呵呵编程还早  socket就不会~~~毕设~上两天看见有人拿cisco3550做毕设那才叫激动  呵呵--------------------There are Pretende..


不是你:(
DragonHeart
普通会员
普通会员
  • 铜币0枚
  • 威望0点
  • 贡献值0点
7C#
发布于:2006-06-18 17:49
Re:sctp 流控制传输协议实验
楼主很烂。
还以为举了个什么好例子,全是从stevens的UNP那里抄来的!
再说如果只是1to1,又何必用SCTP,TCP就足够了。
永
著名写手
著名写手
  • 铜币90枚
  • 威望18点
  • 贡献值0点
8C#
发布于:2006-08-16 22:35
Re:sctp 流控制传输协议实验
今天 高兴 我就顶~
我们都是普通人,普普通通的老百姓! 但是我们也有自己的思想,我们也有自己的语言,精辟的言语不光是出自社会知名人士,不光出自上层名流!
九歌
普通会员
普通会员
  • 铜币0枚
  • 威望0点
  • 贡献值0点
9C#
发布于:2006-09-01 21:14
Re:sctp 流控制传输协议实验
楼主很烂。还以为举了个什么好例子,全是从stevens的UNP那里抄来的!再说如果只是1to1,又何必用SCTP,TCP就足够了。




stevens 的例子比我写的严谨多了,举例吗就得尽量简单让人都能容易看懂。

如果是 1TO1 的引用当然可以只采用TCP,但是如果要实现应用的热备份或者高可用性容错,TCP就很难做到了。

顺便说一句SCTP是在 stevens 死后才成为官方标准的,UNP的第三卷里有介绍,但肯定不是 stevens 加的。

最近刚用SCTP完成了一个通信服务热备份的项目,有时间把关于 1 TO m 的应用细节拿出来和大家讨论。 [ 2006-09-01 21:16:06 九歌 修改 ]
猪八戒
写手
写手
  • 铜币1枚
  • 威望0点
  • 贡献值0点
10C#
发布于:2006-09-30 09:40
Re:sctp 流控制传输协议实验
最近看到好多做毕设的有点担心不会做  呵呵unix高级编程我也看了权当是了解unix  呵呵编程还早  socket就不会~~~毕设~上两天看见有人拿cisco3550做毕设那才叫激动  呵呵--------------------There are Pretende..
3550怎么做毕设
疑惑中
我回帖还是有质量的
游客

返回顶部