⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 my_server.c

📁 《LINUX C编程实战》(作者:童永清
💻 C
字号:
// Client/Server模型的服务器端
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include "my_recv.h"	// 自定义的头文件

#define	SERV_PORT		4507	// 服务器端的端口
#define	LISTENQ			12	// 连接请求队列的最大长度

#define	INVALID_USERINFO	'n'	// 用户信息无效
#define	VALID_USERINFO		'y'	// 用户信息有效

#define	USERNAME		0	// 接收到的是用户名
#define	PASSWORD		1	// 接收到的是密码

struct userinfo {  	// 保存用户名和密码的结构体
	char username[32];
	char password[32];
};
struct userinfo users[ ] = {
	{"linux", "unix"},
	{"4507", "4508"},
	{"clh", "clh"},
	{"xl", "xl"},
	{" "," "}	   	// 以只含一个空格的字符串作为数组的结束标志
};

// 查找用户名是否存在,存在返回该用户名的下标,不存在则返回-1,出错返回-2
int find_name(const char *name)
{
	int i;
	
	if (name == NULL) {
		printf("in find_name, NULL pointer");
		return -2;
	}
	for (i=0; users[i].username[0] != ' ';i++) {
		if (strcmp(users[i].username, name) == 0) {
			return i;
		}
	}

	return -1;
}

// 发送数据
void send_data(int conn_fd, const char *string)
{
	if (send(conn_fd, string, strlen(string), 0) < 0) {
		my_err("send", __LINE__);  // my_err函数在my_recv.h中声明
	}
}

int main()
{
	int			sock_fd, conn_fd;
	int			optval;
	int			flag_recv = USERNAME; // 标识接收到的是用户还是密码
	int			ret;
	int			name_num;
	pid_t			pid;
	socklen_t		cli_len;
	struct sockaddr_in	cli_addr, serv_addr;
	char			recv_buf[128];
	
	// 创建一个TCP套接字
	sock_fd = socket(AF_INET, SOCK_STREAM,0);
	if (sock_fd < 0) {
		my_err("socket", __LINE__);
	}
	
	// 设置该套接字使之可以重新绑定端口
	optval = 1;
	if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, 
		(void *)&optval, sizeof(int)) < 0) {
		my_err("setsockopt", __LINE__);
	}
	
	// 初始化服务器端地址结构
	memset(&serv_addr, 0, sizeof (struct sockaddr_in));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERV_PORT);
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	// 将套接字绑定到本地端口
	if (bind(sock_fd, (struct sockaddr *)&serv_addr, 
		sizeof (struct sockaddr_in)) < 0) {
		my_err("bind", __LINE__);
	}
	
	// 将套接字转化为监听套接字
	if (listen(sock_fd, LISTENQ) < 0) {
		my_err("listen", __LINE__);
	}

	cli_len = sizeof (struct sockaddr_in);
	while (1) {
		// 通过accept接受客户端的连接请求,并返回连接套接字用于收发数据
		conn_fd = accept(sock_fd, (struct sockaddr *)&cli_addr, &cli_len);
		if (conn_fd < 0) {
			my_err("accept", __LINE__);
		}
		
		printf("accept a new client, ip:%s\n", inet_ntoa(cli_addr.sin_addr));
		// 创建一个子进程处理刚刚接受的连接请求
		if ( (pid = fork()) == 0 ) {	// 子进程
			while(1) {
				if ((ret = recv(conn_fd, recv_buf, sizeof (recv_buf), 0)) < 0) {
					perror("recv");
					exit(1);
				}
				recv_buf[ret-1] = '\0'; // 将数据结束标志'\n'替换成字符串结束标志

				if (flag_recv == USERNAME) {	// 接收到的是用户名
					name_num = find_name(recv_buf);
					switch (name_num) {
						case -1:
							send_data(conn_fd, "n\n");
							break;
						case -2:
							exit(1);
							break;
						default:
							send_data(conn_fd, "y\n");
							flag_recv = PASSWORD;
							break;
					}
				} else if (flag_recv == PASSWORD) {	 // 接收到的是密码
					if (strcmp(users[name_num].password, recv_buf) == 0) {
						send_data(conn_fd, "y\n");
						send_data(conn_fd, "Welcome login my tcp server\n");
						printf("%s login\n", users[name_num].username);
						break; // 跳出while循环
					} else 
						send_data(conn_fd, "n\n");
				}
			}
			close(sock_fd);
			close(conn_fd);
			exit(0);  // 结束子进程
		} else {   // 父进程关闭刚刚接受的连接请求,执行accept等待其他连接请求
			close(conn_fd); 
		}
	}
	
	return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -