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

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

问题描述:

我正在C中实现TFTP协议的自己的客户端 - 服务器应用程序。在阅读了TFTP的RFC并开发了一个简单的套接字客户机 - 服务器应用之后,现在我对如何创建必须为TFTP协议创建的特定数据包有点困惑。如何创建我自己的数据包通过UDP发送?

例如,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 
    }; 

我有两个疑惑:

一个)当我做出的sizeof(结构WRQ)它返回20个字节。这不是我想要的尺寸。为什么发生这种情况?

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

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

+0

你可能会得到答案你的问题http://*.com/questions/5596428/sizeofstruct-returns-unexpected-value – Peacelyk 2011-04-10 15:21:24

+0

问:如何字符串长度在数据包中处理?它们是空终止的,有一个字节长度字段,还是固定的长度? – Duck 2011-04-10 15:28:02

+0

谢谢Peacelyk的答案! – 2011-04-10 15:32:30

在你的代码中,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?

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

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

我认为它不能表达这种方式,所以...我怎么能做到这一点?我从来没有使用C中的矢量类型...我不知道如何使用它来确定数据包的字段...谢谢! – 2011-04-10 15:36:29

+0

对不起,我说'vector '但这是C++的东西!如果你使用直接C语言,那么函数需要返回一个C字符串(要么分配并返回一个char *,调用者必须释放这个char *,要么传入一个char *缓冲区和大小 - 我要做后者)。 – 2011-04-10 15:44:41

下面的代码(有没有错误检查)是建立在数据包的一个可能的方式并发送它。请注意,它假定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); 
+0

我会试试这个!谢谢! – 2011-04-10 16:39:15

C结构不能具有可变大小。您必须将“文件名”和“模式”定义为char field_name [preset size];或在运行时手动构建内存缓冲区中的结构。最后,如果你需要的是TF中的TFTP实现,你可以打赌,如果已经有人写了,你可以打赌。例如。在各种Linux发行版中使用了BSD'ed tftp-hpa

+0

我会尽量让它在char * buff变量中做,谢谢! – 2011-04-10 16:38:46