创建SOCK_RAW套接字只是为了在没有任何recvform的情况下发送数据()

Fan*_*Fan 8 sockets linux raw-sockets

如果我创建一个类型为SOCK_RAW的套接字只发送一些数据而不接收任何数据,那么当内核继续接收网络数据包并将其数据报复制到somebuffer(应用程序?)时是否有任何问题.换句话说,在填充一些缓冲区后会发生什么?错误或忽略?

我不知道如何防止内核将数据报副本传递给我的应用程序.

参考http://sock-raw.org/papers/sock_raw 0x4 raw_input

在IP层处理新的传入IP数据报之后,它调用ip_local_deliver_finish()内核函数,该函数负责通过检查IP头的协议字段来调用已注册的传输协议处理程序(从上面记得).但是,在将数据报传递给处理程序之前,它每次都会检查应用程序是否创建了具有相同协议编号的原始套接字.如果有一个或多个此类应用程序,它会复制数据报并将其传递给它们.

phs*_*sym 4

您可以使用shutdown(2)来关闭套接字的接收部分。请参阅关闭手册页

编辑:我发现shutdown仅适用于已连接的(即 TCP)套接字。对于原始套接字,有两种可能性:

  • 将数据接收到临时缓冲区(使用recv)并丢弃它们(可能在其他线程中)
  • 如果我没记错的话,当套接字缓冲区已满时,传入的数据将被自动丢弃(并且缓冲区中的数据不会被修改),因此您可以将套接字接收缓冲区大小设置为 0(如果需要,稍后可以增加它)。

以下是将接收缓冲区大小设置为 0 的方法:

int opt = 0;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
Run Code Online (Sandbox Code Playgroud)

测试

/**
 * @file raw_print_pkt.c
 * @brief 
 * @author Airead Fan <fgh1987168@gmail.com>
 * @date 2012/08/22 12:35:22
 */

#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

int main(int argc, char *argv[])
{
    int s;
    ssize_t rn;                 /* receive number */
    struct sockaddr_in saddr;
    char packet[4096];
    int count;

    if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
        perror("error:");
        exit(EXIT_FAILURE);
    }

    memset(packet, 0, sizeof(packet));
    socklen_t *len = (socklen_t *)sizeof(saddr);
    int fromlen = sizeof(saddr);
    int opt = 0;

    count = 0;
    while(1) {
        if ((rn = recvfrom(s, (char *)&packet, sizeof(packet), 0,
                           (struct sockaddr *)&saddr, &fromlen)) < 0)
            perror("packet receive error:");
        if (rn == 0) {
            printf("the peer has performed an orderly shutdown\n");
            break;
        }

        printf("[%d] rn = %lu \n", count++, rn);

        if (count == 16) {
            if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
                perror("setsocketopt failed");
            } else {
                fprintf(stdout, "setsocketopt successful\n");
            }
            // int shutdown(int sockfd, int how);
            /* if (shutdown(s, SHUT_RD) < 0) {
             *     perror("shutdown failed");
             * } */
        }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

测试2(同样包括):

int main(int argc, char *argv[])
{
int s;
ssize_t rn;                 /* receive number */
char packet[4096];
int count;

if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
    perror("error:");
    exit(EXIT_FAILURE);
}

memset(packet, 0, sizeof(packet));
int opt = 0;
count = 0;

//Set recv buffer size
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
    perror("setsocketopt failed");
} else {
    fprintf(stdout, "setsocketopt successful\n");
}

//10 seconds countdown
int i = 10;
while(i > 0)
{
    printf("\r%d              ", i);
    fflush(stdout);
    i--;
    sleep(1);
}
printf("\n");
while(1) {
    if ((rn = recv(s, (char *)&packet, sizeof(packet), 0)) <= 0)
        perror("packet receive error:");

    printf("[%d] rn = %lu \n", count++, rn);
    }
return 0;
}
Run Code Online (Sandbox Code Playgroud)

以下是进行测试 2 的方法:

首先,将缓冲区大小设置为 4096(如果网络流量很大,则设置更大)。编译并启动。在开始接收数据之前的10秒内,向套接字发送大量数据。10 秒后,程序将收到您在倒计时期间发送的所有内容。

之后,将缓冲区大小设置为 0。按照之前的步骤进行。10秒后,程序将不会收到您在倒计时期间发送的数据。但是如果你在recvfrom中发送数据,它会正常读取它们。