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

来源:互联网 发布:程序员适合写那些博客 编辑:程序博客网 时间:2024/05/16 04:00

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

服务端

首先定义了一个结构体:

#define NAME_AND_PASSWD_SIZE  31typedef 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 10void 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);//初始化mysqlif(!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 -lmysqlclientserver.o: server2.c    gcc -c -g server.cstr_handle.o: str_handle.c str_handle.h data_form.hgcc -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_OBJECTpublic:    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  31typedef struct login_message { char usr_name[NAME_AND_PASSWD_SIZE+1]; char usr_passwd[NAME_AND_PASSWD_SIZE+1]; }l_msg; #endif











0 0
原创粉丝点击