初学期间,在校学习了三子棋的编写,在学习网络编程之后,趋于兴趣,又实现了单机版的五子棋,之后就试图实现网络版五子棋,实现跨机对战。
我在单机版的五子棋上加入了socket,建立c/s/模型进行网络编程,然后实现客户端和服务器端的坐标数据互通,在数据互通之前在自己的终端下进行一系列判断,将合法的坐标传输至对方客户端,然后同时落子,双方同步落子,以此实现了最简单的双人网络版五子棋。
client.c
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/types.h>
#include "game.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
void SocketCommon(int sock, struct sockaddr_in* server)
#include <stdlib.h>
void Menu()
{
char buf[1024];
struct sockaddr_in peer;
while(1)
{
socklen_t len = sizeof(peer);
printf("Please Enter# \n");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s > 0)
{
buf[s-1] = 0;
sendto(sock,buf,strlen(buf),0,(struct sockaddr*)server,sizeof(*server));
ssize_t _s = recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr&peer,&len);
if(_s > 0)
{
buf[_s] = 0;
printf("server echo# %s\n",buf);
}
printf("==================================\n");
printf("|| 1.开始游戏 0.退出游戏 ||\n");
printf("==================================\n");
}
void Game(int sock)
{
Point point_server,point_client;
char board[ROWS][COLS];
InitBoard(board,ROWS,COLS);
PrintBoard(board,ROWS,COLS);
ssize_t s;
int x,y;
while(1){
printf("请下子(输入坐标)> ");
scanf("%d%d",&x,&y);
point_client.row = x-1;
point_client.col = y-1;
ClientMove(board,&point_client);
PrintBoard(board,ROWS,COLS);
write(sock,(void*)&point_client,sizeof(point_client));
if(GameState(board,&point_client) == 'X')
{
printf("You win!\n");
break;
}
else if(GameState(board,&point_client) == 'p')
{
printf("平局!\n");
break;
}
s = read(sock,(void*)&point_server,sizeof(point_server));
if(s == 0)
{
printf("Server quit\n");
return;
}
ServerMove(board,&point_server);
PrintBoard(board,ROWS,COLS);
if(GameState(board,&point_server) == 'O')
{
printf("Server win!\n");
break;
}
else if(GameState(board,&point_server) == 'p')
{
printf("平局!\n");
break;
}
}
}
int main(int argc, char* argv[])
{
if(argc < 3)
if(argc != 3)
{
perror("argc");
printf("Usage: ./server [ip] [port]\n");
exit(1);
}
//创建 socket 文件描述符
//AF_INET ipv4地址类型
//SOCK_DGRAM udp类型
//0 默认使用SOCK_DGRAM类型
int sock = socket(AF_INET,SOCK_DGRAM,0);
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
exit(2);
exit(1);
}
struct sockaddr_in server;
server.sin_family = AF_INET;//ipv4地址类型
server.sin_port = htons(atoi(argv[2]));//端口号
server.sin_addr.s_addr = inet_addr(argv[1]);//ipv4地址转换 因为输入的是点分十进制
struct sockaddr_in client;
client.sin_family = AF_INET;
client.sin_addr.s_addr = inet_addr(argv[1]);
client.sin_port = htons(atoi(argv[2]));
int ret = connect(sock,(const struct sockaddr*)&client,sizeof(client));
if(ret < 0)
{
perror("connect");
exit(2);
}
Menu();
int state;
while(1)
{
printf("请做出你的选择> ");
scanf("%d",&state);
if(state == 1)
{
Game(sock);
}else if(state == 0)
{
break;
}
else
{
printf("选择错误\n");
Menu();
sleep(2);
}
}
SocketCommon(sock,&server);
close(sock);
return 0;
}
#include<stdio.h>
#define ROWS 11
#define COLS 13
struct Point
{
int row;
int col;
};
//初始化棋盘
void InitBoard(char board[ROWS][COLS])
{
memset(board, ' ', ROWS*COLS*sizeof(char));//每个坐标的初始值我用空格代替
}
//打印棋盘
void PrintBoard(char board[ROWS][COLS])//打印棋盘
{
int i = 0;
printf(" ");
for (i = 1; i < COLS; i++)
printf(" %2d ",i);
printf(" %2d \n",i);
for (i = 0; i < ROWS; i++)
{
printf("%2d ", i + 1);
int j = 0;
int col = COLS;
for (; j < COLS; j++)
{
if (j == COLS - 1)
{
printf(" %c \n", board[i][j]);
if (i != ROWS - 1){
printf(" ");
while (--col)
printf("---|");
printf("---\n");
}
break;
}
printf(" %c ", board[i][j]);
printf("|");
}
}
}
//判断棋盘是否满了
int IsFall(char board[ROWS][COLS])
{
size_t i = 0;
for (; i < ROWS; i++)
{
size_t j = 0;
for (; j < COLS; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
//落子
void Move(char board[ROWS][COLS],struct Point* point, char ch)
{
if (point == NULL)
{
return;
}
int x = point->row;
int y = point->col;
//如果棋盘满了的话就退出循环,也就是不能再落子了
while (1)
{
if (x >= 0 && x < ROWS && y >= 0 && y < COLS){
if (board[x][y] == ' ')
{
board[x][y] = ch;
point->row = x;
point->col = y;
break;
}
else
{
printf("该坐标已落子,请重新输入坐标: ");
scanf("%d%d", &x, &y);
x--;
y--;
}
}
else{
printf("坐标输入有误,请重新输入坐标: ");
scanf("%d%d", &x, &y);
x--;
y--;
}
}
}
//列齐
int Cols(char board[ROWS][COLS], struct Point* point)
{
int x = point->row;
int y = point->col;
int count = 1;
while (x - 1 >= 0)
{
if (board[x][y] != board[x - 1][y])
break;
count++;
if (count == 5)
{
//如果往上遍历已经够了五个,说明已经赢了,直接返回
return count;
}
x--;
}
//到这里说明当前点往上走是不满足条件的
//但是已经走到了最上面的点,所以从临界点往相反方向找
//必须要找到五个连续的才算满足条件
count = 1;//count 重新被设定为1
while (x + 1 <= ROWS)
{
if (board[x][y] == board[x + 1][y])
{
count++;
if (count == 5)
{
return count;
}
x++;
}
else
{
//n 代表当前状态未满足赢的条件
return 0;
}
}
return 0;
}
//行齐
int Rows(char board[ROWS][COLS], struct Point* point)
{
int x = point->row;
int y = point->col;
int count = 1;
while (y - 1 >= 0)
{
if (board[x][y] != board[x][y - 1])
break;
count++;
if (count == 5)
{
//如果往左遍历已经够了五个,说明已经赢了,直接返回
return count;
}
y--;
}
//到这里说明当前点往左走是不满足条件的
//但是已经走到了最左面的点,所以从临界点往相反方向找
//必须要找到五个连续的才算满足条件
count = 1;//count 重新被设定为1
while (y + 1 <= COLS)
{
if (board[x][y] == board[x][y + 1])
{
count++;
if (count == 5)
{
return count;
}
y++;
}
else
{
//n 代表当前状态未满足赢的条件
return 0;
}
}
return 0;
}
//左上到右下的对角线
int UpLeft(char board[ROWS][COLS], struct Point* point)
{
int x = point->row;
int y = point->col;
int count = 1;
while (x - 1 >= 0 && y - 1 >= 0)
{
if (board[x][y] != board[x - 1][y - 1])
break;
count++;
if (count == 5)
{
//如果往左上遍历已经够了五个,说明已经赢了,直接返回
return count;
}
x--;
y--;
}
//到这里说明当前点往左上走是不满足条件的
//但是已经走到了最左上的点,所以从临界点往相反方向找
//必须要找到五个连续的才算满足条件
count = 1;//count 重新被设定为1
while (x + 1 <= ROWS && y + 1 <= COLS)
{
if (board[x][y] == board[x + 1][y + 1])
{
count++;
if (count == 5)
{
return count;
}
x++;
y++;
}
else
{
//n 代表当前状态未满足赢的条件
return 0;
}
}
return 0;
}
//右上到左下的对角线
int UpRight(char board[ROWS][COLS],struct Point* point)
{
int x = point->row;
int y = point->col;
int count = 1;
while (x - 1 >= 0 && y + 1 <= COLS)
{
if (board[x][y] != board[x - 1][y + 1])
break;
count++;
if (count == 5)
{
//如果往右上遍历已经够了五个,说明已经赢了,直接返回
return count;
}
x--;
y++;
}
//到这里说明当前点往右上走是不满足条件的
//但是已经走到了最右上面的点,所以从临界点往相反方向找
//必须要找到五个连续的才算满足条件
count = 1;//count 重新被设定为1
while (x + 1 <= ROWS && y - 1 <= COLS)
{
if (board[x][y] == board[x + 1][y - 1])
{
count++;
if (count == 5)
{
return count;
}
x++;
y--;
}
else
{
//n 代表当前状态未满足赢的条件
return 0;
}
}
return 0;
}
//返回游戏进行状态/平局/输赢/正在进行//
char Iswin(char board[ROWS][COLS], struct Point* point)
{
if (point == NULL)
{
return 'e';
}
//判断当前点所在列是否是连续五个子儿
if (Cols(board, point) == 5)
{
return board[point->row][point->col];
}
//判断当前点所在行是否是连续五个子儿
else if (Rows(board, point) == 5)
{
return board[point->row][point->col];
}
//判断当前点所在左上与右下的对角线是否满足条件
else if (UpLeft(board, point) == 5)
{
return board[point->row][point->col];
}
//判断当前点所在左上与右下的对角线是否满足条件
else if (UpRight(board, point) == 5)
{
return board[point->row][point->col];
}
else if (IsFall(board))
{
//平局
return 'p';
}
//当前没有输赢,继续游戏
return 'g';
}
server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include "game.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
void SocketCommon(int sock)
{
char buf[1024];
struct sockaddr_in client;
while(1)
{
socklen_t len = sizeof(client);
ssize_t s = recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&client,&len);
if(s > 0)
{
buf[s] = 0;
printf("[%s:%d]:%s\n",inet_ntoa(client.sin_addr),nto(client.sin_port),buf);
fflush(stdout);
sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&client,sizeof(client));
}
}
}
int main(int argc, char* argv[])
{
if(argc < 3)
if(argc != 3)
{
perror("argc");
printf("Usage: ./server [ip] [port]\n");
exit(1);
}
//创建 socket 文件描述符
//AF_INET ipv4地址类型
//SOCK_DGRAM udp类型
//0 默认使用SOCK_DGRAM类型
int sock = socket(AF_INET,SOCK_DGRAM,0);
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
exit(2);
exit(1);
}
struct sockaddr_in local;
local.sin_family = AF_INET;//ipv4地址类型
local.sin_port = htons(atoi(argv[2]));//端口号
local.sin_addr.s_addr = inet_addr(argv[1]);//ipv4地址转换 因为输入的是点分十进制
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(argv[1]);
server.sin_port = htons(atoi(argv[2]));
if(bind(sock,(const struct sockaddr*)&local,sizeof(local)) < 0)
int ret = bind(sock,(const struct sockaddr*)&server,sizeof(server));
if(ret < 0)
{
perror("bind");
exit(2);
}
ret = listen(sock,5);
if(ret < 0)
{
perror("listen");
exit(3);
}
SocketCommon(sock);
for(;;)
{
struct sockaddr_in client;
socklen_t len;
int client_sock = accept(sock,(struct sockaddr*)&client,&len);
if(client_sock < 0)
{
continue;
}
ssize_t s;
Point point_server,point_client;
char board[ROWS][COLS];
InitBoard(board,ROWS,COLS);
int x,y;
while(1){
s = read(client_sock,(void*)&point_client,sizeof(point_client));
if(s == 0)
{
printf("Client quit...\n");
close(client_sock);
}
ClientMove(board,&point_client);
PrintBoard(board,ROWS,COLS);
if(GameState(board,&point_client) == 'X')
{
printf("Client win!\n");
break;
}
else if(GameState(board,&point_client) == 'p')
{
printf("平局!\n");
break;
}
printf("请下子(输入坐标)> ");
scanf("%d%d",&x,&y);
point_server.row = x-1;
point_server.col = y-1;
ServerMove(board,&point_server);
PrintBoard(board,ROWS,COLS);
write(client_sock,(void*)&point_server,sizeof(point_server));
if(GameState(board,&point_client) == 'O')
{
printf("You win!\n");
break;
}
else if(GameState(board,&point_client) == 'p')
{
printf("平局!\n");
break;
}
}//游戏下完了
}
close(sock);
return 0;
}