0%

一个简单的UDP测试程序

一个简单的UDP测试程序

功能介绍

这是一个简单的UDP测试程序,用于UDP数据包的发送、接收测试。

它能干什么?

它集成了客户端和服务端两个部分,可以通过命令行的启动参数进行状态转换。

它可以按照一定的时间进行UDP数据包的发送,也可以设定发送的UDP数据包的负载长度,当然也可以设定每一轮发送数据包的数量。此外,服务进程监听的端口号及客户进程发送时的目标端口也是可以设定的。

不过,需要确定的是,发送时的IP地址和端口号必须是可连通的。比如,发送端和接收端的设备应该处于同一个子网中,如果发生跨子网的情况,则无法成功进行数据传送。

代码展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#incnclude <stdio.h>
#include <getopt.h>
#include <stdbool.h>
#incle <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
char *l_opt_arg;
char* const short_options = "c:l:i:srhp:a:y:";
/*
struct option
{
const char *name;
int has_arg;
int *flag;
int val;
};
*/
struct option long_options[] = {
{ "count", 1, NULL, 'c' },
{ "length", 1, NULL, 'l' },
{ "interval", 1, NULL, 'i' },
{ "port", 1, NULL, 'p' },
{ "ip", 1, NULL, 'a' },
{ "cycles", 1, NULL, 'y' },
{ "send", 0, NULL, 's' },
{ "receive", 0, NULL, 'r' },
{ "help", 0, NULL, 'h' },
{ 0, 0, 0, 0}
};
void printHelp();
struct ControlArguments{
bool isSend;
bool isReceive;
int SendCount; //packets to send at one time,only for send option,default 1
int PacketLength; //data length in UDP packet,only for send option,default 100
int Interval; //time between send cycles.default is 20ms,only for send option
int Port; //port used for transmit
int Cycles; //operation cycles,only for send option,default 10
char IP[15]; //target ip address,only for send option
};
void sendUDP(struct ControlArguments * const);
void receiveUDP(struct ControlArguments * const);
void initControlArguments(struct ControlArguments * const controlArguments){
controlArguments->isSend = false;
controlArguments->isReceive = false;
controlArguments->SendCount = 1;
controlArguments->PacketLength = 100;
controlArguments->Interval = 20;
controlArguments->Port = 0;
controlArguments->Cycles = 10;
controlArguments->IP[0] = '\0';
}
int charPoint2Int(char const * string){
return atoi(string);
}
int main(int argc, char *argv[])
{
int c;
int index=0;
bool argumentOK = true;
static struct ControlArguments controlArguments;
initControlArguments(&controlArguments);
while((c = getopt_long (argc, argv, short_options, long_options, &index)) != -1)
{
switch (c)
{
case 's':
printf("Send\n");
controlArguments.isSend = true;
break;
case 'r':
printf("Receive\n");
controlArguments.isReceive = true;
break;
case 'c':
l_opt_arg = optarg;
printf("Send Count : %s\n", l_opt_arg);
controlArguments.SendCount = charPoint2Int(l_opt_arg);
break;
case 'y':
l_opt_arg = optarg;
printf("Send Cycles : %s\n", l_opt_arg);
controlArguments.Cycles = charPoint2Int(l_opt_arg);
break;
case 'l':
l_opt_arg = optarg;
printf("Packet Length : %s\n",l_opt_arg);
controlArguments.PacketLength = charPoint2Int(l_opt_arg);
break;
case 'i':
l_opt_arg = optarg;
printf("Interval Time : %s\n",l_opt_arg);
controlArguments.Interval = charPoint2Int(l_opt_arg);
break;
case 'h':
printHelp();
break;
case 'p':
l_opt_arg = optarg;
printf("Chosen Port : %s\n",l_opt_arg);
controlArguments.Port = charPoint2Int(l_opt_arg);
break;
case 'a':
l_opt_arg = optarg;
printf("Target IP : %s\n",l_opt_arg);
strncpy(controlArguments.IP,l_opt_arg,15);
break;
default:
printf("Do not match!!!\n");
argumentOK = false;
}
}
if(argumentOK){
if(controlArguments.isSend && !controlArguments.isReceive){
printf("choosing send\n");
sendUDP(&controlArguments);
}else if(controlArguments.isReceive && !controlArguments.isSend){
printf("choosing receive\n");
receiveUDP(&controlArguments);
}else{
printf("**************************\n");
printf("Arguments Error! both send and receive.\n");
}
}else{
printf("************************************************\n");
printHelp();
}
return 0;
}
void printHelp(){
printf("Help Menu:\n Argument reference list:\n");
printf(" -s :send UDP packets.\n");
printf(" -r :receive UDP packets.\n");
printf(" -c --count :packets to send one cycle.default is 1.\n");
printf(" -y --cycles :send cycles.default is 10.\n");
printf(" -l --length :data length in udp packet.default is 100bytes.\n");
printf(" -i --interval :milliseconds between send options.default is 20ms.\n");
printf(" -a --ip :ip address to operate.\n");
printf(" -p --port :port used for UDP socket.\n");
printf(" -h --help :command help.\n");
}
/*
Check IP Format
if format error return false;
if format check passed return true;
argument is the pointer to the IP string.
*/
bool IPFormatCheck(char * const IP){
printf("IP check\n");
int ip_length = strlen(IP);
bool checkError = false;
int count_s = 0;
for(int i = 0,interval = 0 ; i < ip_length ; ++ i){
if(IP[i] == '.'){
if(interval == 0 || interval > 3){
checkError = true;
break;
}else{
++ count_s;
interval = 0;
}
}else if(IP[i] >= '0' && IP[i] <= '9'){
++ interval;
}else{
checkError = true;
break;
}
}
if(count_s != 3){
checkError = true;
}
return (!checkError);
}
/*
Check Arguments for send Process
if check passed return true;
if check failed return false;
argument is the pointer to struct ControlArguments.
*/
bool checkSendConfig(struct ControlArguments * const controlArguments){
printf("checking arguments.\n");
bool returnVaule;
char * IP;
IP = malloc(sizeof(strlen(controlArguments->IP)) + 1);
strncpy(IP,controlArguments->IP,15);
if(strlen(IP) != 0){
if(IPFormatCheck(IP)){
if((controlArguments->Port == 0) || (controlArguments->PacketLength == 0)){
printf("Port:%d Length:%d\n",controlArguments->Port,controlArguments->PacketLength);
printf("[Error] : Port or PacketLength Error.\n");
returnVaule = false;
}else{
returnVaule = true;
}
}else{
printf("[Error] : IP Error\n");
returnVaule = false;
}
}else{
printf("[Error] : IP length = 0!\n");
returnVaule = false;
}
free(IP);
return returnVaule;
}
void threadSend(struct ControlArguments * const controlArguments){
printf("****************************************\nSending Thread Start\n");
//定义sockfd
int sock_cli = socket(AF_INET,SOCK_DGRAM, 0);
//定义sockaddr_in
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(controlArguments->Port); //服务器端口
servaddr.sin_addr.s_addr = inet_addr(controlArguments->IP); //服务器ip
struct timeval time;
//连接服务器,成功返回0,错误返回-1
if (connect(sock_clli, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0){
perror("connect");
}else{
char *sendbuf = malloc(controlArguments->PacketLength);
for(int i = 0 ; i < controlArguments->PacketLength - 1 ; i++){
gettimeofday(&time,NULL);
int seed = time.tv_usec + i;
sendbuf[i] = rand_r(&seed) % 93 + 33;
}
sendbuf[controlArguments->PacketLength - 1] = '\0';
printf("buf : %s\n",sendbuf);
useconds_t sleep_us = (unsigned int)controlArguments->Interval * 1000;
int pack_seq = 0;
for(int i = 0 ; i < controlArguments->Cycles ; ++ i){
for(int j = 0 ; j < controlArguments->SendCount ; j++){
char *realSend = malloc((strlen(sendbuf) + 20));
if(realSend){
gettimeofday(&time,NULL);
sprintf(realSend,"[%4ld.%06ld][%4d]:%s",time.tv_sec,time.tv_usec,++pack_seq,sendbuf);
if(send(sock_cli, realSend, strlen(realSend),0) > 0){
printf("Send Successful\n");
}
free(realSend);
}
}
usleep(sleep_us);
}
free(sendbuf);
// printf("free.\n");
}
close(sock_cli);
printf("Sending Thread Finish\n****************************************\n");
}
void sendUDP(struct ControlArguments * const controlArguments){
if(checkSendConfig(controlArguments)){
// pthread_t tid;
printf("Before Send Thread\n");
// pthread_create(&tid, NULL, threadSend(controlArguments), NULL);
// pthread_tryjoin_np(tid, NULL);
threadSend(controlArguments);
printf("After Send Thread\n");
}else{
;
}
}
bool checkReceiveConfig(struct ControlArguments * const controlArguments){
if(controlArguments->Port == 0){
printf("[Error] : Please set Listening port.\n");
return false;
}else{
return true;
}
}
void threadReceive(struct ControlArguments * const controlArguments){
printf("****************************************\nReceiving Thread Start\n");
//定义sockfd
int server_sockfd = socket(AF_INET,SOCK_DGRAM, 0);
static int recive_serial = 0;
if(server_sockfd < 0){
perror("socket");
}else{
//定义sockaddr_in
struct sockaddr_in server_sockaddr;
struct sockaddr_in client_addr;
memset(&server_sockaddr,0,sizeof(struct sockaddr_in));
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(controlArguments->Port);
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//bind,成功返回0,出错返回-1
if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1){
perror("bind");
}else{
//客户端套接字
char buffer[1024];
socklen_t length = sizeof(client_addr);
int recv_len = 0;
struct timeval time;
while(1){
//成功返回非负描述字,出错返回-1
recv_len = recvfrom(server_sockfd,buffer,sizeof(buffer),0,(struct sockaddr *)&client_addr,&length);
gettimeofday(&time,NULL);
if(recv_len >= 0){
++ recive_serial;
printf("[%8ld.%06ld]Get[%d][l:%d] : %s\n",time.tv_sec,time.tv_usec,recive_serial,recv_len,buffer);
}
memset(&buffer,0,sizeof(buffer));
}
}
}
close(server_sockfd);
printf("Receiving Thread Finish\n****************************************\n");
}
void receiveUDP(struct ControlArguments * const controlArguments){
if(checkReceiveConfig(controlArguments)){
// pthread_t tid;
printf("Before Thread\n");
// pthread_create(&tid, NULL, threadReceive(controlArguments), NULL);
// pthread_tryjoin_np(tid, NULL);
threadReceive(controlArguments);
printf("After Thread\n");
}else{
;
}
}

函数解析

1.main(int argc, char *argv[])

int main(int argc, char *argv[])

主函数,用于接收参数和命令行解析。采用了getopt.h中提供的参数解析功能。可以像Linux自带的命令工具一样,可以通过短参或长参进行运行时的参数设定。

采用的参数设定方式可以调用短参数-h进行查看。默认的参数设定方式如下:

-s  :send UDP packets.
-r  :receive UDP packets.
-c --count  :packets to send one cycle.default is 1.
-y --cycles  :send cycles.default is 10.
-l --length  :data length in udp packet.default is 100bytes.
-i --interval  :milliseconds between send options.default is 20ms.
-a --ip  :ip address to operate.
-p --port  :port used for UDP socket.
-h --help  :command help.

2.IPFormatCheck(char * const IP)

bool IPFormatCheck(char * const IP)

IP格式检查函数,传入的参数是输入的IP字符串,返回的是检查结果。如果IP字符串符合IP的书写格式,返回true;如果IP字符串出现错误,返回false。

该检查函数仅面向IPv4,如果输入的地址是IPv6,将返回false。

3.checkSendConfig(struct ControlArguments * const controlArguments)

bool checkSendConfig(struct ControlArguments * const controlArguments)

发送参数检查,传入的参数是ControlArguments结构体指针,如果参数完备,返回true;如果参数出现冲突或者错误,返回false。

检查过程中,需要检查设定的IP地址,目标端口地址。如果任何一个参数出现错误,将返回false。

4.checkReceiveConfig(struct ControlArguments * const controlArguments)

bool checkReceiveConfig(struct ControlArguments * const controlArguments)

监听参数检查,因为监听的是本地端口,所以只要本地端口号不为空即可。

传入的参数是ControlArguments结构体指针,返回值为true或者false。

5.threadSend(struct ControlArguments * const controlArguments)

void threadSend(struct ControlArguments * const controlArguments)

UDP数据包发送函数,可以作为单独的线程运行。传入的参数为ControlArguments结构体指针,根据设定的发送轮次和间隔、计数进行一次发送过程。

在UDP的数据负载设定中,采用了随机填充的方法,通过随机数到ASCII码的映射,实现定长负载的内容生成。

6.threadReceive(struct ControlArguments * const controlArguments)

void threadReceive(struct ControlArguments * const controlArguments)

socket处于轮询等待的状态,不断从缓冲区中读取数据包。在接收到数据包后,根据当前的时间添加接收时间戳,用于传输的评估。

7.sendUDP(struct ControlArguments * const controlArguments)

void sendUDP(struct ControlArguments * const controlArguments)

该函数用于调用threadSend函数,在具备pthread支持的环境中,可以将发送线程作为子线程处理。因为此处不具备多线程优化的意义,所以暂时将相关功能屏蔽。

8.receiveUDP(struct ControlArguments * const controlArguments)

void receiveUDP(struct ControlArguments * const controlArguments)

该函数用于调用threadReceive函数,代码中设计使用pthread进行子线程处理,因为此处多线程效果有限,相关功能暂时屏蔽。