首先说明,此小程序是写完后才更新本博客的,所以程序中涉及的内容都是完整了的,与客户端的联系还得等待接下来的更新才会联系得上。
第一点:程序只属于个人测试程序,带有调试信息,同时也还有很多的不完善,觉得不好的,请各位尽管拍砖。~~
下面说正题:
开发环境和工具:Ubuntu 12.04、 Qt4.7 、vi
使用语言:C/C++
详细设计过程:
第一步,要设计一个服务器端,首先要考虑的就是我们如何初始化我们自己的服务器,然后设置好本地的服务器之后,等待客户端的连接。
这里我采用的是linux下的socket编程,按照之前在博客中提到的七步,初始化和设置本地服务器。之后便是开始监听,然后等待客户端的链接。
第二步:当有客户端连接到服务器端之后,我们需要做的是把客户端的连接fd存储起来,然后开辟一个线程用于处理客户端的请求。
在这里我使用的是C++中的容器vector,我在程序中设置了一个全局的vector变量,用于存储客户端的连接到服务器分配给客户端的fd。以便我接下俩用于发送消息回客户端。
第三步:当客户端连接上后,我们需要做的便是开始处理线程,我们为没一个客户端的连接开辟了一个线程处理函数,处理客户端的连接。
第四步:我们开始线程后,就是处理客户端的信息了,这使用了简单的客户端和服务器的数据封装,以便我方便解析到底客户端发送过来的是命令信息还是聊天信息。这样我就可以知道客户端到底想干嘛,然后分别做不同的处理。
关于协议封装这一块,其实还有很多可以做,这里我只是简单的定义了一个头,区分命令和聊天信息而已,有兴趣的可以继续深入协议封装。想现在的zigbee这类的技术。很多都支持二次封装开发。
以下我会给出整个实现的代码,并且写有很多注释。具体要联系接下来的客户端代码一起看,才好。尽量在未来3天内更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <netdb.h> #include <time.h> #include <signal.h> #include <pthread.h> #include <vector> #include <arpa/inet.h> #include <QDebug> using namespace std; int socketfd; //定义一个表示本服务器的socketfd vector<int> v; //定义一个全局容器,存储链接的上的客户端的socketfd int roleFlag1 = 0; //标记下棋的客户端 int roleFlag2 = 0; int flag; //标记转换下棋的角色 int winOrFailure[30][30]={-1}; //记录下棋的位置,以及是谁下了棋子在哪里,用于判断输赢 //输出客户端信息 void out_addr(struct sockaddr *addr) { struct sockaddr_in *a = (struct sockaddr_in *)addr; char ip[16]; memset(ip,0,16); if(a->sin_family == AF_INET) { printf("IPv4 "); printf("%s (%d) connected!\n",(char *)inet_ntop(AF_INET,&a->sin_addr.s_addr,ip,sizeof(ip)),ntohs(a->sin_port)); } else { printf("OTHR"); } } //发送聊天信息 void senMsg(char *msg) { int i=0; for(i=0;i<v.size();i++) //容器循环的遍历所有客户端的socketfd,这里也可以使用容器迭代遍历容器 { write(v.at(i),msg,strlen(msg)); //发送聊天信息给所有的客户端 } } //再次开局,从置所有的标记和下棋记录 void resetGame(void) { roleFlag1 = roleFlag2 = flag = 0; memset(winOrFailure,-1,sizeof(winOrFailure)); } //发送输赢消息 void sendWin(int fd) { int i=0; char buf[30] = {'\0'}; sprintf(buf,"#Command:fd=%d",fd); //发送的消息是赢的客户端的socketfd for(i=0;i<v.size();i++) //发送给所有的客户端 { write(v.at(i),buf,strlen(buf)); } } //判断输赢 int jugeWinOrfailure(char *str,int fd) { int i,j; int x_bak; //记录点击的坐标,用户恢复 int y_bak; int count = 1; //判断输赢的时候统计有多少个连成直线 QString buf = QString(str); buf.remove(0,buf.indexOf("s")+1); int x = buf.left(buf.indexOf(",")).toInt(); int y = buf.mid(buf.indexOf(",")+1).toInt(); //获取当前这一步下棋的位置 winOrFailure[x][y] = fd; //存储到相应的输赢记录数组,记录是谁的棋子 //以下是判断输赢模块 x_bak = x;y_bak = y;count=0; while(winOrFailure[x][y] == winOrFailure[x][y-1]) { y = y-1; } for(i=0;i<5;i++) { if(y>=0 && y<29) { if(winOrFailure[x][y] == winOrFailure[x][y+i]) count=count+1; } if(count >= 5) { return fd;} } x = x_bak; y = y_bak;count=0; while(winOrFailure[x][y] == winOrFailure[x-1][y]) { x = x-1; } for(i=0;i<5;i++) { if(x>=0 && x<29) { if(winOrFailure[x][y] == winOrFailure[x+i][y]) count=count+1; } if(count >= 5) {return fd;} } x = x_bak; y = y_bak;count=0; while(winOrFailure[x][y] == winOrFailure[x+1][y+1]) { x = x+1; y = y+1; } for(i=0;i<5;i++) { if(x>=0 && x<29 && y<29 && y>=0) { if(winOrFailure[x][y] == winOrFailure[x-i][y-i]) count=count+1; } if(count >= 5) {return fd;} } x = x_bak; y = y_bak;count=0; while(winOrFailure[x][y] == winOrFailure[x-1][y-1]) { x = x-1; y = y-1; } for(i=0;i<5;i++) { if(x>=0 && x<29 && y<29 && y>=0) { if(winOrFailure[x][y] == winOrFailure[x+i][y+i]) count=count+1; } if(count >= 5) {return fd;} } return 0; } //发送按钮点击的位置回客户端 void sendButtonPos(char *message) { int i=0; for(i=0;i<v.size();i++) { write(v.at(i),message,strlen(message)); } } //点击开始按钮后,发回信息设置棋盘可用 void sendGridEnableTrue(char *message) { int i=0; for(i=0;i<v.size();i++) { write(v.at(i),message,strlen(message)); } } //发送请求开始按钮信息 void sendStartGameRequest(int fd,char *message) { if(fd == v.at(0)) write(v.at(1),message,strlen(message)); else if(fd == v.at(1)) write(v.at(0),message,strlen(message)); } //线程处理函数 void *thread_fun(void *arg) { int fd = (int)arg; char buf[1024] = {0}; printf("connected! to do service thread_fun\n"); while(1) { memset(buf,0,sizeof(buf)); int ret = read(fd,buf,sizeof(buf)); //qDebug()<<buf; //printf("Data from client:\n"); if(ret < 0) { exit(1); }else if(ret == 0){ ;} else if(strstr(buf,"#Command:") != NULL) //包含子串 { if(strcmp(buf,"#Command:AreYouReady?") == 0) //相等时是0 { //链接到服务器的客户端数量为0 if(v.max_size() <= 1) continue; roleFlag1 = fd; //给客户端1赋值 sendStartGameRequest(fd,buf); }else if(strcmp(buf,"#Command:StartGame!") == 0) { qDebug()<<"#Command:StartGame!\n"; sendGridEnableTrue(buf); roleFlag2 = fd; //给客户端2赋值 flag = roleFlag1; }else if((strstr(buf,"#Command:pos") != NULL) && (flag == fd)) { qDebug()<<"#Command is pos:"<<buf<<endl; sendButtonPos(buf); if(fd == roleFlag1) //角色1下了棋 flag = roleFlag2; else if(fd == roleFlag2) //角色2下了下棋 flag = roleFlag1; int win = jugeWinOrfailure(buf,fd); qDebug()<<win; if(win >= 5) sendWin(win); }else if(strcmp(buf,"#Command:SomeoneWin!") == 0) { qDebug()<<"#Command:SomeoneWin!"; resetGame(); } }else if(strstr(buf,"#Msg:") != NULL) { qDebug()<<"#Msg:.."; senMsg(buf); } } } int main(int argc,char *argv[]) { if(argc < 2) { fprintf(stderr,"%s error \n",strerror(errno)); exit(1); } //注册信号 //1、构建socket结构体 socketfd = socket(AF_INET,SOCK_STREAM,0); if(socketfd < 0) { fprintf(stderr,"socket:%s\n",strerror(errno)); exit(1); } //2、构建表达服务器的地址结构体 struct sockaddr_in saddr; //3、向结构体添加数据 saddr.sin_family = AF_INET; //Internet地址族IPV4 saddr.sin_port = htons(atoi(argv[1])); //设置端口号,把本地端口号转换为网络类型 saddr.sin_addr.s_addr = INADDR_ANY; //Internet地址 bzero(&saddr.sin_zero,8); //专门用于清零操作 //4、绑定地址结构体和socket描述符 if(bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr)) < 0) { fprintf(stderr,"bind:%s\n",strerror(errno)); close(socketfd); exit(1); } //5、监听客户端的链接 if(listen(socketfd,0)<0) { fprintf(stderr,"listen:%s\n",strerror(errno)); exit(1); } //记录客户端的结构体,和接收客户端的请求 struct sockaddr_in caddr; pthread_t tid; while(1) { int connfd; memset(&caddr,0,sizeof(caddr)); socklen_t len = sizeof(caddr); //准备接收客户端的信息服务 connfd = accept(socketfd,(struct sockaddr *)&caddr,&len); if(connfd < 0) { fprintf(stderr,"accept:%s\n",strerror(errno)); close(socketfd); exit(1); } out_addr((struct sockaddr *)&caddr); //连接上一个客户端后开启线程处理函数 if(pthread_create(&tid,NULL,thread_fun,(void *)connfd) != 0) { fprintf(stderr,"pthread_create:%s\n",strerror(errno)); continue; } v.push_back(connfd); pthread_detach(tid); printf("connected!\n"); } return 0; } |