套接字如何在C中工作?

我对C中的套接字编程有点困惑。

您创建一个套接字,将其绑定到一个接口和一个IP地址,然后让它听。 我发现了几个网络资源,并且理解得很好。 特别是,我发现一篇文章在Unix系统下的网络编程非常有用。

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

你怎么知道数据包何时到达,数据包的大小,你是否必须自己完成所有繁重的工作?

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

简短的回答是你必须自己完成所有繁重的工作。 可以通知您有可读取的数据,但您不知道有多少字节可用。 在大多数使用可变长度数据包的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; } 

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

对于UDP,生活变得更加简单,因为您可以使用所需的数据包大小调用recv / recvfrom / recvmsg(您可能无论如何都要从源发送固定长度的数据包),并假设数据可用,它是数据包长度的倍数。 (IE你用你的发送端数据包的大小调用recv *,然后你就设置了。)

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

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

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

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

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

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

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