转:http://www.linuxforu.com/2011/12/socket-api-part-5-sctp/
By Pankaj Tanwar on December 29, 2011 in Coding, Developers · 11 Comments
Similar to TCP and UDP, SCTP provides some features of both. It is message-oriented, and provides a reliable sequenced delivery of messages. SCTP supports multi-homing, i.e., multiple IPs on both sides of the connection. So it is called an association instead of a connection, as a connection involves communication between two IPs, while an association refers to communication between two systems that may have multiple IPs.
SCTP can provide multiple streams between connection endpoints, and each stream will have its own reliable sequenced delivery of messages, so any lost message will not block the delivery of messages in any of the other streams. SCTP is not vulnerable to SYN flooding, as it requires a 4-way handshake.
We will discuss the science later, and now jump to the code, as we usually do. But first you need to know the types of SCTP sockets:
- A one-to-one socket that corresponds to exactly one SCTP association (similar to TCP).
- A one-to-many socket, where many SCTP associations can be active on a socket simultaneously (similar to UDP receiving datagrams from several endpoints).
One-to-one sockets (also called TCP-style sockets) were developed to ease porting existing TCP applications to SCTP, so the difference between a server implemented using TCP and SCTP is not much. We just need to modify the call to socket()
to socket(AF_INET, SOCK_STREAM, and IPPROTO_SCTP)
while everything else stays the same — the calls to listen()
, accept()
for the server, and connect()
for the client, with read()
and write()
calls for both.
Now let’s jump to the one-to-many or UDP-style socket, and write a server using multiple streams that the client follows.
First, here is the code for the server, smtpserver.c
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
#include <stdio.h> #include <string.h> #include <time.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/sctp.h> #include <arpa/inet.h> #define MAX_BUFFER 1024 int main()
{ int sfd, cfd, len, i;
struct sockaddr_in saddr, caddr;
struct sctp_initmsg initmsg;
char buff[INET_ADDRSTRLEN];
char buffer[MAX_BUFFER+1] = "Message ##\n" ;
sfd = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
bzero( ( void *)&saddr, sizeof (saddr) );
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl( INADDR_ANY );
saddr.sin_port = htons(29008);
bind( sfd, ( struct sockaddr *)&saddr, sizeof (saddr) );
/* Maximum of 3 streams will be available per socket */ memset ( &initmsg, 0, sizeof (initmsg) );
initmsg.sinit_num_ostreams = 3;
initmsg.sinit_max_instreams = 3;
initmsg.sinit_max_attempts = 2;
setsockopt( sfd, IPPROTO_SCTP, SCTP_INITMSG,
&initmsg, sizeof (initmsg) );
listen( sfd, 5 );
for (;;) {
printf ( "Server Running\n" );
len= sizeof (caddr);
cfd=accept(sfd, ( struct sockaddr *)&caddr, &len);
printf ( "Connected to %s\n" ,
inet_ntop(AF_INET, &caddr.sin_addr, buff,
sizeof (buff)));
for (i=0; i< 3; i++) {
/* Changing 9th character the character after # in the message buffer */ buffer[9] = '1' +i;
sctp_sendmsg( cfd, ( void *)buffer, ( size_t ) strlen (buffer),
NULL, 0, 0, 0, i /* stream */ , 0, 0 );
printf ( "Sent: %s\n" , buffer);
}
close( cfd );
}
return 0;
} |
And here’s the code of the client, sctpclient.c
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
#include <stdio.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/sctp.h> #define MAX_BUFFER 1024 int main( int argc, char **argv)
{ int cfd, i, flags;
struct sockaddr_in saddr;
struct sctp_sndrcvinfo sndrcvinfo;
struct sctp_event_subscribe events;
struct sctp_initmsg initmsg;
char buffer[MAX_BUFFER+1];
if (argc!=2) {
printf ( "Usage: %s ipaddress\n" , argv[0]);
return -1;
}
cfd = socket( AF_INET, SOCK_STREAM, IPPROTO_SCTP );
/* Specify that a maximum of 3 streams will be available per socket */ memset ( &initmsg, 0, sizeof (initmsg) );
initmsg.sinit_num_ostreams = 3;
initmsg.sinit_max_instreams = 3;
initmsg.sinit_max_attempts = 2;
setsockopt( cfd, IPPROTO_SCTP, SCTP_INITMSG,
&initmsg, sizeof (initmsg) );
bzero( ( void *)&saddr, sizeof (saddr) );
saddr.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &saddr.sin_addr);
saddr.sin_port = htons(29008);
connect( cfd, ( struct sockaddr *)&saddr, sizeof (saddr) );
memset ( ( void *)&events, 0, sizeof (events) );
events.sctp_data_io_event = 1;
setsockopt( cfd, SOL_SCTP, SCTP_EVENTS,
( const void *)&events, sizeof (events) );
/* Sending three messages on different streams */ for (i=0; i<3; i++) {
bzero( ( void *)&buffer, sizeof (buffer) );
sctp_recvmsg( cfd, ( void *)buffer, sizeof (buffer),
( struct sockaddr *)NULL, 0, &sndrcvinfo, &flags );
printf ( "Received following data on stream %d\n\n%s\n" ,
sndrcvinfo.sinfo_stream, buffer);
}
close(cfd);
return 0;
} |
The server is sending three messages on three different streams, and the client is just receiving the messages and printing them on the screen (see Figures 1 and 2).
Figure 1: Server output
Figure 2: Client output
The code is similar to the TCP client, as we are again making calls to the same functions [refer toParts 1, 2, 3 and 4 of this series.]. The difference is that we are creating an iterative server, similar to the one for UDP, but we have an accept()
call here. The client does the reverse, and receives the messages coming from the server to the client. Now let’s try to understand the functions that we used:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/sctp.h> int sctp_sendmsg ( int sd, 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);
|
We are using this function to send a message from a socket while using the advanced features of SCTP. The first argument to the function is sd
, the socket descriptor, from which the messagemsg
of length len
is sent. The fourth argument is to give the destination address — tolen
specifies the length of the destination address, while stream_no
identifies the stream number to send this message to. The flags
parameter is used to send some options to the receiver. You can check out the manual pages for sctp_sendmsg()
.
The timetolive
parameter is time in milliseconds after which the message will expire if not sent by then; the zero here indicates that no time-out is set. The context is the value passed to the upper layer along with the undelivered message, if an error occurs while sending the message. When successful, it will return the number of bytes sent, or -1 on error.
Next is the stcp_recvmsg()
function:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/sctp.h> int sctp_recvmsg( int sd, void * msg, size_t len, struct
sockaddr * from, socklen_t * fromlen, struct sctp_sndrcvinfo
* sinfo, int * msg_flags);
|
This function does the reverse of the sctp_sendmsg
function and is used to receive a message. The parameters are similar. The socket sd
receives the msg
of length len
from the address *from
with a length *fromlen
, and *sinfo
is a pointer to the address, which will be filled upon receipt of the message. mag_flags
is a pointer to an integer with flags like MSG_NOTIFICATION
or MSG_EOR
. It returns the number of bytes received, or -1 on error.
#include <sys/types.h> #include <sys/socket.h> int setsockopt( int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
|
This function is used to set the options for the socket sockfd
. The next argument is the level at which the option resides. To manipulate options at the sockets API level, the level is specified asSOL_SOCKET
. optname
and any specified options are passed uninterpreted to the appropriate protocol module for interpretation. The level
and optname
are defined in sys/sockets.h
. The arguments optval
and optlen
are used to access option values for setsockopt()
that are stored in the structure. The options we set in the server are:
initmsg.sinit_num_ostreams = 3; initmsg.sinit_max_instreams = 3; initmsg.sinit_max_attempts = 2; |
Here, the first two lines tell us that the output and input streams available are three, and the maximum attempts will be two. The same options are set in the client program. Other options are set for the events. This structure will be filled when an event like “message received” occurs, and our program is notified. Its counterpart function is getsockopts()
(look up the man pages for help). The rest of the code is simple to understand.
Now compile and run the program; make sure you have installed sctp-tools
so that you’ll havesctp.h
at netinet/
. To compile, use gcc sctpserver.c -lsctp -o server && gcc sctpclient.c -lsctp -o client
; and to run, use the following code:
$ ./server & $ ./client |
I’m wrapping up the series for now, but will keep adding more to the topic, on and off.
The Socket API, Part 5: SCTP的更多相关文章
-
Creating Your Own Server: The Socket API, Part 1
转:http://www.linuxforu.com/2011/08/creating-your-own-server-the-socket-api-part-1/ By Pankaj Tanwar ...
-
Creating Your Own Server: The Socket API, Part 2
转:http://www.linuxforu.com/2011/09/creating-your-own-server-the-socket-api-part-2/ By Pankaj Tanwar ...
-
UNIX网络编程——SOCKET API和TCP STATE的对应关系_三次握手_四次挥手及TCP延迟确认
在socket系统调用中,如何完成三次握手和四次挥手: SOCK_DGRAM即UDP中的connect操作知识在内核中注册对方机器的IP和PORT信息,并没有建立连接的过程,即没有发包,close也不 ...
-
c/c++ socket API 调用后的错误判断 perror errno
socket API 调用后的错误判断 perror errno 调用完socket API后,需要判断调用是否成功与失败.如果失败,会自动设置errno(是个整数), 并且用perror可以打印出具 ...
-
JNI 和 socket api
1.JavaVM 和 JNIEnvJNIEnv是一个与线程相关的变量,不同线程的JNIEnv彼此独立.JavaVM是虚拟机在JNI层的代表,在一个虚拟机进程中只有一个JavaVM,因此该进程的所有线程 ...
-
LwIP - raw/callback API、协议栈API(sequential API)、BSD API(或者说 SOCKET API)
1.使用raw/callback API编程,用户编程的方法是向内核注册各种自定义的回调函数,回调函数是与内核实现交换的唯一方式. recv_udp, accept_function, sent_tc ...
-
socket编程 ------ BSD socket API
伯克利套接字(Berkeley sockets),也称为BSD Socket.伯克利套接字的应用编程接口(API)是采用C语言的进程间通信的库,经常用在计算机网络间的通信. BSD Socket的应用 ...
-
Python Socket API 笔记
将上节中的C#该成Python版的容易程度大大超出了我的意料之外.从来没有发现,仅仅用灰尘简单的几句话就实现了该程序的主要功能,可见python的简易和强大之处.这里先对SocketAPI 做一下总结 ...
-
TCP协议和socket API 学习笔记
本文转载至 http://blog.chinaunix.net/uid-16979052-id-3350958.html 分类: 原文地址:TCP协议和socket API 学习笔记 作者:gilb ...
随机推荐
-
Ceph剖析:消息处理
作者:吴香伟 发表于 2014/10/9 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 总体上,Ceph的消息处理框架是发布者订阅者的设计结构.Messenge ...
-
二、中间件(middleware)
1. 中间件(middleware) Django中的中间件主要实现一些附加功能,在request被用户handler处理前,以及用户handler处理后生存的response进行处理.因此 ...
-
用java程序模拟网站的登录以及文件批量上传
import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; ...
-
mysql 不能插入中文
三.#vim /etc/mysql/my.cnf .(5.5以前系统)在[client]下面加入 default-character-set=utf8 在[mysqld]下面加入default-cha ...
-
UTC 通用格式时间 转换为 时间戳,并格式化为2017-01-01 12:00:00
在使用阿里云oss获取文件列表是,发现时间格式是这样的 2016-09-20T13:45:04.000Z (尼玛,是什么鬼), 经过度娘的解答,发现这就是传说中的 UTC通用格式时间 问题来了,怎么转 ...
-
11.Flask钩子函数
在Flask中钩子函数是使用特定的装饰器的函数.为什么叫做钩子函数呢,是因为钩子函数可以在正常执行的代码中,插入一段自己想要执行的代码,那么这种函数就叫做钩子函数. before_first_requ ...
-
web框架开发-模板层
你可能已经注意到我们在例子视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python代码之中. def current_datetime(request): now = datet ...
-
2018.5.17 memcached
简介 Memcached是一个*开源的,高性能,分布式内存对象缓存系统. 安装 yum install memcached 连接 telnet HOST PORT telnet 127.0.0.1 ...
-
用doxygen自动生成文档
1. 添加符合doxygen解析规则的注释 (比如函数说明,函数参数/返回值说明) 用qt-creator可以在函数上方一行键入“/**”,然后直接回车,就可以自动生成默认的格式. 2. 安装doxy ...
-
PAT 1087 有多少不同的值(20)(STL-set代码)
1087 有多少不同的值(20 分) 当自然数 n 依次取 1.2.3.--.N 时,算式 ⌊n/2⌋+⌊n/3⌋+⌊n/5⌋ 有多少个不同的值?(注:⌊x⌋ 为取整函数,表示不超过 x 的最大自然数 ...