欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux上的一个简单的多人聊天室

程序员文章站 2022-06-14 16:14:21
...
/*
 *i.h is a used for creating a library
 *for server client
 *Mar 18 2010	
 *
 */
#ifndef _I_H

#define _I_H

#include <math.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <inttypes.h>
#include <time.h>
#include <sys/ioctl.h> 
#include <net/if.h>
#include <signal.h>
#include <ncurses.h>
#include <math.h>

#define SEVR_IP     "127.0.0.1"
#define SEVR_PORT   8081
#define CNTNT_LEN   150
#define MSG_LEN     sizeof(struct msg)
#define ADDR_LEN    sizeof(struct sockaddr)
#define USR_LEN     sizeof(struct user)
#define PRT_LEN     8
#define HSTR_LEN    sizeof(struct chat_history)

/* declare Global variables */
int mainfd;/* used as chat histroy file handle*/
int sockfd;/* used as socket local handle */
int count;
struct sockaddr_in server;

/* msg is used for communicating message */
struct msg
{
	int flag; /* flag meaning:1,ordinary; 2,log msg; 3,reg msg, other,file*/
	int id_from;
	int id_to;
	char content[CNTNT_LEN];
	char append[10]; 
};

/* user is used information list */
struct user
{
	int id;
	char name[10];
	char password[10];
	char *p_chatlog;
	struct sockaddr user_addr;	
};
/* chat_history used for reading chat history */
struct chat_history
{
	char content[CNTNT_LEN];
	char time[25];
	int to;
	int from;
	int count;
};

/* i_functions below is funtions needed by both client and sever */
extern int i_saveto_chat(struct msg *pmsg);

int i_clean_stdin ()
{
	while ('\n' == getchar())
	{
		continue;
	}

	return(0);
}

int i_print(char *pmsg, int size)
{
	int i = 1;

	for (i; i<= size; i++)
	{
		if (*pmsg != '\n')
		{
			printf("%c", *pmsg);
			pmsg ++;
		}
		else 
		{
			return(0);
		}
	}

	return(0);
}
int i_input(char *p_input)
{
	char c = '\0';
	int i;	

	for (i = 0; i < CNTNT_LEN; i++)
	{
		p_input[i] = getchar();
		if (p_input[i] =='\n')
		{
			return(0);		
		}		
	}

	printf("you have input long enough!\n");
	return(0);
}
int i_socket(int domain, int type, int protocol)
{
	int fd;	

	if ((fd = socket(domain, type, protocol)) == -1)
	{
		perror("creat socket error:");
		exit(1);
	}
	
	return(fd);	
}

int i_bind(int fd, const struct sockaddr *addr, int namelen)
{
	if (-1 == bind(fd, addr, namelen))
	{
		perror("i_bind error:");
		exit(1);
	}
	
	return (0);
}

int i_recvfrom(int fd, void *buf, size_t len, int flags, 
		struct sockaddr *addr, int *size)
{	
	if (-1 == recvfrom(fd, buf, len, flags, addr, size))
	{
		perror("i_recvfrom error:");
		exit(1);	
	}
	
	return(0);
}

int i_sendto(int fd, void *buf, size_t len, int flags,
		struct sockaddr *addr, int size)
{
	if (-1 == sendto(fd, buf, len, flags, addr, size))
	{
		perror("i_sendto error");
		exit(1);	
	}
	
	return (0);
}

int i_open(const char *pathname, int flags)
{
	int fd;
	if ((fd = open(pathname, flags)) == -1)
	{
		perror("open_failed");
		exit(1);
	}
	
	return (fd);
}
int i_read(int fd, void *msg, int len)
{
	if(-1 == read(fd, msg, len))
	{
		perror("i_read error");
		exit(1);
	}
	return(0);
}
int i_write(int fd, void *msg, int len)
{
	if (-1 == write(fd, msg, len))
	{
		perror("i_write error");
		exit(0);
	}
	return(0);
}

/* init a socket,file and server addr */
int i_init()
{
	mainfd = i_open("./chat_log", O_RDWR|O_CREAT);
	sockfd = i_socket(AF_INET, SOCK_DGRAM, 0);

	/* initialize server address */
	bzero(&server, sizeof(server));
	server.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
	server.sin_port = htons(SEVR_PORT);

	perror("init");
	
	return (0);
}

char *i_get_time()
{
	time_t time_now;
	time(&time_now);

	return(ctime(&time_now));
}
int i_lseek(int fd, off_t size, int position)
{
	if (-1 == lseek(fd, size, position))
	{
		perror("seek error");
		exit(1);
	}
	return(0);
}
int i_saveto_chat(struct msg *pmsg)
{	
	struct chat_history hstr;


	bzero(&hstr, HSTR_LEN);
	count = count + 1;
	hstr.count =count;
	hstr.from = pmsg->id_from;
	hstr.to = pmsg->id_to;
	strncpy(hstr.content, pmsg->content, CNTNT_LEN);
	strncpy(hstr.time, i_get_time(), 25);

	i_lseek(mainfd, 0, SEEK_END);

	i_write(mainfd, &hstr, HSTR_LEN);

	return(0);
}

int i_print_history(int len, int i)
{
	struct chat_history chat_reader;
	int j;
	int position;
	
	bzero(&chat_reader, HSTR_LEN);
	if (i != 0)
	{
		position = len*i*HSTR_LEN;
		i_lseek(mainfd, position, SEEK_END);
	}
	else
	{
		position = len*i*HSTR_LEN;

		i_lseek(mainfd, HSTR_LEN, SEEK_SET);
	}
		
	for (j = 1; j <= len; j++)
	{
		
		i_read(mainfd, &chat_reader, HSTR_LEN);
		printf("\n#item%d:id%dto id%d \n", j,
			chat_reader.from, chat_reader.to);
		i_print(chat_reader.content, CNTNT_LEN);
		printf("\n  Time:%s\n", chat_reader.time);
	}

	return(0);
}

#endif
/*
 *title:server.c
 *content:server part
 *start time: Mar.25.2011
 *end time:  Apr.8 2011
 */

#include "i.h"

int user_list_fd;

/* start:initialization */
int init()
{
	i_init();

	user_list_fd = i_open("./user_list", O_RDWR|O_CREAT);

	struct user usr;
	/* init the user list file's fist user to 0*/
	memset((struct user*)&usr, '\0', sizeof(struct user));
	i_lseek(user_list_fd, 0, SEEK_SET);
	i_write(user_list_fd, (char*)&usr, USR_LEN);

	/* bind the struct sockaddr_in server to the sockfd */
	i_bind(sockfd, (struct sockaddr*)&server, ADDR_LEN);	

	struct chat_history apple;	

	bzero(&apple, HSTR_LEN);
	i_lseek(mainfd, 0, SEEK_SET);
	i_write(mainfd, &apple, HSTR_LEN);
	i_lseek(mainfd, -HSTR_LEN, SEEK_END);
	i_read(mainfd, &apple, HSTR_LEN);
	count = apple.count;

	return(0);
}
/* end:initialization */

/* start:message control */
int send_msg(struct msg *msg_recv, struct sockaddr *addr)
{
	int i;
	struct user usr;

	/* a common message come */	
	printf("a ordinar message come !\n");
	
	i = msg_recv->id_to;
	i_lseek(user_list_fd, i*USR_LEN, SEEK_SET);
	i_read(user_list_fd, &usr, USR_LEN);
	strncpy(msg_recv->append, usr.name, 10);

	i_sendto(sockfd, msg_recv, MSG_LEN, 0,
		&(usr.user_addr), ADDR_LEN);
	
	printf("id%d send a message to id%d sucess!\n", msg_recv->id_from, msg_recv->id_to);

	return(0);
}
int check_login(struct msg *msg_recv, struct sockaddr *addr)
{
	int i = msg_recv->id_from;;
	struct user usr;

	/* a login requet */
	printf("a login request come!\n");
	
	/* get the id's information */
	i_lseek(user_list_fd, i*USR_LEN, SEEK_SET);
	i_read(user_list_fd, &usr, USR_LEN);

	int n;
	n = strcmp(usr.password, msg_recv->content);
	/* 如果验证成功,则发送成功信息 */
	if (n == 0)
	{
		/* save user new address */
		i_lseek(user_list_fd, -USR_LEN, SEEK_CUR);
		usr.user_addr = *addr;
		i_write(user_list_fd, &usr, USR_LEN);
		/* tell user pass */
		i_sendto(sockfd, (struct msg*)msg_recv, sizeof(struct msg), 0,
			&(usr.user_addr), ADDR_LEN);
		
	}
	else
	{
		/* 出错的话的respond */
		if (0 != n)
		{
			printf("id %d login error.\n", i);
			bzero(msg_recv->content, CNTNT_LEN);			
			msg_recv->flag = -1;
			i_sendto(sockfd, (struct msg*)msg_recv, sizeof(struct msg), 0,
				&(usr.user_addr), ADDR_LEN);
		
		}
		return(1);
	}
	printf("Id %d login sucess!\n", i);	
	
	return(0);
}
int reg_user(struct msg *msg_recv, struct sockaddr *addr)
{
	struct user usr;
	
	printf("a regit requet come:\n");

	/* find the last user and hava the please to add a new user */
	int n;
	i_lseek(user_list_fd, -USR_LEN, SEEK_END);
	i_read(user_list_fd, &usr, USR_LEN);
	/* 把新用户的信息赋值到usr然后填入到user list file中 */
	const char *name;
	const char *password;

	name = &(msg_recv->content[0]);
	password = &(msg_recv->content[10]);
	strcpy((usr.name), name);
	strcpy(usr.password, password);
	memcpy(&(usr.user_addr),addr, ADDR_LEN);

	usr.id = (usr.id + 1);
	i_lseek(user_list_fd, 0, SEEK_END);
	i_write(user_list_fd, &usr, USR_LEN);

	msg_recv->id_from = usr.id;
	/* regist to the user list then tell the user reg success */
	i_sendto(sockfd, (struct msg*)msg_recv, sizeof(struct msg), 0,
		addr, ADDR_LEN); 

	printf("Id %d regist sucess!\n", usr.id);

	return(0);
	
}
int msg_cntl()
{
	struct msg msg_recv;
	struct sockaddr addr_recv;

	printf("begin listen input...\n");
	int size = ADDR_LEN;

	for (;;)
	{
		bzero(&msg_recv, MSG_LEN);
		i_recvfrom(sockfd, &msg_recv, sizeof(struct msg), 0,
			&addr_recv, &size);
		printf("message received...\n");

		i_saveto_chat(&msg_recv);

		switch (msg_recv.flag)
		{
			case 1 :
				send_msg(&msg_recv,(struct sockaddr*)&addr_recv);/* send ordinary chat */
				break;
			case 2 :
				check_login(&msg_recv, (struct sockaddr*)&addr_recv);
				break;			
			case 3 :
				reg_user(&msg_recv, (struct sockaddr*)&addr_recv);
				break;
			default :
				break;
		}
	}
	return(0);
}
/* end:message control*/
/* start:exit_sys()*/
int exit_sys()
{
	close(sockfd);
	close(mainfd);
	close(user_list_fd);
	printf("exit system");
	kill(0, SIGABRT);

	exit(0);
}
/* end:exit_sys()*/

/* start:chat_history*/
int get_page_size()
{
	struct chat_history page_size_reader;
	
	i_lseek(mainfd, -HSTR_LEN, SEEK_END);
	i_read(mainfd, &page_size_reader, HSTR_LEN);

	return(page_size_reader.count);
}

int read_chat_history()
{
	printf("****char*history***");
	printf("(n-nextpage; p-prepage; q-quit)\n");

	int page_num;/* */
	int remains;
	int berry = get_page_size();


	page_num = berry / 8;
	remains = berry % 8;

	if (remains != 0)
		page_num ++;
	else
		page_num = page_num;
		
	printf("there are %d page total %d items", 
		page_num, berry);

	int i = -1;

	while (1)
	{	
		char flag;	

		if ((berry + i*8) >= 0)
		{
			printf("(%d~%d)\n", (berry + i*8), (berry + (i+1)*8));

			i_print_history(PRT_LEN, i);

			printf("@@@\n");
			while ('\n' == (flag = getchar()))
			{
			}

			switch (flag)
			{
				case 'p' :
					i--;
					break;
				case 'n' :
					i++;
					break;
				case 'q' :
					return(0);
				default  :
					break;
			}	
			if (i >= 0)
			{
				printf("have at the end!\n");
				printf("return to menu!\n");
			}		
		}
		else 
		{
			printf("(1~%d)\n", remains);			
		
			i_print_history(remains, 0);
			
			printf("#########over##############\n");

			return(0);
		}	
	}
		
	return(0);
}
/* end:chat_history*/
/* start:menu*/
int menu()
{
	sleep(1);

	printf("----------help----menu---------\n");
	printf("\t r--report to user\n");
	printf("\t c--chat history\n");
	printf("\t h--help menu\n");
	printf("\t e--exit the system\n");
	printf("----------help_menu---------\n");

	int command = 0;

	printf("input command>");
	command = getchar();
	switch(command)
	{

		case 'c':
			read_chat_history();
			break;
		case 'e':
			exit_sys();
			break;
		case 'r':
			//report();
			//break;
		default :
			menu();
			break;
	}
	getchar();
	
	return(0);
}
/* end:menu*/
int main()
{
	init();
	pid_t pid;
	switch (pid = fork())
	{
		case -1 :
			perror("fork error\n");
			exit(1);
			break;
		case 0 :
			msg_cntl();
			break;
		default :
			menu();
			break;
	}

	return(0);
}
/*
 *title: client.c
 *start_time: Mar.18 2011
 *end_time:  Apr.8 2011
 */

#include "i.h"

#define START_PORT 8089

struct sockaddr_in my_addr;
int my_id;

int my_log();/* declare funtion*/

/* */
int i_send_msg()
{		
	int id;
	struct msg the_msg;
	char end = '@';

	printf("input recver id:");
	scanf("%d", &id);
	getchar();
	printf("\ninput content:");
	i_input(the_msg.content);	

	char flag = 'y';
		
	if (1)
	{
		the_msg.flag = 1;
		the_msg.id_from = my_id;
		the_msg.id_to = id;
		
		i_sendto(sockfd, &the_msg, sizeof(struct msg), 0,
			(struct sockaddr*)&server, sizeof(struct sockaddr));
		
		i_saveto_chat(&the_msg); /* save to history */

		printf("send to id:%d success.\n", my_id);
		return(0);
	}
	else
		return(1);

	return(0);
}

int reply()
{
	return(0);
}
int send_file()
{
	return(0);
}
/**/
/* start:initialize */
int init()
{	
	struct ifreq req;
	struct sockaddr_in *host;
	int port;

	i_init();
	/* init user addr */
	bzero(&my_addr, sizeof(struct sockaddr));
	my_addr.sin_family = AF_INET;
	strcpy(req.ifr_name, "lo");
	if ( ioctl(sockfd, SIOCGIFADDR, &req) < 0 ) /* get local ip address */
	{
		perror("get local ip error");
		exit(1);
        } 

	host = (struct sockaddr_in*)&(req.ifr_addr);
	printf("ip: %s\n", inet_ntoa(host->sin_addr));

	memcpy(&my_addr, (struct sockaddr_in*)&(req.ifr_addr),
		 sizeof(struct sockaddr_in));

	port = START_PORT;

	do 
	{
		port++;
		my_addr.sin_port = htons(port);
		bind(sockfd, (struct sockaddr*)&my_addr,
			 sizeof(struct sockaddr)); 		
	} 
	while (errno == EADDRINUSE);

	struct chat_history apple;	
	
	memset(&apple, 'b', HSTR_LEN);
	i_lseek(mainfd, 0, SEEK_SET);
	apple.count = 0;
	i_write(mainfd, &apple, HSTR_LEN);
	i_lseek(mainfd, -HSTR_LEN, SEEK_END);
	i_read(mainfd, &apple, HSTR_LEN);
	count = apple.count;

  
	printf("port:%d\n", port);	
	printf("init successful!!!\n");	

	return(0);

}
/* end:initialize */
/* start:chat_history*/
int get_page_size()
{
	struct chat_history page_size_reader;
	
	i_lseek(mainfd, -HSTR_LEN, SEEK_END);
	i_read(mainfd, &page_size_reader, HSTR_LEN);

	return(page_size_reader.count);
}

int read_chat_history()
{
	printf("****char*history***");
	printf("(n-nextpage; p-prepage; q-quit)\n");

	int page_num;/* */
	int remains;
	int berry = get_page_size();


	page_num = berry / 8;
	remains = berry % 8;

	if (remains != 0)
		page_num ++;
	else
		page_num = page_num;
		
	printf("there are %d page total %d items", 
		page_num, berry);

	int i = -1;

	while (1)
	{	
		char flag;	

		if ((berry + i*8) >= 0)
		{
			printf("(%d~%d)\n", (berry + i*8), (berry + (i+1)*8));

			i_print_history(PRT_LEN, i);

			printf("@@@\n");
			while ('\n' == (flag = getchar()))
			{
			}

			switch (flag)
			{
				case 'p' :
					i--;
					break;
				case 'n' :
					i++;
					break;
				case 'q' :
					return(0);
				default  :
					break;
			}	
			if (i >= 0)
			{
				printf("have at the end!\n");
				printf("return to menu!\n");
			}		
		}
		else 
		{
			printf("(1~%d)\n", remains);			
		
			i_print_history(remains, 0);
			
			printf("#########over##############\n");

			return(0);
		}	
	}
		
	return(0);
}
/* end:chat_history*/
/* start:exit_sys*/
void exit_sys()
{
	close(sockfd);
	close(mainfd);
	kill(0, SIGABRT);

	exit(0);
}
/* end:exit_sys*/

/* start:menu*/
int print_menu()
{
	printf("\n--------------help--menu----------------\n");
	printf("\t h--help munu\n");
	printf("\t s--send message\n");
	printf("\t r--reply to\n");
	printf("\t c--chat history\n");
	printf("\t f--send files\n");
	printf("\t e--exit the system\n");
	printf("----------------help--menu----------------\n");
}
int get_input(char *command)
{	
	printf(">");
	scanf("%c", command);

	return(1);
}
int menu()
{
	/* to avoid the output at mixed with the sub process */
	sleep(1);

	print_menu();
	
	char command;

	while (1 == get_input(&command))
	{	
		switch(command)
		{
			case 'h':
				print_menu();
				break;		
			case 's':
				i_send_msg();
				break;
			case 'r':
				reply();
				break;
			case 'f':
				send_file();
				break;
			case 'c':
				read_chat_history();
				break;
			case 'e':
				exit_sys();
				break;
			default :
				printf(">");
				break;
		}
	}
	return(0);
}
/* end:menu*/
/* start:message contol :send_msg and recv_msg */
int ordnary_msg_recv(struct msg *pmsg)
{
	char time_info[25];
	char end_symble;
	end_symble = '&';

	/* handle the msg */
	printf("Message:from %s(id%d) to U:\n", pmsg->append, pmsg->id_from);
	i_print(pmsg->content, MSG_LEN);
	printf("\n\t%s", i_get_time());

	return(0);
}
int file_msg_recv(struct msg *pmsg)
{
}
int handle_msg(struct msg *pmsg)
{	
	if (pmsg->flag == 1)
	{
		ordnary_msg_recv(pmsg);
		return(0);
	}
	else if (pmsg->flag >= 4)
	{
		file_msg_recv(pmsg);
		return(0);
	}	
	return(0);
}
int listen_msg()
{
	struct msg msg_recv;
	struct sockaddr addr_recv;
	int len = ADDR_LEN;

	printf("begin listen...\n");

	for ( ; ; )
	{	
		i_recvfrom(sockfd, &msg_recv, MSG_LEN, 0, 
			 &addr_recv, &len);

		i_saveto_chat(&msg_recv); /* save to history */
		
		 ordnary_msg_recv(&msg_recv);
	}
}

/* end:message contol*/

/* start:log process :login and regist */
int login()
{
	/* input id:*/
	printf("*****login>>\n");
	printf("id:");
	scanf("%d", &my_id);
	/* input password*/
	char password[15];
	printf("\npassword(*less 15 char):");
	scanf("%s", password);
	getchar();
	
	/* send login information */
	struct msg log_msg;

	bzero(&log_msg, MSG_LEN);
	log_msg.flag = 2;
	log_msg.id_from = my_id;
	log_msg.id_to = 0;
	strncpy(log_msg.content, password, 15);

	i_saveto_chat(&log_msg); /* save to history */
	
	i_sendto(sockfd, (struct msg*)&log_msg, MSG_LEN, 0, 
		(struct sockaddr*)&server, sizeof(struct sockaddr));
//printf("log_msg : %d\n", log_msg.id_from);
//printf("password: %s\n", log_msg.content);
	/* after input msg ,wait for server respond*/
	struct sockaddr in_addr;
	int len = ADDR_LEN;
	i_recvfrom(sockfd, (struct msg*)&log_msg, MSG_LEN,0,
		&in_addr, &len);
	if (2 == log_msg.flag)
	{
		printf("login success\n");
		return(0);
	}	
	else
	{
		printf("login error:%s\n", log_msg.content);
		printf("please relog..\n");
		menu();
	}
	
	return (0);
}
int regist()
{
	printf("*****regist>>\n");
	/* input chat name */
	char name[10];

	bzero(name, 10);
	printf("input your chat name(less 8 char):");
	scanf("%s", name);

	//name[9] = ';';         /* add a ; symbol in the end of name */
	/* input password */
	char password[15];

	bzero(password, 15);
	printf("\ninput your password(less 14 char):");
	scanf("%s", password);

	/* send regist information*/
	struct msg reg_msg;

	bzero(&reg_msg, MSG_LEN);
	reg_msg.flag = 3;
	reg_msg.id_from = 0;
	reg_msg.id_to = 0;
	bzero(reg_msg.content, CNTNT_LEN);
	strncpy(reg_msg.content, name, 10);
	strncpy(&(reg_msg.content[10]), password, 15);
	reg_msg.content[25] = '\n';

	i_saveto_chat(&reg_msg); /* save to history */

	/* send regist informatin to server */
	i_sendto(sockfd, (struct msg*)&reg_msg, sizeof(struct msg), 0, 
		(struct sockaddr*)&server, ADDR_LEN);
	/* after input msg ,wait for server respond*/
	printf("wating for server reply...\n");

	struct sockaddr in_addr;
	struct msg msg_back;
	int len = ADDR_LEN;
	
	bzero(&in_addr, ADDR_LEN);
	bzero(&msg_back, MSG_LEN);
	i_recvfrom(sockfd,(struct msg*)&msg_back, MSG_LEN,0,
		&in_addr, &len);

	/* check whether pass */
	if (3 != msg_back.flag)
	{
		printf("error: %s \n", msg_back.content);
		exit(1);
	}
	else
		my_id = msg_back.id_to;
		printf("congratulate! you have regist" 
			"id %s(id %d) success\n", msg_back.content, msg_back.id_to);

		login();

	return(0);	
}

int my_log()
{
	/* choose login or regist*/
	char flag;
	printf("are you want login or regist(l/r)\n");
	scanf("%c", &flag);
	getchar();
	switch (flag){
		case 'l' :
			login();
			break;
		case 'r' :
			regist();
			break;
		default :
			printf("error input\n");
			my_log();
			break;
	}
	return (0);
}
/* end:log */

int main()
{
	init();
	printf("\n************welcome!************\n");
	my_log();

	pid_t pid;

	switch (pid = fork())
	{
		case -1 :
			perror("fork error!\n");
			exit(1);
			break;
		case 0 :
			listen_msg();
			break;
		default :
			menu();
			break;
	}
}