本文对很多人来说是没有一点技术可言的,仅仅是记录了自己的学习过程.
要求:
输入:iso_stress -c 5000 -I 500 -f iso.txt -H 4.2.2.2 -P 8888
-c 5000就是创建5000个线程来连服务器
-I 就是发送的时间间隔为500ms
-f 就是要发送的文件
-H 就是服务器IP地址
-P 指定端口号
然后根据-c指定的个数创建5000个线程,然后连到服务器上,当线程全部创建完成后就开始同时发送-f指定的文件过去,另外每一次发送文件统计一下,发送到接受数据包的时间
程序各个小模块
命令解析模块
struct option long_options[] =
{
{"thread", require_argument, NULL, 'c'},
{"time", require_argument, NULL, 'I'},
{"file",require_argument, NULL, 'f'},
{"ipaddr", require_argument, NULL,'H'},
{"port", require_argument, NULL, 'P'},
{NULL, 0, NULL, 0}
};
while ((opt = getopt_long(argc, argv, "c:I:f:H:P:", long_options, NULL)) != -1)
{
switch (opt)
{
case 'c':
thread_num = atoi(optarg);
printf("thread num is [%d].\n", thread_num);
break;
case 'I':
delay_time = atoi(optarg);
printf("time is [%d].\n", delay_time);
break;
case 'f':
send_file = optarg;
printf("the send file is [%s].\n", send_file);
break;
case 'H':
serv_ip = optarg;
printf("the ipaddr is [%s].\n", serv_ip);
break;
case 'P':
serv_port = atoi(optarg);
printf("the port is [%d].\n",serv_port);
break;
default:
break;
}
}
初始化打开文件数限制
int network_init(void)
{
struct rlimit rlt;
rlt.rlim_max = rlt.rlim_cur = 5000;
if (setrlimit(RLIMIT_NOFILE, &rlt) != 0)
{
printf("setrlimit failure: %s\n", strerror(errno));
return -1;
}
return 0;
}
信号控制
void signal_handler(int signo)
{
switch(signo)
{
case SIGTERM:
case SIGINT:
printf("program pid [%d] receive SIGINT/SIGTERM signal, exit program now\n", getpid());
g_stop = 1;
break;
case SIGSEGV:
printf("program pid [%d] SIGSEGV signal, exit program now\n", getpid());
g_stop = 1;
break;
case SIGPIPE:
printf("program pid [%d] SIGPIPE signal, exit program now\n", getpid());
g_stop = 1;
break;
default:
printf("program pid [%d] receive signal [%d]. \n", getpid(), signo);
break;
}
return;
}
void install_signal(void)
{
struct sigaction sigact, sigin;
printf("Install signal action\n");
/* initialize the catch signal */
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = signal_handler;
/* setup the ignore signal */
sigemptyset(&sigin.sa_mask);
sigin.sa_flags = 0;
sigin.sa_handler = SIG_IGN;
sigaction(SIGTERM, &sigact, 0); /* catch terminate signal "kill" command */
sigaction(SIGINT, &sigact, 0); /* catch interrupt signal CTRL+C */
sigaction(SIGCHLD, &sigact, 0); /* catch child process return */
sigaction(SIGPIPE, &sigact, 0); /* catch broken pipe */
return;
}
取读取文件长度
int get_file_size(const char *path_file)
{
int fd;
struct stat get_size_buf;
if ((fd = open(path_file, O_RDONLY)) < 0)
{
printf("open the file and get file size failure: %s\n", strerror(errno));
return -1;
}
if (fstat(fd, &get_size_buf) != 0)
{
printf("get the file size failure: %s\n", strerror(errno));
return -1;
}
close(fd);
return get_size_buf.st_size;
}
获取文件数据并转换成十六进制数(要求)
void get_file_data(const char *path_file, unsigned char *get_data_buf, int file_size)
{
assert(path_file != NULL);
assert(get_data_buf != NULL);
assert(&file_size != NULL);
size_t size;
int len;
int fd;
int i;
char buf[3];
unsigned char hex_buf[1024];
memset(buf, 0, sizeof(buf));
if ((fd = open(path_file, O_RDONLY)) < 0)
{
printf("open the file failure and read data failure: %s\n", strerror(errno));
return ;
}
if ((size = read(fd, get_data_buf, file_size)) <= 0)
{
printf("read data from file failure: %s\n", strerror(errno));
return ;
}
/* change sixteen string to ten */
for (i=0; i<file_size/2; i++)
{
buf[0] = get_data_buf[i*2];
buf[1] = get_data_buf[i*2+1];
get_data_buf[i] = strtol(buf, NULL, 16);
}
close(fd);
return ;
}
简单的输出函数
print_hex_string(char *buf, int len)
{
int i;
for (i=0; i<len; i++)
printf("data[%d] = 0x%02X\n", i, buf[i]);
}
处理线程函数
void *thread_func(void *thread_arg)
{
assert(thread_arg != NULL);
int sockfd;
int nsize;
int rv = 0;
int opt = 1;
struct sockaddr_in serv_addr;
char buf[1024];
int count;
THREAD_ARG *arg = (THREAD_ARG *)thread_arg;
count = ++arg->thread_count;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("use socket failure: %s\n", strerror(errno));
return NULL;
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(arg->port);
if (inet_pton(AF_INET, arg->ipaddr, &serv_addr.sin_addr) < 0)
{
printf("inet_pton failure: %s\n", strerror(errno));
return NULL;
}
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) < 0)
{
printf("connect to server failure: %s\n", strerror(errno));
return NULL;
}
printf("the thread [%d] is connect to server.\n",count);
/* set the flag, if flag is 1, send data */
while (g_thread_start_flag != 1)
;
while (!g_stop)
{
/* send data every 500ms */
nanosleep(&(arg->delay_time), NULL);
/* record time when send the data */
struct timeval now;
int rc;
double start_send, recv_end;
if ((rc = gettimeofday(&now, NULL)) != 0)
{
printf("get the time when send data failure\n");
continue;
}
start_send = now.tv_sec + (now.tv_usec / 1000000.0);
/* send the data */
nsize = arg->size;
printf("thread [%d] send %d bytes data to server.\n",count,nsize);
while(nsize > 0)
{
rv= write(sockfd, arg->data, nsize);
if (rv < 0)
{
printf("write() to server failure: %s\n", strerror(errno));
return NULL;
}
nsize -= rv;
}
if((nsize = read(sockfd, buf, sizeof(buf))) < 0)
{
printf("read failure: %s\n", strerror(errno));
return NULL;
}
else
{
printf("thread [%d] receive [%d] bytes data from server.\n", count, nsize);
print_hex_string(buf, nsize);
}
if ((rc = gettimeofday(&now, NULL)) != 0)
{
printf("get the time when received data failure\n");
}
recv_end = now.tv_sec + (now.tv_usec / 1000000.0);
printf("%.6lfs interval for send and receive.\n", recv_end - start_send);
} /* while(1) */
printf("thread [%d] exit now\n ", count);
--arg->thread_count;
close(sockfd);
pthread_exit(NULL);
return NULL;
}
主函数中一些初始化
/* initialize time */
slptime.tv_sec = 0;
slptime.tv_nsec = (delay_time * 1000000); /* 500ms */ /* error */
/* get file size and get file data */
file_size = get_file_size(send_file);
get_data_buf = (char *)malloc(sizeof(char) * (file_size + 1));
get_file_data(send_file, get_data_buf, file_size);
/* initialize thread_reg */
thread_arg.num = thread_num;
thread_arg.delay_time = slptime;
thread_arg.data = get_data_buf;
thread_arg.size = file_size/2;
thread_arg.ipaddr = serv_ip;
thread_arg.port = serv_port;
/* intialize network */
network_init();
for (i=0; i<thread_num; i++)
{
err = pthread_create(&tid[i], NULL, thread_func, (void *)&thread_arg);
if (err != 0)
{
printf("the thread [%d] is create failure: %s\n", i+1, strerror(err));
}
printf("create [%d] thread successfully.\n", i+1);
}
g_thread_start_flag = 1;
for(i=0; i<thread_num;i++)
{
err = pthread_join(tid[i], NULL);
printf("can't get thread [%d] exit.\n", i);
}
return 0;
一个不大的程序,却能从其中学到很多东西.比如:
(1) 多线程中尽量不要使用sleep() 因为它是非线程安全函数
(2) 关于TCP socket中读取字节需要注意的一些因素 http://www.360doc.com/content/11/0731/10/2127922_136887195.shtml
当然,我显然没按照上面的文章里面说的去做...因为要求的关系没有用到.以后再借鉴
(3)进一步了解了多线程的编程,但是还需要更多的努力.
(4)懂得用GDB进行一些初级的调试.