如何创建自己的数据包以通过UDP发送?

时间:2011-04-10 15:13:18

标签: c sockets unix client-server

我在C中创建自己的客户端 - 服务器应用程序,实现TFTP协议。在阅读TFTP的RFC并使用简单的套接字客户端 - 服务器应用程序之后,现在我对如何创建必须为TFTP协议创建的特定数据包感到困惑。

例如,WRQ数据包必须是这样的:

        2 bytes     string    1 byte     string   1 byte
        ------------------------------------------------
       | Opcode |  Filename  |   0  |    Mode    |   0  |
        ------------------------------------------------

摘自官方RFC。

我有一个.h,我在其中定义了数据包的所有结构,但我不确定我是否正确操作,而且我不幸在网上查找信息。

我为此数据包创建的结构是:

    struct WRQ {
      signed short int opcode; //2 bytes
      char * filename; // N bytes
    char zero_0; // 1 byte
      char * mode; // N Bytes
    char zero_1; // 1 byte
    };

我有两个疑问:

a)当我创建sizeof(struct WRQ)时,它返回20个字节。这不是我想要的尺寸。为什么会这样?

b)我如何定义字符串?因为我希望服务器接收字符串本身,而且,我认为,这样,它将收到客户端计算机中字符串的指针。

我希望一切都清楚,你可以帮助我,因为我现在卡住了!

4 个答案:

答案 0 :(得分:2)

以下代码(没有错误检查)是构建数据包并发送数据包的一种可能方法。请注意,它假定filenamemode都不是NULL。我不知道这是否是一个有效的假设。即使它是,在使用实际代码之前检查NULL是明智的:

struct WRQ *p;
int packetLen;
char *buf;
char *pos;
int ret;

// compute packet length.  Start with fixed size data
packetLen = sizeof( p->opcode ) + sizeof( p->zero_0 ) + sizeof( p->zero_1 );
// This assumes (possibly incorrectly) that filename and mode are not null
packetLen += strlen( p->filename ) + 1;
packetLen += strlen( p->mode ) + 1;

// allocate the buffer
buf = malloc( packetLen ); 
pos = buf;

// and start filling it in
// I am assuming (but didn't study the RFC that it should be network byte order)
*(signed short int*)pos = htons( p->opcode );
pos += sizeof( p->opcode );
strcpy( pos, p->filename );
pos += strlen( p->filename ) + 1;
*pos = p->zero_0;
strcpy( pos, p->mode );
pos += strlen( p->mode ) + 1;
*pos = p->zero_1;

ret = send( s, buf, packetLen, flags );

free( buf );

答案 1 :(得分:1)

你不能只在那里放一个char*并希望它能够工作,因为那是一个指针,你需要在数据包中显示实际的字符数据(在两个程序之间传递的指针几乎不会工作!)。由于数据包的文件名部分是可变长度的,因此您无法将整个数据包表示为您尝试执行的结构。相反,您可能应该通过按需连接各个部分来动态生成数据包,例如使用具有此签名的函数:

vector<char> makePacket(uint16_t opcode, const char* filename, const char* mode);

答案 2 :(得分:1)

在您的代码中,char * filename;是指向char的指针。这意味着filename仅占用4个字节。即使你的字符串是1000字节长,因为filename只是一个指针,它只包含字符串第一个字符的4字节内存地址。

所以你有两个解决方案:使用char filename[MAX_LENGTH]来声明一个大小为MAX_LENGTH的字符串并始终传递它。或者,您可以包含另一个字段,例如“filename_length”,它会告诉您在读取文件名字段时要预期的字节数。

(以上实际上是假的。filename可能不是4个字节长。文件名将是sizeof(char*)个字节长。这可能是你计算机上的4个字节。但是指针并不总是4个字节,现在人们在64位体系结构上假设一个4字节指针会遇到很多麻烦。所以我只是说'。不要贬低我。)

回答你的第二个问题 - 为什么sizeof()是20个字节?编译器将使用额外的字节填充结构的片段,以便每个成员都适合4字节的边界。编译器可以有效地使用“单词”而不是奇怪的大小结构。 (同样,“4”只取决于架构。每个架构都有自己的“字”长度。)这称为对齐。有一个很好的SO线程,它提供了更多细节:Why isn't sizeof for a struct equal to the sum of sizeof of each member?

答案 3 :(得分:0)

C结构不能具有可变大小。您必须将“filename”和“mode”定义为char field_name [preset size];,或者在运行时手动在内存缓冲区中构造结构。

最后,如果您需要的是C中的TFTP实现,您可以打赌有人已经写过了。例如。在各种Linux发行版中都使用了BSD'ed tftp-hpa