Linux下基于EPOLL 模型,实现用户登录,客户端采用QT

只实现的最基本的功能框架,仅供参考。

服务端

首先定义了一个结构体:

#define NAME_AND_PASSWD_SIZE  31

typedef struct login_message 
{ 
	char usr_name[NAME_AND_PASSWD_SIZE+1]; 
	char usr_passwd[NAME_AND_PASSWD_SIZE+1]; 
}l_msg; 


一个消息标识:

# define LOGIN_MSG 0x0001

先实现客户端的主体框架

对于一个简单的服务器模型,直接照搬下面的代码即可,对于其中的每一个连接,传给相应的连接处理函数。

<pre name="code" class="cpp">#include <stdio.h> 
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define PORT 6666
#define LISTEN_QUEUE 5
#define MAXBUF 1024
#define MAXEPOLL_SIZE 10000

#define MAX_EVENT 10

void set_socket_non_blocking( int sockfd);// 设置为非阻塞

int main()
{
	int listenfd, connfd, nfds, epollfd, n;
	struct sockaddr_in server_addr, client_addr;
	struct epoll_event ev, events[MAX_EVENT];
	socklen_t server_len = sizeof(server_addr);
	socklen_t client_len = sizeof(server_addr);

	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) <0)
	{
		perror("init:");
		exit(1);
	}

	server_addr.sin_addr.s_addr = INADDR_ANY;
	server_addr.sin_port = htons(PORT);
	server_addr.sin_family = AF_INET;

	if (bind(listenfd, (struct sockaddr *)&server_addr, server_len) <0)
	{
		perror("bind:");
		exit(1);
	}

	if (listen(listenfd, LISTEN_QUEUE) < 0)
	{
		perror("listen:");
		exit(1);
	}

	printf("listenning ...\n");

	if((epollfd = epoll_create(MAXEPOLL_SIZE)) < 0)
	{
		perror("epoll create:");
		exit(1);
	}

	ev.events = EPOLLIN;
	ev.data.fd = listenfd;  

	if( epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev) <0)
	{
		perror("epoll_ctl:");
		exit(1);
	}

	while(1)
	{
		if((nfds = epoll_wait(epollfd, events, MAX_EVENT, -1)) <0)
		{
			perror("epoll_wait:");
			exit(1);
		}

		for (n = 0; n < nfds; ++n)
		{
			if(events[n].data.fd == listenfd)
			{
				if((connfd = accept(listenfd, (struct sockaddr*)&client_addr, &client_len)) < 0)
				{
					perror("accept:");
					exit(1);
				}
				set_socket_non_blocking(connfd);
				ev.events = EPOLLIN | EPOLLET;
				ev.data.fd = connfd;
				if( epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev) ==-1)
				{
					perror("epoll_ctl:connfd");
					exit(1);
				}
				
			}
                        else str_handle(events[n].data.fd);
		}

	}
	return 0;
}

void set_socket_non_blocking( int sockfd )
{
	int ret;
	ret = fcntl( sockfd, F_GETFL );
	if ( ret < 0 )
	{
		perror("fcntl get");
		exit(1);
	}

	ret = ret|O_NONBLOCK;

	if( fcntl( sockfd, F_SETFL, ret)<0)
	{
		perror("fcntl set");
		exit(1);
	}
}


 

对于每一个连接,即调用 str_handle()函数进行处理。

str_handle() 的函数原型如下:

void str_handle(int connfd )
{

	l_msg msg;	// 定义一个登录消息的的结构体变量
	char buffer[BUFF_SIZE] ;
	uint16_t msg_id;
	memset( buffer, 0, BUFF_SIZE);
	uint16_t login_tag = 0; //是否登录成功,默认0表示登录失败

	read( connfd, buffer , BUFF_SIZE);
	memcpy( &msg_id, buffer, sizeof(uint16_t));

	switch (msg_id)
	{
		case LOGIN_MSG:
			memcpy(&msg, buffer + 2, sizeof(l_msg));
			printf("usr_name: %s\n", msg.usr_name);
			printf("passwd: %s\n", msg.usr_passwd);
			login_tag = login_check( msg );
			write( connfd, &login_tag, sizeof(uint16_t));//
			break;
		default:
			break;
	}
}

1、首先先从缓冲区中拷贝1024个字节进buffer数组

2、因为客户端的请求头为 uint16_t msg_id + l_msg(即一开始定义的结构体数组)

3、拷贝两个字节对msg_id进行赋值,用于判断是一个什么样的消息,然后采用switch函数对消息类型进行控制。

4、如果是登录消息,则给结构体变量msg赋值,并传递给函数login_check()进行数据库查询,返回值用于确定是否登录成功,返回0表示查询失败,返回1表示查询成功

5、将是否登录成功的消息发送给客户端


下面是login_check 的函数原型

int login_check( l_msg msg)
{
	int flag;
	MYSQL mysql; //声明mysql变量
	mysql_init(&mysql);//初始化mysql

	if(!mysql_real_connect(&mysql,"localhost","root","123456","practise",3306,NULL,0))
	{
		printf("Error connecting to Mysql!\n");
	}
	else
	{
		printf("Connected Mysql successful!\n");
	}


	char sql[256] = {0};
	char *sql1 = "select '";
	char *sql2 = "' from account where password =";
	char *sql3 = ";";
	strcat( sql, sql1);
	strcat( sql, msg.usr_name );
	strcat( sql, sql2);
	strcat( sql, msg.usr_passwd );
	strcat( sql, sql3);


	flag = mysql_real_query(&mysql, sql, (unsigned int)strlen(sql)); //该函数对数据库进行查询,返回是否成功的进行了查询

	if(flag)//若未成功查询,返回0
	{
		printf("Query failed!\n");
		return 0;
	}
	else
	{
		printf("[%s] made ...\n",sql);
	}

	MYSQL_RES *result =mysql_store_result(&mysql); //*result 指向结果集

	if ( mysql_num_rows(result) == 0 ) //取出结果集,若结果集为空证明无此用户,返回0
	{
		mysql_free_result(result);
		return 0;
	}
	mysql_free_result(result); //释放结果集

	return 1;//若有此用户返回1

}

数据库的连接方式采用包含头文件,以及链接的时候链接mysql客户端的接口库

我的头文件

#include <mysql/mysql.h>

gcc 编译时采用如下命令:

gcc server.o  str_handle.o -o  server -L /usr/lib/mysql -lmysqlclient

贴下我自己写的makefile文件吧,程序代码没有进行规划,仅供参考。

server:  server.o  str_handle.o  
	gcc server.o  str_handle.o -o  server -L /usr/lib/mysql -lmysqlclient
server.o: server2.c    
	gcc -c -g server.c
str_handle.o: str_handle.c str_handle.h data_form.h
	gcc -c -g str_handle.c

qt端

main函数:

#include "send_file.h"
#include "logindlg.h"
#include <QApplication>
#include <QTextCodec>

char usr_name[32];
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Send_file w;
    LoginDlg l;
    if ( l.exec()==QDialog::Accepted)
    {
        w.show();
        return a.exec();
    }
}

LoginDlg 类头文件

#ifndef LOGINDLG_H
#define LOGINDLG_H

#include <QDialog>
#include <QtNetwork>
#include "data_form.h"

namespace Ui {
class LoginDlg;
}

class LoginDlg : public QDialog
{
    Q_OBJECT

public:
    explicit LoginDlg(QWidget *parent = 0);
    ~LoginDlg();


private slots:
    void on_login_btn_clicked();
    void new_conn();
    void read_msg();
    void display_error(QAbstractSocket::SocketError);
    void write_msg();

private:
    Ui::LoginDlg *ui;
    QTcpSocket *tcpsocket;
    char buffer[BUFF_SIZE];
    //quint16 block_size;
    int flag;
    uint16_t login_tag;

};

#endif // LOGINDLG_H

LoginDlg类 cpp文件

#include "logindlg.h"
#include "ui_logindlg.h"
#include "data_form.h"
#include <QMessageBox>

extern char usr_name[32];
LoginDlg::LoginDlg(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::LoginDlg)
{
    ui->setupUi(this);
    tcpsocket = new QTcpSocket(this);
    ui->passwd_edit->setEchoMode(QLineEdit::Password);
    connect(tcpsocket, SIGNAL(readyRead()), this, SLOT(read_msg()));
    connect(tcpsocket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(display_error(QAbstractSocket::SocketError)));
}

LoginDlg::~LoginDlg()
{
    delete ui;
}



void LoginDlg::on_login_btn_clicked() //按下登录按钮时调用该槽函数
{
    new_conn();
    write_msg();
}
void LoginDlg::new_conn() //新建连接
{
    //block_size = 0;
    tcpsocket->abort();//cancel the connected
    tcpsocket->connectToHost("127.0.0.1",6666);
}

void LoginDlg::read_msg()
{
    memset(buffer, 0, BUFF_SIZE);
    tcpsocket->read( buffer, BUFF_SIZE );
    memcpy(&login_tag, buffer, sizeof(uint16_t));
    if(login_tag == 1)
    {
       accept();
    }
    else
    {
        QMessageBox::warning(this,tr("Warning"),tr("user name or password error!"),QMessageBox::Yes);
    }
}

void LoginDlg::display_error(QAbstractSocket::SocketError)
{
    qDebug() << tcpsocket->errorString();//output the error message
}

void LoginDlg::write_msg() //发送用户名、密码
{
    l_msg msg;
    uint16_t msg_id = LOGIN_ID;
    int name_len = ui->usrname_edit->text().length();
    int pass_len = ui->passwd_edit->text().length();
    memcpy( msg.usr_name, ui->usrname_edit->text().toStdString().c_str(), name_len);
    msg.usr_name[name_len] = '\0';
    memcpy( msg.usr_passwd, ui->passwd_edit->text().toStdString().c_str(), pass_len);
    msg.usr_passwd[pass_len] = '\0';
    strcpy(usr_name, msg.usr_name);
    memset( buffer, 0, BUFF_SIZE);
    memcpy( buffer, &msg_id, 2);
    memcpy( buffer+2, &msg ,sizeof( msg));
    tcpsocket->write( buffer, 2+sizeof(msg));

}

data_form.h

#ifndef DATA_FORM_H
#define DATA_FORM_H


#include <stdint.h>

//消息信号
const int LOGIN_ID = 0x0001;

//常量大小 
const int BUFF_SIZE = 1024; 
const int  MAX_CLIENT 20;
const int NAME_AND_PASSWD_SIZE  31

typedef struct login_message 
{ 
	char usr_name[NAME_AND_PASSWD_SIZE+1]; 
	char usr_passwd[NAME_AND_PASSWD_SIZE+1]; 
}l_msg; 

#endif











最近在开发im服务器 需要大并发链接 QT默认的是使用select模型的 这种轮询方式非常慢 在高并发连接 我们需要epoll才能发挥linux服务器的性能 而且使用简单 整个服务端代码架构无需修改 直接可以使用 只要在 main文件添加: int main int argc char argv[] { #ifdef Q OS LINUX QCoreApplication::setEventDispatcher new EventDispatcherLibEvent ; qInstallMessageHandler customMessageHandler ; #endif QCoreApplication a argc argv ; auto ser new ConfigServer; ser >startServer ; return a exec ; } 在 pro文件添加 linux{ LIBS + levent core SOURCES + common eventdispatcher libevent eventdispatcher libevent cpp common eventdispatcher libevent eventdispatcher libevent config cpp common eventdispatcher libevent eventdispatcher libevent p cpp common eventdispatcher libevent socknot p cpp common eventdispatcher libevent tco eventfd cpp common eventdispatcher libevent tco pipe cpp common eventdispatcher libevent tco cpp common eventdispatcher libevent timers p cpp HEADERS + common eventdispatcher libevent common h common eventdispatcher libevent eventdispatcher libevent h common eventdispatcher libevent eventdispatcher libevent config h common eventdispatcher libevent eventdispatcher libevent config p h common eventdispatcher libevent eventdispatcher libevent p h common eventdispatcher libevent libevent2 emul h common eventdispatcher libevent qt4compat h common eventdispatcher libevent tco h common eventdispatcher libevent wsainit h } 可以直接跨平台了使用了 csdn博客:http: blog csdn net rushroom">最近在开发im服务器 需要大并发链接 QT默认的是使用select模型的 这种轮询方式非常慢 在高并发连接 我们需要epoll才能发挥linux服务器的性能 而且使用简单 整个服务端代码架构无需修改 直接可以使用 只要在 main文件添加: [更多]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值