- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我一直在用 C 开发这个简单的客户端-服务器应用程序,其中客户端只是向服务器发送随机数据,而服务器只是监听客户端发送的内容。我使用的协议(protocol)是 SCTP,我对如何为其实现多宿主功能很感兴趣。
我一直在 Internet 上搜索有关 SCTP 和多宿主的信息,但未能找到任何有关如何指示 SCTP 使用多个地址进行通信的示例。我只找到了在尝试使用多宿主设置 SCTP 时应该使用哪些命令,它应该非常简单。
我创建了一个客户端和一个服务器,它们都使用我的计算机的两个 WLAN 接口(interface)作为它们的连接点。两个适配器都连接到同一个 AP。服务器正在从这些接口(interface)监听来自客户端的数据,客户端通过它们发送数据。问题是,当我断开客户端正在向其发送数据的主要 WLAN 适配器时,传输只是在它应该回退到辅助连接时停止。我用 Wireshark 跟踪了数据包,第一个 INIT 和 INIT_ACK 数据包报告客户端和服务器都在使用 WLAN 适配器作为它们的通信链路。
当我重新连接主要 WLAN 连接时,传输会在一段时间后继续,并向服务器突发大量数据包负载,这是不正确的。数据包应该已经通过辅助连接传输。在许多站点上,据说 SCTP 会自动在连接之间切换,但在我的情况下并没有发生。那么你们是否知道为什么当主链路断开时传输不回退到辅助连接,即使客户端和服务器知道彼此的地址(包括辅助地址)?
关于服务器:
服务器创建一个 SOCK_SEQPACKET 类型套接字并绑定(bind)所有与 INADDR_ANY 找到的接口(interface)。 getladdrs 报告服务器绑定(bind)到 3 个地址(包括 127.0.0.1)。之后,服务器监听套接字并等待客户端发送数据。服务器使用 sctp_recvmsg 调用读取数据。
关于客户:
客户端还创建一个 SEQPACKET 套接字并连接到由命令行参数指定的 IP 地址。在这种情况下,getladdrs 也返回 3 个地址,就像服务器的情况一样。之后客户端才开始向服务器发送数据,延迟一秒到服务器,直到用户使用 Ctrl-C 中断发送。
这是一些源代码:
服务器:
#define BUFFER_SIZE (1 << 16)
#define PORT 10000
int sock, ret, flags;
int i;
int addr_count = 0;
char buffer[BUFFER_SIZE];
socklen_t from_len;
struct sockaddr_in addr;
struct sockaddr_in *laddr[10];
struct sockaddr_in *paddrs[10];
struct sctp_sndrcvinfo sinfo;
struct sctp_event_subscribe event;
struct sctp_prim prim_addr;
struct sctp_paddrparams heartbeat;
struct sigaction sig_handler;
void handle_signal(int signum);
int main(void)
{
if((sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0)
perror("socket");
memset(&addr, 0, sizeof(struct sockaddr_in));
memset((void*)&event, 1, sizeof(struct sctp_event_subscribe));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(PORT);
from_len = (socklen_t)sizeof(struct sockaddr_in);
sig_handler.sa_handler = handle_signal;
sig_handler.sa_flags = 0;
if(sigaction(SIGINT, &sig_handler, NULL) == -1)
perror("sigaction");
if(setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(struct sctp_event_subscribe)) < 0)
perror("setsockopt");
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))< 0)
perror("setsockopt");
if(bind(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)) < 0)
perror("bind");
if(listen(sock, 2) < 0)
perror("listen");
addr_count = sctp_getladdrs(sock, 0, (struct sockaddr**)laddr);
printf("Addresses binded: %d\n", addr_count);
for(i = 0; i < addr_count; i++)
printf("Address %d: %s:%d\n", i +1, inet_ntoa((*laddr)[i].sin_addr), (*laddr)[i].sin_port);
sctp_freeladdrs((struct sockaddr*)*laddr);
while(1)
{
flags = 0;
ret = sctp_recvmsg(sock, buffer, BUFFER_SIZE, (struct sockaddr*)&addr, &from_len, NULL, &flags);
if(flags & MSG_NOTIFICATION)
printf("Notification received from %s:%u\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
printf("%d bytes received from %s:%u\n", ret, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
if(close(sock) < 0)
perror("close");
}
void handle_signal(int signum)
{
switch(signum)
{
case SIGINT:
if(close(sock) != 0)
perror("close");
exit(0);
break;
default: exit(0);
break;
}
}
和客户:
#define PORT 10000
#define MSG_SIZE 1000
#define NUMBER_OF_MESSAGES 1000
#define PPID 1234
int sock;
struct sockaddr_in *paddrs[10];
struct sockaddr_in *laddrs[10];
void handle_signal(int signum);
int main(int argc, char **argv)
{
int i;
int counter = 1;
int ret;
int addr_count;
char address[16];
char buffer[MSG_SIZE];
sctp_assoc_t id;
struct sockaddr_in addr;
struct sctp_status status;
struct sctp_initmsg initmsg;
struct sctp_event_subscribe events;
struct sigaction sig_handler;
memset((void*)&buffer, 'j', MSG_SIZE);
memset((void*)&initmsg, 0, sizeof(initmsg));
memset((void*)&addr, 0, sizeof(struct sockaddr_in));
memset((void*)&events, 1, sizeof(struct sctp_event_subscribe));
if(argc != 2 || (inet_addr(argv[1]) == -1))
{
puts("Usage: client [IP ADDRESS in form xxx.xxx.xxx.xxx] ");
return 0;
}
strncpy(address, argv[1], 15);
address[15] = 0;
addr.sin_family = AF_INET;
inet_aton(address, &(addr.sin_addr));
addr.sin_port = htons(PORT);
initmsg.sinit_num_ostreams = 2;
initmsg.sinit_max_instreams = 2;
initmsg.sinit_max_attempts = 5;
sig_handler.sa_handler = handle_signal;
sig_handler.sa_flags = 0;
if(sigaction(SIGINT, &sig_handler, NULL) == -1)
perror("sigaction");
if((sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0)
perror("socket");
if((setsockopt(sock, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg))) != 0)
perror("setsockopt");
if((setsockopt(sock, SOL_SCTP, SCTP_EVENTS, (const void *)&events, sizeof(events))) != 0)
perror("setsockopt");
if(sendto(sock, buffer, MSG_SIZE, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr)) == -1)
perror("sendto");
addr_count = sctp_getpaddrs(sock, 0, (struct sockaddr**)paddrs);
printf("\nPeer addresses: %d\n", addr_count);
for(i = 0; i < addr_count; i++)
printf("Address %d: %s:%d\n", i +1, inet_ntoa((*paddrs)[i].sin_addr), (*paddrs)[i].sin_port);
sctp_freepaddrs((struct sockaddr*)*paddrs);
addr_count = sctp_getladdrs(sock, 0, (struct sockaddr**)laddrs);
printf("\nLocal addresses: %d\n", addr_count);
for(i = 0; i < addr_count; i++)
printf("Address %d: %s:%d\n", i +1, inet_ntoa((*laddrs)[i].sin_addr), (*laddrs)[i].sin_port);
sctp_freeladdrs((struct sockaddr*)*laddrs);
i = sizeof(status);
if((ret = getsockopt(sock, SOL_SCTP, SCTP_STATUS, &status, (socklen_t *)&i)) != 0)
perror("getsockopt");
printf("\nSCTP Status:\n--------\n");
printf("assoc id = %d\n", status.sstat_assoc_id);
printf("state = %d\n", status.sstat_state);
printf("instrms = %d\n", status.sstat_instrms);
printf("outstrms = %d\n--------\n\n", status.sstat_outstrms);
for(i = 0; i < NUMBER_OF_MESSAGES; i++)
{
counter++;
printf("Sending data chunk #%d...", counter);
if((ret = sendto(sock, buffer, MSG_SIZE, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr))) == -1)
perror("sendto");
printf("Sent %d bytes to peer\n",ret);
sleep(1);
}
if(close(sock) != 0)
perror("close");
}
void handle_signal(int signum)
{
switch(signum)
{
case SIGINT:
if(close(sock) != 0)
perror("close");
exit(0);
break;
default: exit(0);
break;
}
}
那么你们知道我做错了什么吗?
最佳答案
好的,我终于解决了多宿主问题。这就是我所做的。
我使用 sctp_paddrparams 结构将心跳值调整为 5000 毫秒。位于结构中的标志变量必须处于 SPP_HB_ENABLE 模式,否则 SCTP 在尝试使用 setsockopt() 设置值时会忽略心跳值。
这就是为什么 SCTP 没有像我想要的那样频繁地发送心跳的原因。我没有注意到标志变量的原因是我正在阅读的过时的 SCTP 引用指南,其中指出结构中不存在标志变量!较新的引用资料显示有。这样心跳问题就解决了!
另一件事是将 rto_max 值修改为例如 2000 毫秒左右。降低该值会告诉 SCTP 更快地更改路径。默认值为 60 000 毫秒,太高了(开始更改路径前 1 分钟)。可以使用 sctp_rtoinfo 结构调整 rto_max 值。
通过这两项修改,多宿主开始工作。哦,还有一件事。当服务器处于 SEQPACKET 模式时,客户端必须处于 STREAM 模式。客户端使用正常的 send() 命令向服务器发送数据,服务器使用 sctp_recvmsg() 读取数据,其中 addr 结构设置为 NULL。
我希望这些信息能帮助其他为 SCTP 的多宿主而苦苦挣扎的人。为您的意见干杯,这些对我有很大帮助!这是一些代码示例,所以如果您问我,这可能是网络中的第一个多宿主简单示例(除了多流示例之外没有找到任何示例)
服务器:
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <stdlib.h>
#include <pthread.h>
#define BUFFER_SIZE (1 << 16)
#define PORT 10000
int sock, ret, flags;
int i, reuse = 1;
int addr_count = 0;
char buffer[BUFFER_SIZE];
socklen_t from_len;
struct sockaddr_in addr;
struct sockaddr_in *laddr[10];
struct sockaddr_in *paddrs[10];
struct sctp_sndrcvinfo sinfo;
struct sctp_event_subscribe event;
struct sctp_prim prim_addr;
struct sctp_paddrparams heartbeat;
struct sigaction sig_handler;
struct sctp_rtoinfo rtoinfo;
void handle_signal(int signum);
int main(void)
{
if((sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0)
perror("socket");
memset(&addr, 0, sizeof(struct sockaddr_in));
memset(&event, 1, sizeof(struct sctp_event_subscribe));
memset(&heartbeat, 0, sizeof(struct sctp_paddrparams));
memset(&rtoinfo, 0, sizeof(struct sctp_rtoinfo));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(PORT);
from_len = (socklen_t)sizeof(struct sockaddr_in);
sig_handler.sa_handler = handle_signal;
sig_handler.sa_flags = 0;
heartbeat.spp_flags = SPP_HB_ENABLE;
heartbeat.spp_hbinterval = 5000;
heartbeat.spp_pathmaxrxt = 1;
rtoinfo.srto_max = 2000;
/*Set Heartbeats*/
if(setsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS , &heartbeat, sizeof(heartbeat)) != 0)
perror("setsockopt");
/*Set rto_max*/
if(setsockopt(sock, SOL_SCTP, SCTP_RTOINFO , &rtoinfo, sizeof(rtoinfo)) != 0)
perror("setsockopt");
/*Set Signal Handler*/
if(sigaction(SIGINT, &sig_handler, NULL) == -1)
perror("sigaction");
/*Set Events */
if(setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(struct sctp_event_subscribe)) < 0)
perror("setsockopt");
/*Set the Reuse of Address*/
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))< 0)
perror("setsockopt");
/*Bind the Addresses*/
if(bind(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)) < 0)
perror("bind");
if(listen(sock, 2) < 0)
perror("listen");
/*Get Heartbeat Value*/
i = (sizeof heartbeat);
getsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &heartbeat, (socklen_t*)&i);
printf("Heartbeat interval %d\n", heartbeat.spp_hbinterval);
/*Print Locally Binded Addresses*/
addr_count = sctp_getladdrs(sock, 0, (struct sockaddr**)laddr);
printf("Addresses binded: %d\n", addr_count);
for(i = 0; i < addr_count; i++)
printf("Address %d: %s:%d\n", i +1, inet_ntoa((*laddr)[i].sin_addr), (*laddr)[i].sin_port);
sctp_freeladdrs((struct sockaddr*)*laddr);
while(1)
{
flags = 0;
ret = sctp_recvmsg(sock, buffer, BUFFER_SIZE, NULL, 0, NULL, &flags);
if(flags & MSG_NOTIFICATION)
printf("Notification received from %s:%u\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
printf("%d bytes received from %s:%u\n", ret, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
if(close(sock) < 0)
perror("close");
}
void handle_signal(int signum)
{
switch(signum)
{
case SIGINT:
if(close(sock) != 0)
perror("close");
exit(0);
break;
default: exit(0);
break;
}
}
客户:
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#define PORT 11000
#define MSG_SIZE 1000
#define NUMBER_OF_MESSAGES 1000
int sock;
struct sockaddr_in *paddrs[5];
struct sockaddr_in *laddrs[5];
void handle_signal(int signum);
int main(int argc, char **argv)
{
int i;
int counter = 0;
int asconf = 1;
int ret;
int addr_count;
char address[16];
char buffer[MSG_SIZE];
sctp_assoc_t id;
struct sockaddr_in addr;
struct sctp_status status;
struct sctp_initmsg initmsg;
struct sctp_event_subscribe events;
struct sigaction sig_handler;
struct sctp_paddrparams heartbeat;
struct sctp_rtoinfo rtoinfo;
memset(&buffer, 'j', MSG_SIZE);
memset(&initmsg, 0, sizeof(struct sctp_initmsg));
memset(&addr, 0, sizeof(struct sockaddr_in));
memset(&events, 1, sizeof(struct sctp_event_subscribe));
memset(&status, 0, sizeof(struct sctp_status));
memset(&heartbeat, 0, sizeof(struct sctp_paddrparams));
memset(&rtoinfo, 0, sizeof(struct sctp_rtoinfo))
if(argc != 2 || (inet_addr(argv[1]) == -1))
{
puts("Usage: client [IP ADDRESS in form xxx.xxx.xxx.xxx] ");
return 0;
}
strncpy(address, argv[1], 15);
address[15] = 0;
addr.sin_family = AF_INET;
inet_aton(address, &(addr.sin_addr));
addr.sin_port = htons(PORT);
initmsg.sinit_num_ostreams = 2;
initmsg.sinit_max_instreams = 2;
initmsg.sinit_max_attempts = 1;
heartbeat.spp_flags = SPP_HB_ENABLE;
heartbeat.spp_hbinterval = 5000;
heartbeat.spp_pathmaxrxt = 1;
rtoinfo.srto_max = 2000;
sig_handler.sa_handler = handle_signal;
sig_handler.sa_flags = 0;
/*Handle SIGINT in handle_signal Function*/
if(sigaction(SIGINT, &sig_handler, NULL) == -1)
perror("sigaction");
/*Create the Socket*/
if((ret = (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP))) < 0)
perror("socket");
/*Configure Heartbeats*/
if((ret = setsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS , &heartbeat, sizeof(heartbeat))) != 0)
perror("setsockopt");
/*Set rto_max*/
if((ret = setsockopt(sock, SOL_SCTP, SCTP_RTOINFO , &rtoinfo, sizeof(rtoinfo))) != 0)
perror("setsockopt");
/*Set SCTP Init Message*/
if((ret = setsockopt(sock, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg))) != 0)
perror("setsockopt");
/*Enable SCTP Events*/
if((ret = setsockopt(sock, SOL_SCTP, SCTP_EVENTS, (void *)&events, sizeof(events))) != 0)
perror("setsockopt");
/*Get And Print Heartbeat Interval*/
i = (sizeof heartbeat);
getsockopt(sock, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &heartbeat, (socklen_t*)&i);
printf("Heartbeat interval %d\n", heartbeat.spp_hbinterval);
/*Connect to Host*/
if(((ret = connect(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)))) < 0)
{
perror("connect");
close(sock);
exit(0);
}
/*Get Peer Addresses*/
addr_count = sctp_getpaddrs(sock, 0, (struct sockaddr**)paddrs);
printf("\nPeer addresses: %d\n", addr_count);
/*Print Out Addresses*/
for(i = 0; i < addr_count; i++)
printf("Address %d: %s:%d\n", i +1, inet_ntoa((*paddrs)[i].sin_addr), (*paddrs)[i].sin_port);
sctp_freepaddrs((struct sockaddr*)*paddrs);
/*Start to Send Data*/
for(i = 0; i < NUMBER_OF_MESSAGES; i++)
{
counter++;
printf("Sending data chunk #%d...", counter);
if((ret = send(sock, buffer, MSG_SIZE, 0)) == -1)
perror("write");
printf("Sent %d bytes to peer\n",ret);
sleep(1);
}
if(close(sock) != 0)
perror("close");
}
void handle_signal(int signum)
{
switch(signum)
{
case SIGINT:
if(close(sock) != 0)
perror("close");
exit(0);
break;
default: exit(0);
break;
}
}
关于c - SCTP多宿主,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6342617/
我正在使用 Linux SCTP 堆栈。目前在同一台 Linux 机器上,我需要部署一个使用非操作系统 SCTP STACK 的进程。 (即它打开一个 RAW 套接字,然后自己处理传输级别的消息)。当
为了我的论文,我正在研究并展示一种新的通信协议(protocol),我们的老师没有跟我们谈过。这是SCTP。 现在我正在写论文,解释协议(protocol)并强调 SCTP 和 TCP 之间的区别。无
是否可以强制 SCTP 发送完全有序的所有数据? 让我们做这个实验: 1) 拿这个 SCTP-discard-server而这个 SCTP-client . 2)让客户端多次计数到100,每次分别向服
我正在使用SCTP客户端通过100m秒的延迟链路将1000byte数据发送到另一个SCTP服务器。使用Linux中可用的流量控制(tc)和netem配置延迟 tc qdisc add dev eth0
我在一个小型服务器上有这段代码,它使用 SCTP 连接从客户端获取请求,我时不时地收到这个错误。 BlockingIOError: [Errno 11] Resource temporarily un
我在 SCTP 中作为服务器端实现多宿主时遇到了问题。服务器有 2 个正在监听的 IP。我快到了,但有 2 个问题: 第一个 IP 返回 INIT-ACK, header 内有 2 个不同的 IP,但
我正在做一个网络C 库,我也想支持SCTP。我在这个文件中有 wrapper_socket_functions.c /*SCTP WRAPPER */ void Sctp_bindx(int sd,
前段时间我在 Linux 上使用过 SCTP,现在我想在新项目中再次使用它。问题是,lksctp 似乎已经死了,自 2009 年以来没有更新(根据 git)。在这段时间里,SCTP 草案中几乎没有什么
我正在调查 SCTP协议(protocol)。它的功能看起来很有吸引力。我担心的是它来自社区的支持。是否有任何好的跨平台(至少 Linux 和 Windows)C++(或至少 C)SCTP 库?是否支
我遇到了一个问题,不确定我是否可以将其称为问题或只是理解上的差距。 我正在 SCTP 套接字 FD 上调用 close()(类似这样的东西:close(sctp_sock_fd);)。我期待这个关闭调
我正在尝试构建一个非常简单且基本的STCP一对多服务器以进行测试,但是由于某种原因,套接字似乎只能接收一条消息。收到一条消息后,对recvmsg的每个后续调用均返回-1,且errno为EFAULT。这
我发现 SCTP 将用于从 Chrome 31 开始的数据通道,并且根据此 Google 小组帖子,基于 RTP 的正式 channel 有时将在 2014 年 2 月被弃用: https://gro
我正在努力 retrieve information about a specific peer address of anassociation, including its reachabilit
我在Linux 4.1.0上,安装了iperf3(版本3.1)来运行SCTP连接测试。在iperf3的手册页上,指示了要用于实例化SCTP连接的选项:“SCTP”。 运行iperf3 --client
建立一对一sctp连接时,sctp连接accept()后内部服务器日志报如下错误: "Error getting socket options for socket: 13" 从错误来看,getsoc
在 3.x Linux 内核中,内核和 uapi 之间的 sctp 状态定义不同。这是不匹配还是我遗漏了什么? 在 include/uapi/linux/sctp.h /* Association s
我一直在尝试为网络部署测试 SCTP。我没有 SCTP 服务器或客户端,希望能够使用 pysctp。 我相当确定我的客户端代码可以正常工作。 def sctp_client (): print
我目前正在开发一个使用面向连接的 SCTP 的服务器来为少量客户端提供服务。在完成第一个带有简单实现的原型(prototype)之后,我现在正在分析要优化的应用程序。事实证明,CPU 时间的两个主要消
这与问题有关:SCTP with Multihoming as a Drop In Replacement for TCP 我有一个简单的回显客户端/并发服务器应用程序,它使用 TCP 运行得非常好。
我在搞乱套接字,我想在我的 mac 上使用 SCTP 协议(protocol)进行测试。 但是,当我尝试包含文件 netinet/sctp.h编译器给了我一个“找不到文件”错误。 所以我做了一些挖掘,
我是一名优秀的程序员,十分优秀!