Winsocket编程,事件选择模型+mysql简易聊天室,含登录同步离线消息

来源:互联网 发布:golang 2.0教程 编辑:程序博客网 时间:2024/06/07 19:57

一、有关数据库操作,封装在DBHelper.h内,代码如下

#include <winsock.h>  #include "mysql.h"#include<stdio.h>bool getPassword(char * username, char * &pwd){MYSQL mysql, *sock;mysql_init(&mysql);if (!(sock = mysql_real_connect(&mysql, "localhost", "root", "admin", "windowsPrograming", 0, NULL, 0))){printf("Cannot connect to database:%s\n", mysql_error(sock));return false;}char sqlbuf[200];sprintf(sqlbuf, "select pass_word from userInfo where user_name=%s", username);//printf("%s\n", sqlbuf);if (mysql_query(sock, sqlbuf)){printf("Query Failed:%s", mysql_error(sock));return 0;}MYSQL_RES *res;MYSQL_ROW row;if (!(res = mysql_store_result(sock))){printf("Failed to store result:%s", mysql_error(sock));return 0;}while (row = mysql_fetch_row(res)){pwd = (char *)malloc(sizeof(char *));strcpy(pwd, (char*)row[0]);//puts(row[0]);}mysql_free_result(res);mysql_close(sock);return true;}bool isExist(char * username){MYSQL mysql, *sock;mysql_init(&mysql);if (!(sock = mysql_real_connect(&mysql, "localhost", "root", "admin", "windowsPrograming", 0, NULL, 0))){printf("Cannot connect to database:%s\n", mysql_error(sock));return false;}char sqlbuf[200];sprintf(sqlbuf, "select user_name from userInfo where user_name=%s", username);if (mysql_query(sock, sqlbuf)){printf("Query Failed:%s", mysql_error(sock));return 0;}MYSQL_RES *res;MYSQL_ROW row;if (!(res = mysql_store_result(sock))){printf("Failed to store result:%s", mysql_error(sock));return 0;}if (row = mysql_fetch_row(res)){mysql_free_result(res);mysql_close(sock);return true;}else{mysql_free_result(res);mysql_close(sock);return false;}}bool regist(char * username, char * password){MYSQL mysql, *sock;mysql_init(&mysql);if (!(sock = mysql_real_connect(&mysql, "localhost", "root", "admin", "windowsPrograming", 0, NULL, 0))){printf("Cannot connect to database:%s\n", mysql_error(sock));return false;}char sqlbuf[200];sprintf(sqlbuf, "insert into  userInfo(user_name,pass_word) values('%s','%s')", username, password);if (mysql_query(sock, sqlbuf)){printf("Query Failed:%s", mysql_error(sock));return 0;}return true;}bool getDelayMessage(char * username, char(*message)[100], int &len){MYSQL mysql, *sock;mysql_init(&mysql);if (!(sock = mysql_real_connect(&mysql, "localhost", "root", "admin", "windowsPrograming", 0, NULL, 0))){printf("Cannot connect to database:%s\n", mysql_error(sock));return false;}char sqlbuf[200];sprintf(sqlbuf, "select message from delayMessage where status=0 and user=%s", username);if (mysql_query(sock, sqlbuf)){printf("Query Failed:%s", mysql_error(sock));return 0;}MYSQL_RES *res;MYSQL_ROW row;if (!(res = mysql_store_result(sock))){printf("Failed to store result:%s", mysql_error(sock));return 0;}int total = 0;while (row = mysql_fetch_row(res)){//*message+total = (char *)malloc(sizeof(char *));strcpy(*(message + total), (char*)row[0]);total++;}len = total;sprintf(sqlbuf, "update delayMessage set status='1' where user=%s", username);if (mysql_query(sock, sqlbuf)){printf("Query Failed:%s", mysql_error(sock));return 0;}mysql_free_result(res);mysql_close(sock);return true;}bool reserveMessage(char * username, char * message){MYSQL mysql, *sock;mysql_init(&mysql);if (!(sock = mysql_real_connect(&mysql, "localhost", "root", "admin", "windowsPrograming", 0, NULL, 0))){printf("Cannot connect to database:%s\n", mysql_error(sock));return false;}char sqlbuf[200];sprintf(sqlbuf, "insert into  delayMessage(user,message) values('%s','%s')", username, message);if (mysql_query(sock, sqlbuf)){printf("Query Failed:%s", mysql_error(sock));return 0;}return true;}

2、服务器端代码

// WSAAsyncSelect.cpp : 定义控制台应用程序的入口点。//#include <stdio.h>#include <winsock2.h>#include"DBHelper.h"#pragma comment(lib, "WS2_32")// 链接到WS2_32.libstatic CRITICAL_SECTION cs;//临界资源typedef struct UserBindSock{SOCKET socket;char user[100];int isLogin;};typedef struct LoginHelper{UserBindSock * user;char userName[100];WSAEVENT event;};UserBindSock LoginInfo[100];int totalConnection=0;WSAEVENT eventArray[100];SOCKET sockArray[100];int totalEvent = 0;//封装函数声明DWORD WINAPI toLogin(LPVOID pM);UserBindSock * getUserBySocket(SOCKET socket);void deleteIllegalUser(SOCKET socket);void saveEvents(WSAEVENT event, SOCKET socket);int main(){//初始化sock环境BYTE minorVer = 2;BYTE majorVer = 2;WSADATA wsaData;WORD sockVersion = MAKEWORD(minorVer, majorVer);if (::WSAStartup(sockVersion, &wsaData) != 0){exit(0);}USHORT nPort = 4567;// 此服务器监听的端口号SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(nPort);sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//INADDR_ANY;if (::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR){printf(" Failed bind() \n");return -1;}listen(sListen, 5);WSAEVENT myevent = ::WSACreateEvent();::WSAEventSelect(sListen, myevent, FD_ACCEPT | FD_CLOSE);eventArray[totalEvent] = myevent;sockArray[totalEvent] = sListen;totalEvent++;while (true){int nIndex = ::WSAWaitForMultipleEvents(totalEvent, eventArray, FALSE, 1000, FALSE);if (nIndex == WSA_WAIT_TIMEOUT)continue;//减去WSA_WAIT_EVENT_0 纯属规范性问题,WSA_WAIT_EVENT_0 表示数组的起始位置,值为0nIndex = nIndex - WSA_WAIT_EVENT_0;for (int i = nIndex; i < totalEvent; i++){int res;res = ::WSAWaitForMultipleEvents(1, &eventArray[i], TRUE, 1000, FALSE);if (res == WSA_WAIT_FAILED || res == WSA_WAIT_TIMEOUT)continue;else{WSANETWORKEVENTS eventReady;::WSAEnumNetworkEvents(sockArray[i], eventArray[i], &eventReady);if (eventReady.lNetworkEvents&FD_ACCEPT){if (eventReady.iErrorCode[FD_ACCEPT_BIT] == 0){if (totalEvent >= 100){printf("too many connections \n");continue;}//接收客户端连接请求SOCKET newConnection = ::accept(sockArray[i], NULL, NULL);//用户验证UserBindSock newUser;newUser.isLogin = 0;newUser.socket = newConnection;LoginInfo[totalConnection] = newUser;totalConnection++;printf("received a connection!\n");//事件选择WSAEVENT newEvent = ::WSACreateEvent();::WSAEventSelect(newConnection, newEvent, FD_READ | FD_CLOSE | FD_WRITE);saveEvents(newEvent, newConnection);}}else if (eventReady.lNetworkEvents&FD_READ){         //读取客户端信息,判断如果1.用户已登录,转发信息 2.用户未登录要求其登录char userName[100];char buff[100];int totalRecv=::recv(sockArray[i], buff, sizeof(buff), 0);if (totalRecv > 0){buff[totalRecv] = '\0';strcpy(userName, buff);}else{continue;}//验证链接UserBindSock * user = getUserBySocket(sockArray[i]);if (user == NULL){printf("closed an illegal connection!\n");strcpy(buff, "something is wrong,please try later!");::send(sockArray[i], buff, strlen(buff), 0);//关闭非法链接::closesocket(sockArray[i]);//删除sockeArry中的sockfor (int j = i; j < totalEvent - 1; j++){sockArray[j] = sockArray[i];eventArray[j] = eventArray[j];}totalEvent--;continue;}//检测到未登录链接要求其登录if (user->isLogin == 0){LoginHelper loginHelper;loginHelper.event = eventArray[i];  strcpy(loginHelper.userName, userName);loginHelper.user = user;for (int j = i; j < totalEvent - 1; j++){sockArray[j] = sockArray[i];eventArray[j] = eventArray[j];}totalEvent--;HANDLE handle = CreateThread(NULL, 0, toLogin, (LPVOID)&loginHelper, 0, NULL);CloseHandle(handle);continue;}//已经登录的用户,转发其消息至聊天室int member = totalConnection;while (member > 0){if (LoginInfo[member - 1].isLogin == 1){char  message[100];sprintf(message, "%s:%s", user->user, buff);int send=::send(LoginInfo[member - 1].socket, message, strlen(message), 0);//发送失败,转为离线消息printf("%d\n", send);if (send < 0){reserveMessage(LoginInfo[member - 1].user, message);}}member--;}}else if (eventReady.lNetworkEvents&FD_WRITE){//要求客户端登陆if (eventReady.iErrorCode[FD_WRITE_BIT] == 0){char buff[100]="please input your username to login \n";int totalSend = ::send(sockArray[i], buff, strlen(buff), 0);}}else if (eventReady.lNetworkEvents&FD_CLOSE){//关闭客户端通信if (eventReady.iErrorCode[FD_CLOSE_BIT] == 0){deleteIllegalUser(sockArray[i]);::closesocket(sockArray[i]);for (int j = i; j < totalEvent - 1; j++){sockArray[j] = sockArray[i];eventArray[j] = eventArray[j];}totalEvent--;}}}}}}UserBindSock * getUserBySocket(SOCKET socket){int i;for (i = 0; i < totalConnection; i++){if (LoginInfo[i].socket == socket)return &LoginInfo[i];}return NULL;}void deleteIllegalUser(SOCKET socket){int i;for (i = 0; i < totalConnection; i++){if (LoginInfo[i].socket == socket)break;}for (int j = i; j < totalConnection - 1; j++){LoginInfo[j] = LoginInfo[i];}totalConnection--;printf("a connection losed\n");}DWORD WINAPI toLogin(LPVOID pM){LoginHelper * loginHelper = (LoginHelper *)pM;char buff[100];//验证用户名if (!isExist(loginHelper->userName)){printf("closed an illegal connection!\n");strcpy(buff, "this user is not exist!");::send(loginHelper->user->socket, buff, strlen(buff), 0);deleteIllegalUser(loginHelper->user->socket);//关闭非法链接::closesocket(loginHelper->user->socket);}//验证密码strcpy(loginHelper->user->user, loginHelper->userName);strcpy(buff, "please input your password!");::send(loginHelper->user->socket, buff, strlen(buff), 0);//获取数据库该用户密码char * passWord;getPassword(loginHelper->userName, passWord);//暂时设置socket为阻塞状态,接收客户端密码::WSAEventSelect(loginHelper->user->socket, loginHelper->event, 0);DWORD dwFlag = 0;ioctlsocket(loginHelper->user->socket, FIONBIO, &dwFlag);//接收用户密码int totalRecv = ::recv(loginHelper->user->socket, buff, sizeof(buff), 0);::WSAEventSelect(loginHelper->user->socket, loginHelper->event, FD_READ | FD_CLOSE | FD_WRITE);if (totalRecv > 0){buff[totalRecv] = '\0';if (strcmp(buff, passWord) == 0){loginHelper->user->isLogin = 1;strcpy(buff, "login success!");::send(loginHelper->user->socket, buff, strlen(buff), 0);char  message[100][100];int len = 0;getDelayMessage(loginHelper->user->user, message, len);len--;while (len >= 0){printf("%s\n", *(message + len));strcpy(buff, *(message + len));::send(loginHelper->user->socket, buff, strlen(buff), 0);len--;}}else{strcpy(buff, "wrong password,please try again later!");::send(loginHelper->user->socket, buff, strlen(buff), 0);deleteIllegalUser(loginHelper->user->socket);//关闭链接::closesocket(loginHelper->user->socket);}}else{strcpy(buff, "wrong password,please try again later!");::send(loginHelper->user->socket, buff, strlen(buff), 0);deleteIllegalUser(loginHelper->user->socket);//关闭链接::closesocket(loginHelper->user->socket);}saveEvents(loginHelper->event, loginHelper->user->socket);return 0;}void saveEvents(WSAEVENT event, SOCKET socket){InitializeCriticalSection(&cs);EnterCriticalSection(&cs);eventArray[totalEvent] = event;sockArray[totalEvent] = socket;totalEvent++;LeaveCriticalSection(&cs);}

3、客户端代码

//////////////////////////////////////////////////////////// TCPClient.cpp文件#include <stdio.h>//#include <windows.h>  #include <winsock2.h>#pragma comment(lib, "WS2_32")DWORD WINAPI ThreadFun(LPVOID pM)  {      SOCKET *s = (SOCKET *)pM;      while(true){char receive[200];int nRecv = ::recv(*s, receive, 256, 0);if(nRecv>0){receive[nRecv]='\0';printf("%s \n",receive);}}    return 0;  }  int main(){WSADATA wsaData;BYTE minorVer = 2;BYTE majorVer = 2;WORD sockVersion = MAKEWORD(minorVer, majorVer);if(::WSAStartup(sockVersion, &wsaData) != 0){exit(0);}// 创建套节字SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if(s == INVALID_SOCKET){printf(" Failed socket() \n");return 0;}// 也可以在这里调用bind函数绑定一个本地地址// 否则系统将会自动安排// 填写远程地址信息sockaddr_in servAddr; servAddr.sin_family = AF_INET;servAddr.sin_port = htons(4567);// 注意,这里要填写服务器程序(TCPServer程序)所在机器的IP地址// 如果你的计算机没有联网,直接使用127.0.0.1即可servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1){printf(" Failed connect() \n");return 0;}HANDLE handle = CreateThread(NULL, 0, ThreadFun, (LPVOID)&s, 0, NULL);char buff[256];while(true){scanf("%s",buff);::send(s,buff,strlen(buff),0);}// 关闭套节字::closesocket(s);return 0;}





0 0