使用

  1. 科学上网+全局代理
  2. 浏览器打开隐私
  3. https://chat.openai.com/
  4. “继续” 或者 “continue” 接着上一对话

编写 socket 聊天程序

问:socket 聊天程序

以下是一个简单的基于 TCP 协议的 socket 聊天程序,包含服务器端和客户端代码。该程序可实现在客户端和服务器端之间双向通信的功能。

服务器端代码:chat_server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 1024
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int serv_sock, clnt_sock;
	struct sockaddr_in serv_addr, clnt_addr;
	socklen_t clnt_addr_size;

	char message[BUF_SIZE];
	int str_len;

	if (argc != 2) {
		printf("Usage: %s <port>\n", argv[0]);
		exit(1);
	}

	serv_sock = socket(PF_INET, SOCK_STREAM, 0);
	if (serv_sock == -1)
		error_handling("socket() error");

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(atoi(argv[1]));

	if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
		error_handling("bind() error");

	if (listen(serv_sock, 5) == -1)
		error_handling("listen() error");

	clnt_addr_size = sizeof(clnt_addr);
	clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
	if (clnt_sock == -1)
		error_handling("accept() error");

	while (1) {
		str_len = read(clnt_sock, message, BUF_SIZE);
		if (str_len == -1)
			error_handling("read() error");
		printf("Message from client: %s", message);
		fputs("Input message (q to quit): ", stdout);
		fgets(message, BUF_SIZE, stdin);
		if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
			break;
		write(clnt_sock, message, strlen(message));
	}

	close(clnt_sock);
	close(serv_sock);
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

客户端代码:chat_client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 1024
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int sock;
	struct sockaddr_in serv_addr;
	char message[BUF_SIZE];
	int str_len;

	if (argc != 3) {
		printf("Usage: %s <IP> <port>\n", argv[0]);
		exit(1);
	}

	sock = socket(PF_INET, SOCK_STREAM, 0);
	if (sock == -1)
		error_handling("socket() error");

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
	serv_addr.sin_port = htons(atoi(argv[2]));

	if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
		error_handling("connect() error");
	else
		puts("Connected......");
	while (1) {
		fputs("Input message (q to quit): ", stdout);
		fgets(message, BUF_SIZE, stdin);
		if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
			break;
		write(sock, message, strlen(message));
		str_len = read(sock, message, BUF_SIZE - 1);
		if (str_len == -1)
			error_handling("read() error");
		message[str_len] = 0;
		printf("Message from server: %s", message);
	}

	close(sock);
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

上述程序中,服务器端通过调用 socket()、bind()、listen() 和 accept() 等函数创建一个套接字、绑定 IP 地址和端口号、监听连接请求,并接受来自客户端的连接请求。一旦连接建立,服务器端通过调用 read() 函数从套接字中读取客户端发送的消息,然后向套接字中写入服务器端要发送的消息。

客户端通过调用 socket() 和 connect() 函数创建一个套接字并向服务器端发起连接请求,一旦连接建立,客户端通过调用 write() 函数向套接字中写入客户端要发送的消息,并通过调用 read() 函数从套接字中读取服务器端返回的消息。

需要注意的是,在读写套接字时,需要考虑到网络字节序和主机字节序之间的转换,以及消息的截断和缓冲区溢出等问题。为了保证程序的健壮性,需要对这些问题进行认真处理。

CC = gcc
CFLAGS = -Wall -Wextra -pedantic -std=c99
LIBS = -lpthread

all: chat_server chat_client

chat_server: chat_server.o
	$(CC) $(CFLAGS) -o $@ $^ $(LIBS)

chat_server.o: chat_server.c
	$(CC) $(CFLAGS) -c -o $@ $^

chat_client: chat_client.o
	$(CC) $(CFLAGS) -o $@ $^

chat_client.o: chat_client.c
	$(CC) $(CFLAGS) -c -o $@ $^

clean:
	rm -f chat_server chat_client *.o

该 Makefile 定义了两个目标,即 chat_server 和 chat_client。chat_server 的依赖是 chat_server.o,该目标生成了一个聊天服务器可执行文件。chat_client 的依赖是 chat_client.o,该目标生成了一个聊天客户端可执行文件。

Makefile 还定义了清除目标 clean,它可以清除生成的可执行文件和目标文件。

chat

以上就是使用 ncurses 编写俄罗斯方块游戏的示例代码,其中包括了初始化游戏区域、随机生成方块、移动方块、旋转方块、消除行、判断游戏是否结束等功能。这个示例代码只是一个基础的版本,可以根据需求添加更多功能和界面元素。

用 ncurses 编写一个俄罗斯方块游戏

#include <unistd.h>
#include <ncurses.h>
#include <stdlib.h>
#include <time.h>

// 定义俄罗斯方块方块的形状
char block[7][4][4] = {
	{
		"X  ",
		"X  ",
		"X  ",
		"X  "
	},
	{
		"X  ",
		"XX ",
		"X  ",
		"   "
	},
	{
		"X  ",
		"XX ",
		" X ",
		"   "
	},
	{
		"   ",
		"XX ",
		"XX ",
		"   "
	},
	{
		" X ",
		"XX ",
		"X  ",
		"   "
	},
	{
		"X  ",
		"XX ",
		" X ",
		"   "
	},
	{
		"X  ",
		"X  ",
		"XX ",
		"   "
	}
};

// 定义游戏区域的宽度和高度
#define WIDTH 10
#define HEIGHT 20

// 定义游戏区域
char board[HEIGHT][WIDTH];

// 定义当前方块的位置和类型
int cur_x, cur_y;
int cur_block_type;

// 定义游戏结束的标志
int gameover = 0;

// 随机生成一个方块
void new_block()
{
	cur_block_type = rand() % 7;
	cur_x = WIDTH / 2 - 2;
	cur_y = 0;
}

// 绘制游戏区域
void draw_board()
{
	clear();

	for (int i = 0; i < HEIGHT; i++) {
		for (int j = 0; j < WIDTH; j++) {
			if (board[i][j]) {
				mvprintw(i, j, "X");
			}
		}
	}

	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			if (block[cur_block_type][i][j] == 'X') {
				mvprintw(cur_y + i, cur_x + j, "X");
			}
		}
	}

	refresh();
}

// 判断方块是否能够向下移动
int can_move_down()
{
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			if (block[cur_block_type][i][j] == 'X') {
				int x = cur_x + j;
				int y = cur_y + i + 1;

				if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT || board[y][x]) {
					return 0;
				}
			}
		}
	}

	return 1;
}

// 移动方块
void move_block(int dx, int dy)
{
	cur_x += dx;
	cur_y += dy;
}

// 旋转方块
void rotate_block()
{
	char temp[4][4];

	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {

			temp[i][j] = block[cur_block_type][i][j];
		}
	}

	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			block[cur_block_type][i][j] = temp[3-j][i];
		}
	}

	if (cur_x < 0) {
		cur_x = 0;
	} else if (cur_x + 4 > WIDTH) {
		cur_x = WIDTH - 4;
	}
}

// 消除一行
void remove_line(int y)
{
	for (int i = y; i > 0; i--) {
		for (int j = 0; j < WIDTH; j++) {
			board[i][j] = board[i-1][j];
		}
	}
	for (int j = 0; j < WIDTH; j++) {
		board[0][j] = 0;
	}

}

// 消除所有满行
void remove_full_lines()
{
	int lines_cleared = 0;

	for (int i = 0; i < HEIGHT; i++) {
		int full = 1;

		for (int j = 0; j < WIDTH; j++) {
			if (!board[i][j]) {
				full = 0;
				break;
			}
		}

		if (full) {
			remove_line(i);
			lines_cleared++;
		}
	}

	if (lines_cleared) {
		draw_board();
	}

}

// 判断游戏是否结束
int is_gameover()
{
	for (int j = 0; j < WIDTH; j++) {
		if (board[0][j]) {
			return 1;
		}
	}
	return 0;

}

int main()
{
	// 初始化 ncurses
	initscr();
	cbreak();
	noecho();
	keypad(stdscr, TRUE);
	curs_set(0);
	srand(time(NULL));

	// 初始化游戏区域
	for (int i = 0; i < HEIGHT; i++) {
		for (int j = 0; j < WIDTH; j++) {
			board[i][j] = 0;
		}
	}

	// 开始游戏循环
	while (!gameover) {
		// 随机生成一个方块
		new_block();

		// 循环处理当前方块
		while (can_move_down()) {
			draw_board();

			int ch = getch();

			switch (ch) {
				case KEY_LEFT:
					if (cur_x > 0) {
						move_block(-1, 0);
					}

					break;
				case KEY_RIGHT:
					if (cur_x + 4 < WIDTH) {
						move_block(1, 0);
					}

					break;
				case KEY_DOWN:
					move_block(0, 1);
					break;
				case KEY_UP:
					rotate_block();
					break;
				case 'q':
					gameover = 1;
					break;
			}

			if (gameover) {
				break;
			}

			if (!can_move_down()) {
				for (int i = 0; i < 4; i++) {
					for (int j = 0; j < 4; j++) {
						if (block[cur_block_type][i][j] == 'X') {
							board[cur_y + i][cur_x + j] = 1;
						}
					}
				}

				remove_full_lines();

				if (is_gameover()) {
					gameover = 1;
				}
			}

			cur_y++;

			usleep(10000);
		}

		usleep(50000);
	}

	// 结束游戏循环,清理 ncurses
	endwin();

	return 0;
}

以上就是使用 ncurses 编写俄罗斯方块游戏的示例代码,其中包括了初始化游戏区域、随机生成方块、移动方块、旋转方块、消除行、判断游戏是否结束等功能。这个示例代码只是一个基础的版本,可以根据需求添加更多功能和界面元素。

CC = gcc
# warning: implicit declaration of function ‘usleep’; did you mean ‘sleep’? [-Wimplicit-function-declaration]
# CFLAGS = -Wall -Wextra -Werror -std=c99 -pedantic
CFLAGS = -Wall -Wextra -Werror -pedantic
LIBS = -lncurses

all: tetris

tetris: tetris.o
	$(CC) $(CFLAGS) -o tetris tetris.o $(LIBS)

tetris.o: tetris.c
	$(CC) $(CFLAGS) -c tetris.c

clean:
	rm -f *.o tetris