今天有空升级了一下前面网络聊天室的功能,加了私聊、账号注册登录等操作、由于感觉存储聊天记录文件整理麻烦没有很大必须,需要还没做查看聊天记录的功能,所以就没加文件保存聊天记录这一功能,后面有需要再加,刚入门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;
}