Extended the TCP tester app a bit:
* you can now instruct it to drop specific packets * you can now also define a round trip time for the packets * added a "send" command to send arbitrarily sized buffers over TCP. Not surprising, the current implementation of TCP doesn't do anything with those (other than crashing as soon as you try to send data). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19362 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
/*
|
||||
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Axel Dörfler, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "argv.h"
|
||||
#include "tcp.h"
|
||||
#include "utility.h"
|
||||
@@ -13,8 +22,10 @@
|
||||
#include <module.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <netinet/in.h>
|
||||
#include <new>
|
||||
#include <set>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -46,7 +57,12 @@ extern struct net_socket_module_info gNetSocketModule;
|
||||
struct net_protocol_module_info *gTCPModule;
|
||||
struct net_socket *gServerSocket, *gClientSocket;
|
||||
|
||||
bool gTCPDump = true;
|
||||
static set<uint32> sDropList;
|
||||
static bigtime_t sRoundTripTime = 0;
|
||||
static bool sIncreasingRoundTrip = false;
|
||||
static bool sRandomRoundTrip = false;
|
||||
static bool sTCPDump = true;
|
||||
static bigtime_t sStartTime;
|
||||
|
||||
static struct net_domain sDomain = {
|
||||
"ipv4",
|
||||
@@ -290,6 +306,63 @@ socket_listen(net_socket *socket, int backlog)
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
socket_send(net_socket *socket, const void *data, size_t length, int flags)
|
||||
{
|
||||
if (socket->peer.ss_len == 0)
|
||||
return EDESTADDRREQ;
|
||||
|
||||
if (socket->address.ss_len == 0) {
|
||||
// try to bind first
|
||||
status_t status = socket_bind(socket, NULL, 0);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
// TODO: useful, maybe even computed header space!
|
||||
net_buffer *buffer = gNetBufferModule.create(256);
|
||||
if (buffer == NULL)
|
||||
return ENOBUFS;
|
||||
|
||||
// copy data into buffer
|
||||
if (gNetBufferModule.append(buffer, data, length) < B_OK) {
|
||||
gNetBufferModule.free(buffer);
|
||||
return ENOBUFS;
|
||||
}
|
||||
|
||||
buffer->flags = flags;
|
||||
//buffer->source = (sockaddr *)&socket->address;
|
||||
//buffer->destination = (sockaddr *)&socket->peer;
|
||||
memcpy(&buffer->source, &socket->address, socket->address.ss_len);
|
||||
memcpy(&buffer->destination, &socket->peer, socket->peer.ss_len);
|
||||
|
||||
status_t status = socket->first_info->send_data(socket->first_protocol, buffer);
|
||||
if (status < B_OK) {
|
||||
gNetBufferModule.free(buffer);
|
||||
return status;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
socket_recv(net_socket *socket, void *data, size_t length, int flags)
|
||||
{
|
||||
net_buffer *buffer;
|
||||
status_t status = socket->first_info->read_data(
|
||||
socket->first_protocol, length, flags, &buffer);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
ssize_t bytesReceived = buffer->size;
|
||||
gNetBufferModule.read(buffer, 0, data, bytesReceived);
|
||||
gNetBufferModule.free(buffer);
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
socket_spawn_pending(net_socket *parent, net_socket **_socket)
|
||||
{
|
||||
@@ -644,15 +717,36 @@ status_t
|
||||
domain_receive_data(net_buffer *buffer)
|
||||
{
|
||||
static uint32 packetNumber = 1;
|
||||
static bigtime_t lastTime = 0;
|
||||
|
||||
if (gTCPDump) {
|
||||
bool drop = false;
|
||||
if (sDropList.find(packetNumber) != sDropList.end())
|
||||
drop = true;
|
||||
|
||||
if (!drop && (sRoundTripTime > 0 || sRandomRoundTrip || sIncreasingRoundTrip)) {
|
||||
bigtime_t add = 0;
|
||||
if (sRandomRoundTrip)
|
||||
add = (bigtime_t)(1.0 * rand() / RAND_MAX * 500000);
|
||||
if (sIncreasingRoundTrip)
|
||||
sRoundTripTime += (bigtime_t)(1.0 * rand() / RAND_MAX * 150000);
|
||||
|
||||
snooze(sRoundTripTime / 2 + add);
|
||||
}
|
||||
|
||||
if (sTCPDump) {
|
||||
NetBufferHeader<tcp_header> bufferHeader(buffer);
|
||||
if (bufferHeader.Status() < B_OK)
|
||||
return bufferHeader.Status();
|
||||
|
||||
tcp_header &header = bufferHeader.Data();
|
||||
|
||||
printf("% 3ld ", packetNumber++);
|
||||
bigtime_t now = system_time();
|
||||
if (lastTime == 0)
|
||||
lastTime = now;
|
||||
|
||||
printf("% 3ld %8.6f (%8.6f) ", packetNumber, (now - sStartTime) / 1000000.0,
|
||||
(now - lastTime) / 1000000.0);
|
||||
lastTime = now;
|
||||
|
||||
if (is_server((sockaddr *)&buffer->source))
|
||||
printf("\33[31mserver > client: ");
|
||||
@@ -727,9 +821,19 @@ domain_receive_data(net_buffer *buffer)
|
||||
}
|
||||
}
|
||||
|
||||
if (drop)
|
||||
printf(" <DROPPED>");
|
||||
printf("\33[0m\n");
|
||||
|
||||
bufferHeader.Detach();
|
||||
} else if (drop)
|
||||
printf("<**** DROPPED %ld ****>\n", packetNumber);
|
||||
|
||||
packetNumber++;
|
||||
|
||||
if (drop) {
|
||||
gNetBufferModule.free(buffer);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return gTCPModule->receive_data(buffer);
|
||||
@@ -817,6 +921,8 @@ do_connect(int argc, char** argv)
|
||||
if (argc > 1)
|
||||
port = atoi(argv[1]);
|
||||
|
||||
sStartTime = system_time();
|
||||
|
||||
sockaddr_in address;
|
||||
memset(&address, 0, sizeof(address));
|
||||
address.sin_family = AF_INET;
|
||||
@@ -830,6 +936,120 @@ do_connect(int argc, char** argv)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
do_send(int argc, char** argv)
|
||||
{
|
||||
size_t size = 1024;
|
||||
if (argc > 1 && isdigit(argv[1][0])) {
|
||||
char *unit;
|
||||
size = strtoul(argv[1], &unit, 0);
|
||||
if (unit != NULL && unit[0]) {
|
||||
if (unit[0] == 'k' || unit[0] == 'K')
|
||||
size *= 1024;
|
||||
else if (unit[0] == 'm' || unit[0] == 'M')
|
||||
size *= 1024 * 1024;
|
||||
else {
|
||||
fprintf(stderr, "unknown unit specified!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (argc > 1) {
|
||||
fprintf(stderr, "invalid args!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (size > 4 * 1024 * 1024) {
|
||||
printf("amount to send will be limited to 4 MB\n");
|
||||
size = 4 * 1024 * 1024;
|
||||
}
|
||||
|
||||
char *buffer = (char *)malloc(size);
|
||||
if (buffer == NULL) {
|
||||
fprintf(stderr, "not enough memory!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// initialize buffer with some not so random data
|
||||
for (uint32 i = 0; i < size; i++) {
|
||||
buffer[i] = (char)(i & 0xff);
|
||||
}
|
||||
|
||||
ssize_t bytesWritten = socket_send(gClientSocket, buffer, size, 0);
|
||||
if (bytesWritten < B_OK) {
|
||||
fprintf(stderr, "failed sending buffer: %s\n", strerror(bytesWritten));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
do_drop(int argc, char** argv)
|
||||
{
|
||||
if (argc == 1) {
|
||||
// show list of dropped packets
|
||||
printf("Drop pakets:\n");
|
||||
|
||||
set<uint32>::iterator iterator = sDropList.begin();
|
||||
uint32 count = 0;
|
||||
for (; iterator != sDropList.end(); iterator++) {
|
||||
printf("%4lu\n", *iterator);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
printf("<empty>\n");
|
||||
} else if (!strcmp(argv[1], "-f")) {
|
||||
// flush drop list
|
||||
sDropList.clear();
|
||||
puts("drop list cleared.");
|
||||
} else if (isdigit(argv[1][0])) {
|
||||
// add to drop list
|
||||
for (int i = 1; i < argc; i++) {
|
||||
uint32 packet = strtoul(argv[i], NULL, 0);
|
||||
if (packet == 0) {
|
||||
fprintf(stderr, "invalid packet number: %s\n", argv[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
sDropList.insert(packet);
|
||||
}
|
||||
} else {
|
||||
// print usage
|
||||
puts("usage: drop <packet-number> [...]\n"
|
||||
" or: drop [-f]\n\n"
|
||||
"Specifiying -f flushes the drop list; if you called drop without\n"
|
||||
"any arguments, the current drop list is dumped.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
do_round_trip_time(int argc, char** argv)
|
||||
{
|
||||
if (argc == 1) {
|
||||
// show current time
|
||||
printf("Current round trip time: %g ms\n", sRoundTripTime / 1000.0);
|
||||
} else if (!strcmp(argv[1], "-r")) {
|
||||
// toggle random time
|
||||
sRandomRoundTrip = !sRandomRoundTrip;
|
||||
printf("Round trip time is now %s.\n", sRandomRoundTrip ? "random" : "fixed");
|
||||
} else if (!strcmp(argv[1], "-i")) {
|
||||
// toggle increasing time
|
||||
sIncreasingRoundTrip = !sIncreasingRoundTrip;
|
||||
printf("Round trip time is now %s.\n", sIncreasingRoundTrip ? "increasing" : "fixed");
|
||||
} else if (isdigit(argv[1][0])) {
|
||||
// set time
|
||||
sRoundTripTime = 1000LL * strtoul(argv[1], NULL, 0);
|
||||
} else {
|
||||
// print usage
|
||||
puts("usage: rtt <time in ms>\n"
|
||||
" or: rtt [-r|-i]\n\n"
|
||||
"Specifiying -r sets random time, -i causes the times to increase over time;\n"
|
||||
"witout any arguments, the current time is printed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
do_dprintf(int argc, char** argv)
|
||||
{
|
||||
@@ -844,8 +1064,11 @@ do_dprintf(int argc, char** argv)
|
||||
|
||||
static cmd_entry sBuiltinCommands[] = {
|
||||
{"connect", do_connect, "Connects the client"},
|
||||
{"send", do_send, "Sends data from the client to the server"},
|
||||
{"dprintf", do_dprintf, "Toggles debug output"},
|
||||
{"drop", do_drop, "Lets you drop packets during transfer"},
|
||||
{"help", do_help, "prints this help text"},
|
||||
{"rtt", do_round_trip_time, "Specifies the round trip time"},
|
||||
{"quit", NULL, "exits the application"},
|
||||
{NULL, NULL, NULL},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user