Linux下网络聊天室

今天有空升级了一下前面网络聊天室的功能,加了私聊、账号注册登录等操作、由于感觉存储聊天记录文件整理麻烦没有很大必须,需要还没做查看聊天记录的功能,所以就没加文件保存聊天记录这一功能,后面有需要再加,刚入门Linux有需要的小伙伴代码自取

服务器端代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <fcntl.h>
#include <time.h>
#include <ctype.h>
#include <memory.h>
#define PORT 8888
#define IP "127.0.0.1"

#define SIZE sizeof(X)   //定义一个结构体大小空间
typedef struct xinxi
{
	char id[20];          //每个账号唯一id
	char passwd[20];	 //账号密码
	char name[50];	 //账号昵称
	int  fd;              //存放客户端成功连接后accept产生的新的套接字
	struct xinxi* next;	 //下一条链表的首地址	
}X;

int count = 0;            //保存帐号个数
X* head = NULL;
//保存账户信息
void Save_account()
{
	FILE* fp;
	fp = fopen("123", "w");    //123用于保存所有帐号信息
	if (fp == NULL)
	{
		printf("open error\n");
		return;
	}
	fwrite(&count, sizeof(int), 1, fp);		//先保存账号个数	
	X* p;
	p = head;
	if (p->next == NULL) 		//如果账号列表为空,关闭文件并退出函数
	{
		fclose(fp);
		return;
	}
	p = p->next;
	while (p)                //按结构体大小保存账号信息
	{
		fwrite(p, sizeof(X), 1, fp);
		p = p->next;
	}
	printf("账号信息保存成功\n");
	fclose(fp);
}

//创建账号信息的头指针,并从文件里读取保存已注册的账号信息
X* create()
{
	X* x;
	x = (X*)malloc(SIZE);                    //定义一个结构体大小的空间
	if (x == NULL)
	{
		printf("create error\n");
		return NULL;                   //如果没有创建成功指针返回NULL
	}
	x->next = NULL;
	FILE* fp;
	fp = fopen("123", "a+");	     //a+方式打开,读取文件,以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的                                                               数据会被加到文件尾后,即文件原先的内容会被保留。
	if (fp == NULL)
	{
		printf("open error\n");
		return NULL;
	}
	if (fgetc(fp) == EOF) 	   //如果文件为空,返回头指针
	{
		fclose(fp);
		return x;
	}
	rewind(fp);                //文件指针重返文件头
	int n;
	fread(&n, sizeof(int), 1, fp);		//先读取长度
	printf("n=%d\n", n);
	count = n;                   //全局变量获取账号长度
	X t;
	X* p, * p1;
	int i;
	for (i = 0; i < n; i++)				//按账号结构体长度读取信息
	{
		fread(&t, sizeof(X), 1, fp);
		p1 = x;
		while (p1->next)
		{
			p1 = p1->next;
		}
		p = (X*)malloc(sizeof(X));         //每次创建一个新的账号结构体大小空间来存信息
		p->next = NULL;
		strcpy(p->id, t.id);				//账号,昵称,密码读取保存
		strcpy(p->name, t.name);
		strcpy(p->passwd, t.passwd);
		p->fd = -5;                        //每次服务器新连接字置-5,不在线
		p1->next = p;
	}
	fclose(fp);
	return x;
}

//注册功能函数
void Sign_in(int fd)
{
	X* p1, * p;
	int flag = 0;                             //标识符,账号是否能正确注册
	p = (X*)malloc(SIZE);			//开辟一个账号信息结构体大小的空间
	if (p == NULL)
		return;
	char str[256];
	char str1[256];
	memset(str, 0, 256);
	memset(str1, 0, 256);
	strcpy(str, "请输入你想注册的账号");
	send(fd, str, strlen(str), 0);
	memset(str, 0, 256);
	recv(fd, str, 256, 0);
	strcpy(str1, str);
	p1 = head;
	while (p1->next)
	{
		if (strcmp(p1->next->id, str) == 0)
		{
			flag = 1;
			break;
		}
		p1 = p1->next;
	}
	if (flag == 1)
	{
		memset(str, 0, 256);
		strcpy(str, "账号重复\n");
		send(fd, str, strlen(str), 0);
		return;
	}
	//注册账号信息并赋初值
	strcpy(p->id, str);
	memset(str, 0, 256);
	strcpy(str, "请输入密码");
	send(fd, str, strlen(str), 0);
	memset(str, 0, 256);
	recv(fd, str, 256, 0);
	strcpy(p->passwd, str);
	memset(str, 0, 256);
	strcpy(str, "请输入昵称");
	send(fd, str, strlen(str), 0);
	memset(str, 0, 256);
	recv(fd, str, 256, 0);
	strcpy(p->name, str);
	p1 = head;
	while (p1->next)
	{
		p1 = p1->next;
	}
	p1->next = p;
	p->fd = -5;
	p->next = NULL;
	memset(str, 0, 256);
	strcpy(str, "注册成功\n");
	send(fd, str, strlen(str), 0);
	count++;                          //全局变量账号数量+1
	Save_account();		          //保存一次账号信息在文件
}

char a[20][20];	//放聊天室人的id
int len = 0;		//聊天室人数
//创建多人聊天室
void Multiplayer_chat(int fd, char id[20])
{
	char fa[1024];
	char shou[1024];
	memset(fa, 0, 1024);
	memset(shou, 0, 1024);
	strcpy(fa, "您已进入聊天室,输入stop退出,输入look查看当前人");
	send(fd, fa, strlen(fa), 0);
	strcpy(a[len], id);
	len++;					//每进入一个人,长度加1
	int i;
	X* p;
	time_t timep;
	time(&timep);  			//时间函数
	while (1)				//建立聊天室基本信息
	{
		memset(shou, 0, 1024);
		recv(fd, shou, 1024, 0);
		if (strcmp(shou, "stop") == 0)		//stop退出聊天室
		{
			for (i = 0; i < len; i++)
			{
				if (strcmp(a[i], id) == 0)
				{
					while (i < len - 1)
					{
						strcpy(a[i], a[i + 1]);
						i++;
					}
				}
			}
			len--;
			for (i = 0; i < len; i++)
			{
				p = head->next;
				while (p)
				{
					if (strcmp(p->id, a[i]) == 0)
					{
						memset(fa, 0, 1024);
						sprintf(fa, "%s退出聊天室", id);
						send(p->fd, fa, strlen(fa), 0);
						break;
					}
					p = p->next;
				}
			}
			return;
		}
		if (strcmp(shou, "look") == 0)          //look查看聊天室有多少人,并显示出他们的昵称以及账号
		{
			memset(fa, 0, 1024);
			sprintf(fa, "当前聊天室有%d人,他们是:", len);
			send(fd, fa, strlen(fa), 0);
			for (i = 0; i < len; i++)
			{
				p = head->next;
				while (p)
				{
					if (strcmp(p->id, a[i]) == 0)
					{
						memset(fa, 0, 1024);
						sprintf(fa, "昵称是%s  账号是%s\n", p->name, p->id);
						send(fd, fa, strlen(fa), 0);
						break;
					}
					p = p->next;
				}
			}
			continue;                         //look之后继续执行程序
		}
		for (i = 0; i < len; i++)        //发的消息对聊天室里所有人发出
		{
			p = head->next;
			while (p)
			{
				if (strcmp(p->id, a[i]) == 0 && strcmp(p->id, id) != 0)
				{
					memset(fa, 0, 1024);
					sprintf(fa, "%s%s say: %s", ctime(&timep), id, shou);
					send(p->fd, fa, strlen(fa), 0);
					break;
				}
				p = p->next;
			}
		}
	}
}

//私聊函数 
void Direct_Message(int fd, char id[20])
{
	time_t timep;
	time(&timep);  			//时间函数
	X* p;
	p = head->next;
	while (p)
	{
		if (strcmp(p->id, id) == 0)
			break;
		p = p->next;
	}
	char shou[1024];
	char fa[1024];
	memset(shou, 0, 1024);
	memset(fa, 0, 1024);
	strcpy(fa, "输入你想聊天的好友id: 输入over退出私聊");
	send(fd, fa, strlen(fa), 0);
	recv(fd, shou, 1024, 0);
	X* p1;
	p1 = head->next;
	while (p1)
	{
		if (strcmp(p1->id, shou) == 0)
			break;
		p1 = p1->next;
	}
	if (p1 == NULL)
	{
		memset(fa, 0, 1024);
		strcpy(fa, "此好友不存在");
		send(fd, fa, strlen(fa), 0);
		return;
	}
	while (1)					//进入聊天,输入over退出
	{
		memset(shou, 0, 1024);
		recv(fd, shou, 1024, 0);
		if (strcmp(shou, "over") == 0)
		{
			break;
		}
		memset(fa, 0, 1024);
		sprintf(fa, "%s%s say:%s", ctime(&timep), id, shou);
		send(p1->fd, fa, 1024, 0);
	}
}



int Verify_account(char id[20])

{
	X* p;
	int leap = 0;
	if (head->next == NULL)
	{
		return 0;
	}
	p = head->next;
	while (p)
	{
		if (strcmp(id, p->id) == 0)
		{
			return 1;
		}
		p = p->next;
	}
	return 0;
}

//验证密码和帐号是否匹配
int Authentication_password(char id[20], char passwd[20])
{
	X* p;
	int leap = 0;
	if (head->next == NULL)
	{
		return 0;
	}
	p = head->next;
	while (p)
	{
		if (strcmp(id, p->id) == 0 && strcmp(passwd, p->passwd) == 0)
		{
			return 1;
		}
		p = p->next;
	}
	return 0;
}


//服务器信息处理线程 

void* handleclient(void* newfd)
{
	int fd = *(int*)newfd;
	char recvbuf[1024];
	char recvbuf1[1024];
	char sendbuf[1024];
	memset(recvbuf, 0, 1024);
	memset(recvbuf1, 0, 1024);
	memset(sendbuf, 0, 1024);
	if (recv(fd, recvbuf, 1024, 0) == -1)
	{
		perror("recv");						//错误提示
		exit(1);
	}
	else
	{
		printf("cli say:%s\n", recvbuf);
	}
p:
	while (1)
	{
		memset(sendbuf, 0, 1024);
		//登录后界面
		strcpy(sendbuf, "请选择进行的操作:\n\t1.登录帐号\n\t2.注册帐号\n\tend.退出\n");
		send(fd, sendbuf, strlen(sendbuf), 0);
		memset(recvbuf, 0, sizeof(recvbuf));
		recv(fd, recvbuf, 1024, 0);
		if (strcmp(recvbuf, "1") == 0)     //接收到客户端输入为1,请输入登录账号
		{
			memset(sendbuf, 0, 1024);
			strcpy(sendbuf, "请输入登录账号");
			send(fd, sendbuf, strlen(sendbuf), 0);
			memset(recvbuf, 0, sizeof(recvbuf));
			recv(fd, recvbuf, 1024, 0);
			memset(sendbuf, 0, sizeof(sendbuf));
			strcpy(sendbuf, "请输入登录密码");
			send(fd, sendbuf, strlen(sendbuf), 0);
			memset(recvbuf1, 0, sizeof(recvbuf1));
			recv(fd, recvbuf1, 1024, 0);            //recvbuf存入帐号,recvbuf1存入密码,调验证函数验证帐号和密码是否正确 
			//匹配账号密码是否都存在且正确   
			if (Verify_account(recvbuf) == 0 || Authentication_password(recvbuf, recvbuf1) == 0)
			{
				memset(sendbuf, 0, 1024);
				strcpy(sendbuf, "输入账号或密码不正确");
				send(fd, sendbuf, strlen(sendbuf), 0);
			}

			else
			{
				strcpy(recvbuf1, recvbuf);   //把id存入recvbuf1,recvbuf用于接受客户端发来的信息
				memset(sendbuf, 0, 1024);
				strcpy(sendbuf, "登陆成功\n");
				X* p;
				p = head;
				while (p->next)
				{
					if (strcmp(p->next->id, recvbuf1) == 0)//登录成功后去查找获取其对应的套接字,便于通信
					{
						p->next->fd = fd;
						break;
					}
					p = p->next;
				}

				send(fd, sendbuf, strlen(sendbuf), 0);
				//登录之后的操作 
				while (1)
				{
					memset(sendbuf, 0, 1024);
					strcpy(sendbuf, "请选择你要进行的操作:\n1.群聊\n2.私聊\n3.退出\n");
					send(fd, sendbuf, strlen(sendbuf), 0);
					memset(recvbuf, 0, sizeof(recvbuf));
					if (recv(fd, recvbuf, 1024, 0) == -1)
					{
						perror("recv");
						exit(1);
					}
					if (strcmp(recvbuf, "1") == 0)//进入多人聊天室
					{
						Multiplayer_chat(fd, recvbuf1);
					}
					else if (strcmp(recvbuf, "2") == 0) //双人聊天
					{
						Direct_Message(fd, recvbuf1);
					}
					else if (strcmp(recvbuf, "3") == 0)//退出,返回到登录界面
					{
						goto p;
					}
				}

			}

		}
		else if (strcmp(recvbuf, "2") == 0)
		{
			Sign_in(fd);
			goto  p;
		}
		else if (strcmp(recvbuf, "end") == 0)
		{
			memset(sendbuf, 0, 1024);
			strcpy(sendbuf, "欢迎使用,再见");
			send(fd, sendbuf, strlen(sendbuf), 0);
			break;
		}
		else
		{
			continue;             //如果选择不存在的功能键,直接continue继续
		}
	}
	close(fd);
}


int main()
{
	int sockfd = 0, newfd = 0;    //定义并初始化
	int len = sizeof(struct sockaddr_in);
	struct sockaddr_in myaddr;
	struct sockaddr_in otheraddr;
	memset(&myaddr, 0, len);
	memset(&otheraddr, 0, len);
	//实现端口复用,必须在绑定之前,而且只要绑定到同一个端口的所有套接字都得设置复用
	int reuse = 1;
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		perror("setsockopet error\n");
		return -1;
	}
	//初始化结构体
	myaddr.sin_family = AF_INET;
	myaddr.sin_port = htons(PORT);
	myaddr.sin_addr.s_addr = inet_addr(IP);
	//绑定套接字
	if ((bind(sockfd, (struct sockaddr*)&myaddr, len)) == -1)
	{
		perror("bind");
		return -1;
	}
	else
	{
		printf("bind success...\n");
	}
	//开始监听
	if ((listen(sockfd, 10)) == -1)
	{
		perror("listen");
		return -1;
	}
	else
	{
		printf("listen success...\n");
	}
	pthread_t id1;
	head = create();         //创建账号信息的头结点
	while (1)
	{
		if ((newfd = accept(sockfd, (struct sockaddr*)&otheraddr, &len)) == -1)    //连接客户端 
		{
			perror("accept");
			return -1;
		}
		else		//连接成功输出客户端等信息
		{
			printf("accept success...client's fd=%d,sever's fd=%d\n", newfd, sockfd);   //打印出客户端和服务器的fd
			printf("--client ip=%s,port=%d\n", inet_ntoa(otheraddr.sin_addr), ntohs(otheraddr.sin_port)); //打印出客户端的ip地址和端口号
		}
		//线程客户端
		pthread_create(&id1, NULL, handleclient, &newfd);
	}
	close(sockfd);
	return 0;
}


客户端代码

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#define PORT 8888
#define IP "127.0.0.1"

//shou线程接收服务器消息
void* recv_info(void* newfd)
{
    int fd = *(int*)newfd;
    int ret = 0;
    char shou[1024];
    while (1)
    {
        memset(shou, 0, 1024);
        if ((ret = recv(fd, shou, 1024, 0)) == -1)
        {
            exit(1);
        }
        puts(shou);
    }
}

//fa线程用来向服务器发送信息
void* send_info(void* newfd)
{
    int fd = *(int*)newfd;
    char fa[1024];
    while (1)
    {
        memset(fa, 0, 1024);
        scanf("%s", fa);
        if (send(fd, fa, strlen(fa), 0) == -1)
            exit(1);
        if (strcmp(fa, "end") == 0)//发送end结束客户端程序
        {
            printf("欢迎使用再见\n");
            close(fd);//关闭套接字
            exit(0);
        }
    }
}


int main()
{
    int sockfd = 0;//定义并初始化
    int len = sizeof(struct sockaddr);
    struct sockaddr_in otheraddr;
    memset(&otheraddr, 0, len);
    //创建连接
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("sockfd");
        return -1;
    }
    else
    {
        printf("socket success...%d\n", sockfd);
    }
    otheraddr.sin_family = AF_INET;
    otheraddr.sin_port = htons(PORT);
    otheraddr.sin_addr.s_addr = inet_addr(IP);
    //连接服务器
    if (connect(sockfd, (struct sockaddr*)&otheraddr, len) == -1)
    {
        perror("connect");
        return -1;
    }
    else
    {
        printf("connect success...client's fd=%d\n", sockfd);
        printf("--client ip=%s,port=%d\n", inet_ntoa(otheraddr.sin_addr), ntohs(otheraddr.sin_port));
    }
    //创建线程
    pthread_t id1, id2;
    char recvbuf[1024];
    char sendbuf[1024];
    memset(recvbuf, 0, sizeof(recvbuf));
    memset(sendbuf, 0, 1024);
    //给服务器发送信息,握手判断是否建立连接		
    strcpy(sendbuf, "我是客户端 \n");
    if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1)
    {
        perror("send");
        return -1;
    }
    if (recv(sockfd, recvbuf, 1024, 0) == -1)
    {
        perror("recv");
        return -1;
    }
    else
    {
        printf("sever say:%s\n", recvbuf);
    }
    //启动客户端线程的收发功能
    pthread_create(&id2, NULL, send_info, &sockfd);
    pthread_create(&id1, NULL, recv_info, &sockfd);
    //等待发送线程结束,退出客户端
    pthread_join(id2, NULL);
    return 0;
}



  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值