C++学习日志之二—贪吃蛇网游化框架搭建3通信部分
来源:互联网 发布:加密国际算法有哪些 编辑:程序博客网 时间:2024/04/29 05:38
七、通信部分
客户端与服务端采用TCP/IP协议进行通信,客户端连接函数代码如下:
void Game::gconnect(){ WSADATA wsaData; WORD sockVersion = MAKEWORD(2, 2); struct sockaddr_in servaddr; sockfd = socket(AF_INET, SOCK_STREAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(9999); inet_pton(AF_INET, "192.168.0.10", &servaddr.sin_addr); while (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) < 0) { SetOutputposition(38, 24, hout); printf("connect error"); Sleep(1000); printf(" press any key to retry"); _getch(); for (int i = 0; i != 40; i++) cout << "\b \b"; } SetOutputposition(53, 24, hout); printf("connected!"); Sleep(1000); for (int i = 0; i != 11; i++) cout << "\b \b";}
服务端MAIN代码:
int main(int argc, char **argv){ while (!connecttodb()) { printf("connect to database error,press any key to retry...\n"); system("pause>>nul"); } printf("connect to database succeed!\n"); WSADATA wsaData; WORD sockVersion = MAKEWORD(2, 2); if (WSAStartup(sockVersion, &wsaData) != 0) { exit(0); } int listenfd, connfd=-1; struct sockaddr_in servaddr; listenfd = socket(AF_INET, SOCK_STREAM, 0); memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(9999); bind(listenfd, (SA *)&servaddr, sizeof(servaddr)); listen(listenfd, LISTENQ); for (;;) { if ((connfd = accept(listenfd, (SA *)NULL, NULL)) < 0) { if (errno == EINTR) continue; else printf("accept error"); } else{ printf("connected\n"); str_ser(connfd); printf("disconnected!\n"); } } exit(0);
第一段为数据库连接代码,下一章内容详细讲。通信部分首先进行初始化,然后创建一个套接字,将套接字地址结构置零,设置端口与IP地址,客户端申请与服务端连接,连接失败则询问用户,服务端在连接成功后进入str_ser函数与客户端进行数据交换。
连接建立成功后,客户端进行登陆操作:
void Game::login(){ while (1) { char sendline[20]; SetOutputposition(38, 24, hout); cout << "Username: "; sendline[0] = 'u'; cin >> (sendline + 1); send(sockfd, sendline, strlen(sendline), 0); SetOutputposition(38, 25, hout); cout << "Password: "; for (int i = 0; i != 18;) { char temp = _getch(); if (isalnum(temp)) { sendline[i] = temp; cout << '*'; i++; } else if (temp == '\b'&&i > 0) { i--; sendline[i] = '\0'; cout << "\b \b"; } else if (temp == '\r') { if (i == 0) continue; sendline[i] = '\0'; break; } } send(sockfd, sendline, strlen(sendline), 0); char recvmsg[4]; int n = recv(sockfd, recvmsg, 4, 0); recvmsg[n] = 0; SetOutputposition(38, 26, hout); if (recvmsg[0] == 'y') break; cout << "User informaton error,please try again!"; SetOutputposition(78, 24, hout); for (int i = 0; i != 40; i++) cout << "\b \b"; SetOutputposition(78, 25, hout); for (int i = 0; i != 40; i++) cout << "\b \b"; }}
在整个的通信过程中,客户端通过在发送的数据头部加上标识,如用户名头部加上’u’,使服务端能够识别不同的数据并进行相应的处理。登陆过程中,首先输入用户名并发送给服务端,服务端接受数据并从数据库中提取相应的数据与之后接受到的密码进行对比,若查找用户名失败或密码不正确则发送n,反之发送y,客户端接受到y则继续运行,否则询问用户再次输入。
服务端处理接受数据的代码如下:
void str_ser(int connfd) { char user[20]; char codes[20]; char body[200] = "\0" ; char food[4]; char direction[3]; char score[3]; char record[3]; RETCODE retcode; int n; char recvline[MAXLINE];for (;;) { again: if ((n = recv(connfd, recvline, MAXLINE, 0)) > 0) { recvline[n] = 0; switch (recvline[0])//判断发送数据的头字母,区分不同的数据 { case user_name: strcpy(user, recvline); if (readdb(user)) { SQLLEN columnLen = 0; retcode = SQLBindCol(hstmt1, 1, SQL_C_CHAR, user, 200, &columnLen); retcode = SQLBindCol(hstmt1, 2, SQL_C_CHAR, codes, 200, &columnLen); retcode = SQLBindCol(hstmt1, 3, SQL_C_CHAR, body, 200, &columnLen); retcode = SQLBindCol(hstmt1, 4, SQL_C_CHAR, food, 200, &columnLen); retcode = SQLBindCol(hstmt1, 5, SQL_C_CHAR, direction, 200, &columnLen); retcode = SQLBindCol(hstmt1, 6, SQL_C_CHAR, score, 200, &columnLen); retcode = SQLBindCol(hstmt1, 7, SQL_C_CHAR, record, 200, &columnLen); retcode = SQLFetch(hstmt1);//从数据库获取数据 retcode = SQLCloseCursor(hstmt1);//执行select语句会分配一个cursor,下次执行必须先关闭该cursor if ((n = recv(connfd, recvline, MAXLINE, 0)) > 0) { recvline[n] = 0; char rpassword[20]; strcpy(rpassword, recvline); if (!strcmp(codes, rpassword)) { send(connfd, "y", strlen("y"), 0);//验证成功,发送y确认 break; } } } send(connfd, "n", strlen("n"), 0);//失败发送n break; case snake_body: strcpy(body, recvline); send(connfd, "LWB", 3, 0); break; case game_food: strcpy(food, recvline); send(connfd, "LWB", 3, 0); break; case snake_direction: strcpy(direction, recvline); send(connfd, "LWB", 3, 0); break; case game_score: strcpy(score, recvline); send(connfd, "LWB", 3, 0); break; case game_record: strcpy(record, recvline); send(connfd, "LWB", 3, 0); break; case ask_for_body: send(connfd, body, strlen(body), 0); break; case ask_for_dir: send(connfd, direction, strlen(direction), 0); break; case ask_for_food: send(connfd, food, strlen(food), 0); break; case ask_for_score: send(connfd, score, strlen(score), 0); break; case ask_for_record: send(connfd, record, strlen(record), 0); break; default: break; } } if (n < 0 && errno == EINTR) goto again; if (n <= 0)//客户端连接断开 { if (strlen(body)) writedb(user); break; }
readdb根据user名称从数据库读取其相应的数据,并根据接受到的不同数据保存到相应的缓存中,在客户端退出断开连接的时候用writerdb函数将缓存数据保存到数据库。根据main函数可以看出,登陆成功之后调用loaddata函数从服务器请求数据,并将请求到的数据分别载入到game、snake、和food类中:
void Game::loaddata(){ snake.recvdata(sockfd); food.recvdata(sockfd); char recvline[3]; send(sockfd, "S", 1, 0); if ((n = recv(sockfd, recvline, 3, 0)) > 0) recvline[n] = 0; score = recvline[1]; send(sockfd, "R", 1, 0); if ((n = recv(sockfd, recvline, 3, 0)) > 0) recvline[n] = 0; record = recvline[1];}bool Snake::recvdata(int &sock){ char recvline[200]; send(sock, "B", 1, 0); if ((n = recv(sock, recvline, 200, 0)) > 0) { recvline[n] = 0; body.clear(); for (int i = 1; i < n; i++, i++) { Point temp; temp.xx() = recvline[i]; temp.yy() = recvline[i + 1]; body.push_back(temp); } } else return false; send(sock, "D", 1, 0); if ((n = recv(sock, recvline, 200, 0)) > 0) { recvline[n] = 0; direction = recvline[1]; } else return false; return true;}
通过发送S告诉服务端请求score数据,同理,发送B请求snake的body数据等等,将收到的数据对已有的类进行初始化,在之后的游戏运行过程中,新建一个线程并不断调用一下senddata函数将游戏实时数据不断发往服务端,并在游戏退出或断开连接时让服务端将数据保存到数据库。
bool senddata() { string temp("s"); temp.push_back(score); if ((n = send(sockfd, temp.c_str(), strlen(temp.c_str()), 0)) < 0 || !chekans(sockfd)) return false; temp.assign("r"); temp.push_back(record); if ((n = send(sockfd, temp.c_str(), strlen(temp.c_str()), 0)) < 0 || !chekans(sockfd)) return false; if(!snake.senddata(sockfd)||!food.sendfood(sockfd)) return false; return true; }
阅读全文
0 0
- C++学习日志之二—贪吃蛇网游化框架搭建3通信部分
- C++学习日志之二—贪吃蛇网游化框架搭建1
- C++学习日志之二—贪吃蛇网游化框架搭建2
- C++学习日志之二—贪吃蛇网游化框架搭建4数据库连接
- C++学习日志之二—贪吃蛇网游化框架搭建5多线程同步
- C/S通信交互之Cocos2dx使用BSD Socket与Mina手机网游通信框架
- 【C/S通信交互之Socket篇】Cocos2dx(Client)使用BSD Socket与Mina(Server)手机网游通信框架!
- 【C/S通信交互之Socket篇】Cocos2dx(Client)使用BSD Socket与Mina(Server)手机网游通信框架
- 【C/S通信交互之Socket篇】Cocos2dx(Client)使用BSD Socket与Mina(Server)手机网游通信框架!
- 【C/S通信交互之Socket篇】Cocos2dx(Client)使用BSD Socket与Mina(Server)手机网游通信框架!
- 【C/S通信交互之Socket篇】Cocos2dx(Client)使用BSD Socket与Mina(Server)手机网游通信框架!
- 【C/S通信交互之Socket篇】Cocos2dx(Client)使用BSD Socket与Mina(Server)手机网游通信框架!
- c/c++学习 No.5 AI贪吃蛇(二)
- C语言版贪吃蛇:第一部分
- C语言版贪吃蛇:第二部分
- C语言版贪吃蛇:第三部分
- C语言版贪吃蛇:第四部分
- 贪吃蛇总结之二
- JavaScript/jQuery中函数调用加不加括号,加不加引号的解析
- 乘法口诀
- java总结
- 【论文阅读】Addressing the RareWord Problem in NeuralMachine Translation
- First Blood(C语言)
- C++学习日志之二—贪吃蛇网游化框架搭建3通信部分
- Swift中的typealias(别名)的用法
- 浅析Python运行原理
- java初学者买衣服案例
- Jquery方法click() bind() live() delegate()区别
- hibernate demo 搭建
- Day13-XML
- ScrollView组件
- Eclipse调试进入JRE系统库源码并且显示源码中变量信息