局域网五子棋——C语言基于IO多路复用+简易进程工作池的网络编程小项目

复制即可运行

在这里插入图片描述

想拿走即可运行的同学请严格按照我的目录结构准备好文件夹和文件

拷贝完成后将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;
}


  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值