复制即可运行
想拿走即可运行的同学请严格按照我的目录结构准备好文件夹和文件
拷贝完成后将wuziqi.h文件中的IP地址改为自己linux的IP
ifconfig // Linux终端下查看本机IP地址
suto spt install net-tools // 上面不行的话先执行这条命令,再执行ifconfig
上述都完成后在linux终端在wuziqi目录下make即可启动服务端,这个时候bin目录下会有客户端的可执行文件,将其发给局域网内的其他电脑,添加可执行权限执行,就可以对战了
chmod 0777 wzq_client // 该命令加可执行权限
./wzq_client //执行
若是报thread的错误请执行下面的两条命令,make不报错的略过
sudo apt-get install glibc-doc
#man手册
sudo apt-get install manpages-posix manpages-posix-dev
下面按文件夹发代码,一共7个文件注意别粘贴错了!!!
首先是wuziqi目录下的Makefile文件
OBJS:=net.o server.o client.o
OBJSSER:=net.o server.o
OBJSCLI:=net.o client.o
APP:=wzq_server wzq_client
FLAGS:=-g -c
CC:=gcc
export OBJS APP FLAGS CC OBJSSER OBJSCLI
ALL:
make -C ./src
make -C ./obj
./bin/wzq_server
.PHONY:clean
clean:
$(RM) ./obj/*.o
$(RM) ./bin/*
其次是src目录下的Makefile
ALL:$(OBJS)
mv $^ ../obj
net.o:net.c
$(CC) $(FLAGS) $< -o $@ -lpthread
server.o:server.c
$(CC) $(FLAGS) $< -o $@ -lpthread
client.o:client.c
$(CC) $(FLAGS) $< -o $@ -lpthread
login.o:login.c
$(CC) $(FLAGS) $< -o $@ -lpthread
obj目录下的Makefile
ALL:$(APP)
mv $^ ../bin
wzq_server:$(OBJSSER)
$(CC) $^ -o $@ -lpthread
wzq_client:$(OBJSCLI)
$(CC) $^ -o $@ -lpthread
include目录下的wuziqi.h文件
#ifndef _WUZIQI_H
#define _WUZIQI_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <pthread.h>
#define DEFAULT_PORT 12345 // 端口号
#define DEFAULT_IP "192.168.0.103" // 服务器IP地址
#define MAXSIZE 128 // 缓冲区默认大小
#define MAXLISTEN 50 // 默认最大监听数
#define MAXTHREADCOUNT 20 // 最大线程数
#define INIT_BLOAD 15 // 默认15×15的棋盘
#define MAXFILECOUNT 1024 // 最大监听数
// 设置结构体
void fun_sockaddr(struct sockaddr_in * addr);
// 初始化棋盘
void bload_Init(char key[][INIT_BLOAD]);
// 展示棋盘
void show(char key[][INIT_BLOAD], int key_x, int key_y, char ch);
// 判断坐标是否有效,并解析坐标
int isExists(char buf[], int xy[3], char key[][INIT_BLOAD], char ch);
// 判断是否胜利
int isWin(char key[][INIT_BLOAD], int x, int y);
int direction(char key[][INIT_BLOAD], int x, int y, int dire);
// 获取棋子
char getButtn();
// 先手
void firstBload(int *sockfd, struct sockaddr_in *cliaddr, char ch);
// 后手
void secondBload(int* sockfd, struct sockaddr_in* cliaddr, char ch);
// 服务器中介两客户端交互
int response(int conn[]);
//void* response_chilrd(void* conn);
// 服务器socket
void fun_serversock(int *listenfd, struct sockaddr_in *servaddr);
// 客户端socket
void fun_sock(struct sockaddr_in * cliaddr, int *sockfd);
// 封装
void send_buf(int *butn, char id[], char pass[], char buff[]);
// 拆箱
void recv_buf(int *butn, char id[], char pass[], char buff[]);
#endif
src目录下的net.c文件
#include "../include/wuziqi.h"
// 客户端socket
void fun_sock(struct sockaddr_in * cliaddr, int *sockfd)
{
*sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(*sockfd < 0){
printf("socket error! \n");
exit(-1);
}
fun_sockaddr(cliaddr);
}
// 服务器socket
void fun_serversock(int *listenfd, struct sockaddr_in *servaddr)
{
// 建立socket链接
*listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd < 0){
printf("socket error!\n");
exit(0);
}
printf("listenfd = %d\n", listenfd);
fun_sockaddr(servaddr);
if(bind(*listenfd, (struct sockaddr*)servaddr, sizeof(struct sockaddr_in))<0){
perror("bind error");
exit(0);
}
printf("bind successful!\n");
if(listen(*listenfd, MAXLISTEN) < 0){
perror("listen error");
exit(0);
}
printf("listening...\n");
}
// 设置结构体参数
void fun_sockaddr(struct sockaddr_in * servaddr){
servaddr->sin_family = AF_INET;
servaddr->sin_port = htons(DEFAULT_PORT);
servaddr->sin_addr.s_addr = inet_addr(DEFAULT_IP);
}
// 初始化棋盘
void bload_Init(char key[][INIT_BLOAD])
{
if(key == NULL) return;
for(int i = 0; i < INIT_BLOAD; ++i){
for(int j = 0; j < INIT_BLOAD; ++j){
key[i][j] = '*';
}
}
printf("\n |");
printf("ˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉ");
printf("|\n");
printf(" |0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 |\n");
for(int i = 0; i < INIT_BLOAD; ++i){
printf("%-2d|", i);
for(int j = 0; j < INIT_BLOAD; ++j){
printf("%c ", key[i][j]);
}
printf("|%-2d\n", i);
}
printf(" |0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 |\n");
printf(" |______________________________|\n");
}
// 展示棋盘
void show(char key[][INIT_BLOAD], int key_x, int key_y, char ch)
{
if(key_x == -1 && key_y == -1){
return;
}
key[key_x][key_y] = ch;
printf("\n |");
printf("ˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉ");
printf("|\n");
printf(" |0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 |\n");
for(int i = 0; i < INIT_BLOAD; ++i){
printf("%-2d|", i);
for(int j = 0; j < INIT_BLOAD; ++j){
printf("%c ", key[i][j]);
}
printf("|%-2d\n", i);
}
printf(" |0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 |\n");
printf(" |______________________________|\n");
}
// 判断坐标, -1坐标有误,1落子成功,2胜利,-3该位置有棋子
int isExists(char buf[], int dire[3], char key[][INIT_BLOAD], char ch)
{
if(buf == NULL || key == NULL) return -1;
buf[strlen(buf)-1] = '\0';
int xy[2] = {-1, -1};
int pos = 0;
for(int i = 0; i < MAXSIZE; ++i){
if(buf[i] == ')' || buf[i] == '\0' || buf[i] == '-'){
break;
}
if(buf[i] >= '0' && buf[i] <= '9'){
if(buf[i+1] < '0' || buf[i+1] > '9'){
xy[pos] = buf[i] - '0';
}
else{
xy[pos] = buf[i] - '0';
xy[pos] = xy[pos]*10 + (buf[i+1] - '0');
++i;
}
++pos;
}
}
if((xy[0] < 0 || xy[0] >= INIT_BLOAD)
|| (xy[1] < 0 || xy[1] >= INIT_BLOAD)){
return -1;
}
else if(key[xy[0]][xy[1]] != '*'){
return -3;
}
else{
dire[0] = xy[0];
dire[1] = xy[1];
dire[2] = ch;
key[dire[0]][dire[1]] = dire[2];
}
if(isWin(key, xy[0], xy[1]) == 1){
return 2;
}
else{
return 1;
}
}
// 判断是否胜利
int isWin(char key[][INIT_BLOAD], int x, int y)
{
if(1 == direction(key, x, y, 1)){
return 1;
}
if(1 == direction(key, x, y, 2)){
return 1;
}
if(1 == direction(key, x, y, 3)){
return 1;
}
if(1 == direction(key, x, y, 4)){
return 1;
}
return 0;
}
// dire = 1左斜,2右斜,3横,4纵
int direction(char key[][INIT_BLOAD], int x, int y, int dire)
{
char ch = key[x][y];
int i = x, j = y;
int count = 1;
switch(dire){
case 1:
while(1){
++i,++j;
if(i < 0 || i > INIT_BLOAD || j < 0 || j > INIT_BLOAD){
break;
}
if(ch == key[i][j]){
++count;
}else{
break;
}
}
i = x, j = x;
while(1){
--i, --j;
if(i < 0 || i > INIT_BLOAD || j < 0 || j > INIT_BLOAD){
break;
}
if(ch == key[i][j]){
++count;
}else{
break;
}
}
break;
case 2:
while(1){
--i,++j;
if(i < 0 || i >= INIT_BLOAD || j < 0 || j >= INIT_BLOAD){
break;
}
if(ch == key[i][j]){
++count;
}else{
break;
}
}
i = x, j = y;
while(1){
++i, --j;
if(i < 0 || i >= INIT_BLOAD || j < 0 || j >= INIT_BLOAD){
break;
}
if(ch == key[i][j]){
++count;
}else{
break;
}
}
break;
case 3:
while(1){
++i;
if(i < 0 || i >= INIT_BLOAD){
break;
}
if(ch == key[i][y]){
++count;
}else{
break;
}
}
i = x;
while(1){
--i;
if(i < 0 || i >= INIT_BLOAD){
break;
}
if(ch == key[i][y]){
++count;
}else{
break;
}
}
break;
case 4:
while(1){
++j;
if(j < 0 || j >= INIT_BLOAD){
break;
}
if(ch == key[x][j]){
++count;
}else{
break;
}
}
j = y;
while(1){
--j;
if(j < 0 || j >= INIT_BLOAD){
break;
}
if(ch == key[x][j]){
++count;
}else{
break;
}
}
break;
default: break;
}
if(count >= 5){
return 1;
}
return 0;
}
// 先手
void firstBload(int *sockfd, struct sockaddr_in *cliaddr, char ch)
{
printf("等待对手选择棋子...\n");
recv(*sockfd, &ch, 1, 0);
char temp_ch = -2;
printf("对方选择了 %c \n", ch);
// 获取棋子
while (1){
temp_ch = getButtn();
if(temp_ch == ch){
printf("该棋子已被对手选定,请您重新挑选棋子...\n");
}else{
break;
}
}
ch = temp_ch;
send(*sockfd, &ch, 1, 0);
printf("已选中 %C \n", ch);
char buff[MAXSIZE] = {0};
char key[INIT_BLOAD][INIT_BLOAD] = {0};
system("clear");
bload_Init(key);// 初始化棋盘
int xy[3] = {-1, -1, -1}; // 存放坐标和棋子
printf("您是先手,请您先落子...\n");
// 业务处理
while(1){
xy[0] = -1, xy[1] = -1, xy[2] = -1;
bzero(buff, MAXSIZE);
fgets(buff, MAXSIZE, stdin);// 获取坐标
int flag = isExists(buff, xy, key, ch);
if(flag == 1){
// 落子成功
}else if(flag == -1){
printf("坐标有误!\n");
continue;
}else if(flag == 2){
printf("```恭喜您胜出!\n");
xy[0] = -1, xy[1] = -1, xy[2] = -8;
send(*sockfd, xy, sizeof(xy), 0);
break;
}else if(flag == -3){
printf("当前位置已有棋子...\n");
continue;
}
system("clear");
if(send(*sockfd, xy, sizeof(xy), 0) == -1)// 发送坐标
{
printf("落子失败,请重新落子!\n");
continue;
}
show(key, xy[0], xy[1], ch);
printf("等待对方落子...\n");
if(recv(*sockfd, xy, sizeof(xy), 0) == -1){
perror("recv error");
break;
}
if(xy[2] == -9){
printf("对放已逃跑\n```恭喜您胜出!```\n");
break;
}else if(xy[2] == -8){
printf("```你输了```\n");
break;
}
system("clear");
show(key, xy[0], xy[1], (char)xy[2]);
printf("轮到您落子...\n");
}
}
// 后手
void secondBload(int* sockfd, struct sockaddr_in* cliaddr, char ch)
{
ch = getButtn();
send(*sockfd, &ch, 1, 0);
char buff[MAXSIZE] = { 0 };
char key[INIT_BLOAD][INIT_BLOAD] = { 0 };
system("clear");
bload_Init(key);// 初始化棋盘
int xy[3] = { -1, -1, -1 }; // 存放坐标和棋子
printf("您是后手,请等待对手落子...\n");
// 业务处理
while (1) {
xy[0] = -1, xy[1] = -1, xy[2] = -1;
bzero(buff, MAXSIZE);
if (recv(*sockfd, xy, sizeof(xy), 0) == -1) {
perror("recv error");
continue;
}
if (xy[2] == -9) {
printf("对放已逃跑\n```恭喜您胜出!```\n");
break;
}
else if (xy[2] == -8) {
printf("```你输了```\n");
break;
}
lp:
system("clear");
show(key, xy[0], xy[1], xy[2]);
printf("轮到您落子...\n");
fgets(buff, MAXSIZE, stdin);// 获取坐标
int flag = isExists(buff, xy, key, ch);
if (flag == 1) {
// 落子成功
}
else if (flag == -1) {
printf("坐标有误!\n");
goto lp;
}
else if (flag == 2) {
printf("```恭喜您胜出!\n");
xy[0] = -1, xy[1] = -1, xy[2] = -8;
send(*sockfd, xy, sizeof(xy), 0);
break;
}
else if (flag == -3) {
printf("当前位置已有棋子...\n");
goto lp;
}
system("clear");
if (send(*sockfd, xy, sizeof(xy), 0) == -1)// 发送坐标
{
printf("落子失败,请重新落子!\n");
goto lp;
}
show(key, xy[0], xy[1], ch);
printf("等待对方落子...\n");
}
}
// 获取棋子
char getButtn()
{
int flag = -1;
lp:
printf("1:@;2:#;3:A;4:B;5:X;6:Q;7:$;8:&\n");
printf("请选择棋子皮肤\n");
scanf("%d", &flag);
switch (flag) {
case 1: return '@';
case 2: return '#';
case 3: return 'A';
case 4: return 'B';
case 5: return 'X';
case 6: return 'Q';
case 7: return '$';
case 8: return '&';
default: printf("对不起,没有这个棋子!请重新选择...\n");
goto lp;
}
}
// 简易线程池
// int response(int conn[])
// {
// pthread_t pits[10];
// for(int i = 0; i < 10; ++i){
// //pthread_create(&pits[i], );
// }
// }
// 服务器中介两客户端交互
int response(int conns[])
{
int flag = 0;// 0先手,1后手
int dire = -1; // 标记先手 0表示conns[0]先手 1表示conns[1]先手
char buf[MAXSIZE] = {0};
int ran = rand()%10; // 随机获取一个0-9的数字
if(ran % 2 == 0){// conns[0] 先手
send(conns[0], &flag, 4, 0);
++flag;
send(conns[1], &flag, 4, 0);
dire = 0;
}
else{// conns[1] 先手
send(conns[1], &flag, 4, 0);
++flag;
send(conns[0], &flag, 4, 0);
dire = 1;
}
if(dire == 1){ // 始终保证conns[0]是先手
int temp = conns[0];
conns[0] = conns[1];
conns[1] = temp;
}
char piece[2] = {-1, -1}; // 存放双方棋子
recv(conns[1], &piece[1], 1, 0);// 后手先选
send(conns[0], &piece[1], 1, 0);// 将棋子发送给先手,告诉先手不要重复选择
recv(conns[0], &piece[0], 1, 0);// 先手选棋子
int xy[3] = {-1, -1, -1};
// 对战开始
while(1){
xy[0] = -1, xy[1] = -1, xy[2] = -1;
if (recv(conns[0], xy, sizeof(xy), 0) == -1) {
perror("recv error");
continue;
}
if (xy[2] == -9) {
send(conns[1], xy, sizeof(xy), 0);
break;
}
else if (xy[2] == -8) {
send(conns[1], xy, sizeof(xy), 0);
break;
}
send(conns[1], xy, sizeof(xy), 0);
xy[0] = -1, xy[1] = -1, xy[2] = -1;
recv:
if (recv(conns[1], xy, sizeof(xy), 0) == -1) {
perror("recv error");
goto recv;
}
if (xy[2] == -9) {
send(conns[0], xy, sizeof(xy), 0);
break;
}
else if (xy[2] == -8) {
send(conns[0], xy, sizeof(xy), 0);
break;
}
send(conns[0], xy, sizeof(xy), 0);
}
return -1;
}
src目录下的server.c文件
#include "../include/wuziqi.h"
/*int llogin(int *fd)
{
int i = *fd;
int butn = -1;
char id[128] = {0};
char pass[16] = {0};
char temp[128] = {0};
recv(i, &butn, 4, 0);
if (butn == 1)
{ // 登录
recv(i, id, 128, 0);
recv(i, pass, 16, 0);
int ll = login_db(id, temp);
if (strcmp(temp, pass) == 0)
{ // 登录成功
send(i, &butn, 4, 0);
return 1;
}
else if (ll == -2)
{
butn = -2;
send(i, &butn, 4, 0);
}
else
{
butn = -1;
send(i, &butn, 4, 0);
}
}
else if (butn == 2)
{ // 注册
recv(i, id, 128, 0);
recv(i, pass, 16, 0);
int ff = insert_db(id, pass);
if (ff == 1)
{
send(i, &butn, 4, 0);
}
else
{
butn = -1;
send(i, &butn, 4, 0);
}
}
}*/
int main()
{
int listenfd, connfd;
struct sockaddr_in servaddr, cliaddr;
socklen_t perrlen = sizeof(cliaddr);
fun_serversock(&listenfd, &servaddr);
int sum_thread = 0; // 创建线程数,全局监控
// 创建监听队列
fd_set readfds, temp, loginning;
// 清空监听队列
FD_ZERO(&readfds);
FD_ZERO(&temp);
FD_ZERO(&loginning);
// 将监听文件描述符加入到监听队列中
FD_SET(listenfd, &readfds);
char buff[MAXSIZE] = {0};
int conns[2] = {-1, -1};
int pos = 0;
while (1)
{
temp = readfds;
int ret = select(MAXFILECOUNT, &temp, NULL, NULL, NULL);
if (ret < 0)
{
perror("select error");
return -1;
}
// 遍历监听队列
for (int i = 0; i < MAXFILECOUNT; ++i)
{
int flag = -1;
// 判断文件描述符是否在监听队列中
if (FD_ISSET(i, &temp))
{
if (i == listenfd)
{ // 监听到新的连接
connfd = accept(listenfd, NULL, NULL);
//FD_SET(i, &readfds);
conns[pos++] = connfd;
if (conns[1] != -1)
{
int temp_conns[2] = {conns[0], conns[1]};
conns[0] = -1, conns[1] = -1;
// 可以配对
pos = 0;
printf("配对成功\n");
pid_t pid = fork();
if (0 == pid)
{
int n = response(temp_conns);
if (n < 0)
{
//FD_CLR(temp_conns[0], &readfds); // 从消息队列中删除
//FD_CLR(temp_conns[1], &readfds);
close(temp_conns[0]);
close(temp_conns[1]);
}
}
else if (pid > 0)
{
//wait(NULL);
}
else
{
printf("创建失败!\n");
}
}
}
else
{
}
}
}
}
close(listenfd);
return 0;
}
src目录下的client.c文件
#include "../include/wuziqi.h"
int main()
{
struct sockaddr_in cliaddr;
int sockfd;
fun_sock(&cliaddr, &sockfd);
if (connect(sockfd, (struct sockaddr*)&cliaddr, sizeof(cliaddr)) < 0) {
perror("connect error");
return -1;
}
printf("connect successful!\n");
int flag = -1; // 接收先后判断位
printf("匹配中...\n");
recv(sockfd, &flag, 4, 0);
printf("--匹配成功!%d\n", flag);
if(flag == 0){
// 先手
firstBload(&sockfd, &cliaddr, -1);
}else{
// 后手
secondBload(&sockfd, &cliaddr, -1);
}
printf("再次运行即可继续游戏!\n");
close(sockfd);
return 0;
}