只实现的最基本的功能框架,仅供参考。
服务端
首先定义了一个结构体:
#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