套接字如何在C中工作?

时间:2008-09-08 00:00:32

标签: c sockets network-programming

我对C语言中的套接字编程感到有点困惑。

您创建一个套接字,将其绑定到一个接口和一个IP地址并让它听。我发现了几个网络资源,并且理解得很好。特别是,我发现文章 Network programming under Unix systems 非常有用。

令我困惑的是数据到达套接字的时间。

如何判断数据包何时到达以及数据包的大小,您是否必须自己完成所有繁重的工作?

我的基本假设是数据包可以是可变长度的,所以一旦二进制数据开始出现在套接字中,你如何从那开始构造数据包呢?

4 个答案:

答案 0 :(得分:17)

简短的回答是你必须自己完成所有繁重的工作。可以通知您有可读取的数据,但您不知道有多少字节可用。在大多数使用可变长度数据包的IP协议中,将有一个标头,其前面包含已知的固定长度。此标头将包含数据包的长度。您读取标头,获取数据包的长度,然后读取数据包。重复此模式(读取标头,然后读取数据包),直到通信完成。

从套接字读取数据时,请求一定数量的字节。读取调用可能会阻塞,直到读取所请求的字节数,但它可以返回的字节数少于请求的字节数。发生这种情况时,您只需重试读取,请求剩余的字节。

这是从套接字读取设定字节数的典型C函数:

/* buffer points to memory block that is bigger than the number of bytes to be read */
/* socket is open socket that is connected to a sender */
/* bytesToRead is the number of bytes expected from the sender */
/* bytesRead is a pointer to a integer variable that will hold the number of bytes */
/*           actually received from the sender. */
/* The function returns either the number of bytes read, */
/*                             0 if the socket was closed by the sender, and */
/*                            -1 if an error occurred while reading from the socket */
int readBytes(int socket, char *buffer, int bytesToRead, int *bytesRead)
{
    *bytesRead = 0;
    while(*bytesRead < bytesToRead)
    {
        int ret = read(socket, buffer + *bytesRead, bytesToRead - *bytesRead);
        if(ret <= 0)
        {
           /* either connection was closed or an error occurred */
           return ret;
        }
        else
        {
           *bytesRead += ret;
        }
    }
    return *bytesRead;
}

答案 1 :(得分:13)

因此,您的问题的答案取决于您是否使用UDP或TCP作为传输。

对于UDP,生活变得更加简单,因为您可以使用所需的数据包大小调用recv / recvfrom / recvmsg(您可能无论如何都要从源发送固定长度的数据包),并假设如果数据可用,它有多个包长度大小。 (例如,您使用发送方数据包的大小调用recv *,然后进行设置。)

对于TCP,生活变得更有趣 - 为了解释的目的,我将假设您已经知道如何使用socket(),bind(),listen()和accept() - 后者是如何你得到你新建连接的文件描述符(FD)。

对于套接字执行I / O有两种方法 - 阻塞,在其中调用read(fd,buf,N)并且读取位于那里并等待直到您将N个字节读入buf - 或非-blocking,你必须检查(使用select()或poll())FD是否可读,然后你的read()。

在处理基于TCP的连接时,操作系统不会关注数据包大小,因为它被认为是连续的数据流,而不是单独的数据包大小的块。

如果您的应用程序使用“数据包”(您传递的打包或解包数据结构),您应该能够使用正确的大小参数调用read(),并从套接字读取整个数据结构一时间您必须处理的唯一警告是,在源和目标系统具有不同字节字节的情况下,记住要正确地对您发送的任何数据进行字节顺序排序。这适用于UDP和TCP。

就* NIX套接字编程而言,我强烈推荐W. Richard Stevens的“Unix网络编程,第1卷”(UNPv1)和“Unix环境中的高级编程”(APUE)。第一个是关于基于网络的编程的一本书,无论传输如何,后者是一个很好的全面编程书,因为它适用于基于* NIX的编程。另外,请查看“TCP / IP Illustrated”,第1卷和第2卷。

答案 2 :(得分:3)

当您对套接字执行读操作时,您会告诉它要读取多少个最大字节数,但是如果它没有那么多,那么它会为您提供多少字节。您可以自行设计协议,以便了解是否有部分数据包。例如,在过去发送可变长度的二进制数据时,我会在开头放一个int表示期望的字节数。我做一个读取请求比我的协议中最大可能的数据包大的字节数,然后我将第一个int与我收到的多少字节进行比较,并处理它或尝试更多读取,直到我' d得到了完整的数据包,取决于。

答案 3 :(得分:1)

套接字的运行级别高于原始数据包 - 它就像一个可以读/写的文件。此外,当您尝试从套接字读取时,操作系统将阻止(暂停)您的进程,直到它有数据来满足请求。