UNIX网络编程中SCTP接收重复数据包问题求助
SCTP客户端重复接收首个数据包问题排查
问题背景
在UNIX网络编程的SCTP应用场景中,实现了客户端与回显服务器:
- 携带
echo参数启动客户端时,会调用sctpstr_cli_echoall函数,向服务器所有可用流各发送一条消息 - 服务器将收到的数据按原流回显给客户端
- 运行后客户端输出显示首个数据包被重复接收
客户端代码
#include "unp.h" int main(int argc, char **argv) { int sock_fd; struct sockaddr_in servaddr; struct sctp_event_subscribe evnts; int echo_to_all = 0; if (argc < 2) err_quit("Missing host argument - use '%s host [echo]'\n", argv[0]); if (argc > 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; Setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts)); 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); }
客户端多流发送接收函数:sctpstr_cli_echoall
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)); 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 < SERV_MAX_SCTP_STRM; i++) { snprintf(sendline + strsz, sizeof(sendline) - strsz, ".msg.%d", i); Sctp_sendmsg(sock_fd, sendline, sizeof(sendline), to, tolen, 0, 0, i, 0, 0); } for (i = 0; i < SERV_MAX_SCTP_STRM; i++) { 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, (unsigned int)sri.sinfo_assoc_id); printf("%.*s\n", rd_sz, recvline); } } }
服务器相关代码(注:用户提供的服务器主逻辑存在粘贴重复,以下为有效代码片段)
// 获取服务器可用流数量的函数 int sctp_get_no_strms(int sock_fd, sctp_assoc_t sinfo_assoc_id) { int retsz; struct sctp_status status; retsz = sizeof(status); bzero(&status, sizeof(status)); status.sstat_assoc_id = sinfo_assoc_id; Getsockopt(sock_fd, IPPROTO_SCTP, SCTP_STATUS, &status, &retsz); return (status.sstat_outstrms); } // 正确的服务器回显逻辑参考(用户提供的主逻辑重复了客户端函数,需替换为此类代码) /* void sctp_echo(int sock_fd, struct sockaddr *cliaddr, socklen_t clilen) { struct sctp_sndrcvinfo sri; char buf[SCTP_MAXLINE]; int rd_sz, msg_flags; socklen_t len; for ( ; ; ) { len = clilen; rd_sz = Sctp_recvmsg(sock_fd, buf, sizeof(buf), cliaddr, &len, &sri, &msg_flags); if (rd_sz == 0) break; Sctp_sendmsg(sock_fd, buf, rd_sz, cliaddr, len, sri.sinfo_ppid, sri.sinfo_flags, sri.sinfo_stream, 0, 0); } } */
问题排查与修复方案
核心原因分析
- 未过滤SCTP通知消息:客户端开启了
sctp_data_io_event事件订阅,首次建立SCTP关联时,系统会发送关联建立通知(如SCTP_COMM_UP),当前代码未区分通知消息与用户数据,将通知误当成回显数据处理,造成“重复接收”假象。 - 硬编码流数量不匹配:客户端使用
SERV_MAX_SCTP_STRM硬编码流数量,若与服务器实际支持的流数量不一致,会导致接收循环次数与发送消息数不匹配。 - 服务器代码逻辑错误:用户提供的服务器主逻辑重复了客户端函数,实际运行时若服务器存在重复发送逻辑,也会导致客户端重复接收。
修复步骤
- 过滤SCTP通知消息:在客户端接收循环中增加标志位判断,跳过非数据消息:
rd_sz = Sctp_recvmsg(sock_fd, recvline, sizeof(recvline), (SA *)&peeraddr, &len, &sri, &msg_flags); // 跳过SCTP通知消息 if (msg_flags & MSG_NOTIFICATION) { continue; }
- 动态获取服务器流数量:替换硬编码的流数量,使用
sctp_get_no_strms获取实际可用流数:
// 在发送前获取服务器实际流数量 int num_streams = sctp_get_no_strms(sock_fd, 0); // 0表示当前关联 // 发送循环 for (i = 0; i < num_streams; i++) { // 原发送逻辑 } // 接收循环 for (i = 0; i < num_streams; i++) { // 原接收逻辑(加上通知过滤) }
- 修正服务器代码:确保服务器使用正确的回显逻辑,仅在收到用户数据时按原流回显,无重复发送操作。
内容的提问来源于stack exchange,提问作者raijin
相关产品推荐
相关产品推荐

