利用libevent做一个简单的服务器压力测试例子

小例子 服务器端用libevent 处理监听多个端口, 客户端多线程数据发送过来的时候写入对应不同文件中。

#include
#include
#include

#include 
#include 
#include 

#include
#include
#include

#define MAX_CONNECT_NUM 100   //暂定最大的连接数目
#define Port_Begin 10000               //侦听端口的其实端口号
#define DEF_TIME_OUT 20             //暂定超时调用时间设置20s
#define ONCE_READ_LEN 1000       //一次读取套接字的长度

//该结构体,本来想是构建传递给回调函数的
//但最终未用到,以后扩展可用
struct my_struct
{
	struct event_base* base;
	struct event *ev;
	int listener_fd[MAX_CONNECT_NUM];
	int connect_fd;
	int port_num;
	int index;
};
void accept_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void *arg);
int tcp_server_init(int port, int listen_num);

//定义个全局变量用来记录每一个端口发送过来的数据长度
//由于公用回调函数,无法合适处理计数,借助粗犷的全局变量
int len_from_on_port[MAX_CONNECT_NUM];

int main(int argc, char** argv)
{
	struct event evtimeout;
	int i,listener[MAX_CONNECT_NUM];
	
	struct event_base* base = event_base_new();
	struct event* ev_listen[MAX_CONNECT_NUM]; 
	struct my_struct tran_struct;
	tran_struct.base = base;
        //再次循环建立多个监听端口,然后设置回调accept函数
	for(i=0; i
g++ -o s server_100port.c -I ./include/ -Wl,-Bstatic -L ./lib -levent -Wl,-Bdynamic -lm -lrt
静态方式编译

客户端代码则是,利用多线程的方式,去连接服务器的端口,然后读同一个文件,发送,直到发送完毕:

#include
#include
#include

#include
#include
#include
#include

#include 
#include 
#include 

#define CONNECT_CLIENT_NUM 100
#define FILE_NAME  "update.bin"
#define ONCE_WRITE_LEN 1000

int tcp_connect_server(const char* server_ip, int port);
void * client_connect_thread_function(void * arg);

int main(int argc, char** argv)
{
	int i, client_socket_fd[CONNECT_CLIENT_NUM]={0};
	pthread_t client_thread[CONNECT_CLIENT_NUM] ;
	int begin_port = atoi( argv[2] );
	if( argc < 3 )
	{
		printf("please input like: ./a.out  192.168.1.1 10000 \n");
		return -1;
	}
	i=0;
	//如果将for注释掉就为一个线程的情况
	for ( i = 0; i < CONNECT_CLIENT_NUM; i++)
	{
		client_socket_fd[i] = tcp_connect_server(argv[1], begin_port + i);
		printf("port:%d,socket_fd:%d; ",begin_port + i,client_socket_fd[i]);
		//以下建立分离线程,传入了套接字描述符
		pthread_attr_t attr_client;
		pthread_attr_init(&attr_client);
		pthread_attr_setdetachstate(&attr_client, PTHREAD_CREATE_DETACHED);
		pthread_create(&client_thread[i], &attr_client, &client_connect_thread_function,(void *)(&client_socket_fd[i]));
		pthread_attr_destroy(&attr_client);
	}
	//这里暂时用如此粗犷的方法不退出主线程
	//暂时未找到合适的等待所有线程结束的函数
	while(1)  
		;
	printf("\n\nMain exit\n");
}
void * client_connect_thread_function(void * arg)
{
	FILE * stream_out = NULL;  //读取文件流
	int write_len_count = 0, once_write_len = 0 , i, my_port_num;
	int * my_thread_write_fd ;
	char buf[ONCE_WRITE_LEN] = {0};
	my_thread_write_fd = (int*)arg;    //获取传入的参数 为描述符
	//以下 获取到服务端的连接的端口,
	struct   sockaddr_in   me;  
        bzero(&me   ,   sizeof(me)); 
	socklen_t   socklen_len   =   sizeof(me);   
	if(getpeername(*my_thread_write_fd   ,   (struct   sockaddr   *)&me   ,   &socklen_len)   ==   0) 
	{
		my_port_num = ntohs(me. sin_port );
	}
	//获取当前线程号,和对应连接的服务器端口
	printf("I'm thread %lu, server port%d",  (unsigned long)pthread_self(), my_port_num);
	if((stream_out = fopen (FILE_NAME,"r+"))==NULL)  
	{  
	  	printf("file open error\n");	
	  	//exit(1);
	} 
	//一直读文件,然后写入套接字
	while( (i = fread(buf, sizeof(char), ONCE_WRITE_LEN, stream_out)) >0)
	{
		//由于采用阻塞式,没有发送完会组塞到这里
		if( (once_write_len=send(*my_thread_write_fd, buf, i, 0)) != i )
		{
			printf("read %d, while send %d\n",i, once_write_len);
			perror("first  error");
			printf("already send %d \n", write_len_count);
			sleep(5);	
		}
		//printf("send %d, ",i);
		write_len_count = write_len_count + once_write_len;
		once_write_len=0;
	}
	perror("read file");  //看最终的状态,如果一切正常那么会输出read file: Success
	fclose(stream_out);
	
	printf( " %lu thread over, port: %d,write length:%d\n", (unsigned long)pthread_self(), my_port_num, write_len_count);
	return NULL;
}

int tcp_connect_server(const char* server_ip, int port)
{
	int sockfd, status, save_errno;
	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr) );
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	status = inet_aton(server_ip, &server_addr.sin_addr);

	if( status == 0 ) //the server_ip is not valid value
	{
		errno = EINVAL;
		return -1;
	}

	sockfd = socket(PF_INET, SOCK_STREAM, 0);
	if( sockfd == -1 )
		return sockfd;
	status = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr) );
	if( status == -1 )
	{
		perror("connect error");
		save_errno = errno;
		close(sockfd);
		errno = save_errno; //the close may be error
		return -1;
	}

	return sockfd;
}
编译: gcc -o thread_c client_thread.c -lpthread


依次运行服务器程序    ./s   

客户端   ./thread_c 192.168.3.90 10000


测试可以

以上,这几天一个新任务是对公司某型号机器进行升级的项目,由于该型机器组网连接,并且已经具有通过网页接受升级文件的功能。

所以该项目就是模拟网页发送文件的方式,然后批量升级机器。

前期通过抓包工具 fiddler,查看浏览器实现升级文件发送的过程,网上辛苦找了个C++下http,post大文件的例子,和同事总算是“拼凑”成了数据,可以用软件对一个机器进行升级文件发送。

然后就是批量升级的模拟,由于公司内网没法模拟用户批量的机器,所以负责人让编写一个模拟程序,通过一个IP,多个端口的方式,来模拟多个机器文件接收。

当然程序还有待改进, 由于采用粗犷的编程方式,CPU占用飙升是肯定的。


所以有了上述服务器端的例子,为了测试服务器端的例子,自己有编写了上边客户端例子,记录下来,忘的太快了。



你可能感兴趣的:(网络通信,libevent学习和使用)