经过一个学期的学习,C语言的学习已经告一段落,经过几天的埋头苦干,我的聊天室总算出具雏形,实现了老师要求的功能(私聊、群聊、管理员、禁言),但是在功能切换方面还是有些不太流畅,服务器有时会出现死循环等错误,还有待改善。
在写代码的几天中,我遇到了以下几个问题:
1.无从下手,由于是在一学期抽空学习C语言,所以我有些知识已经有些遗忘,在编写代码的第一天,我就陷入了窘境,但我还是硬着头皮在老师写了注册的基础上继续往下写,而没有好好理解一下老师的代码在写,而我周围就有一些同学是先将老师的代码重新写了一边并理解了的基础上写的,虽然一开始进度比我慢,但是越往后越难写的时候优势就体现出来了,我陷入瓶颈花了一天在解决无法显示在线好友这个功能,后来在辅导及老师的帮助下才完成。
2.对网络编程了解的不够透彻,我选用的是TCP / Select来实现聊天室的,主要就是server 方负责recv(接收) client发出的请求send,继而在后台调用程序来实现该功能,然后再将结果通过send传回客户端,但是我在用户登录时就建立线程负责接收从服务器传回的消息,但是相同的服务器,相同的客户端,使得服务器发回的所有结果均被线程接收而无法回到功能模块,后来将接受回来的结果在线程中进行判断解决了这个问题。
chat.h
#ifndef CHAT_H
#define CAHT_H
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sqlite3.h>
#include <pthread.h>
#define PORT 8000
#define IP "127.0.0.1"
#define REGISTER 1 //注册
#define LOGIN 2 //登录
#define ONLINENUM 3 //查看在线人数
#define PRIVATE 4 //私聊
#define GROUP 5 //群聊
#define ROOT 6 //注册会员
#define EXIT 7 //退出
#define FORBIDDEN 8 //禁言
#define RES_SUCCESS 100000 //执行成功
#define RES_USER_EXIST 100001 //用户存在(用于注册功能)
#define RES_USER_NOT_EXIST 100002 //用户不存在(用于登录功能)
#define RES_PASSWD_ERROR 100003 //密码错误(用于登录功能)
#define RES_FAILURE 100004
#define RES_FLAG 100005
#define RES_JUDGE 100006
#define SIZE 100
pthread_t tid[2];
struct Info
{
int command;
char id[32]; //昵称
char password[32]; //密码
char text[128]; //聊天内容
int tofd; //对方的fd
char toid[32]; //对方昵称
int result; //执行的结果
};
typedef struct Info info;
#endif
客户端
show.c
void welcome()
{
system("clear");
printf("\n\n\n\n\n\n\n\t\t\t\t\t\t************************************************\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* welcome to *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* ********* *\n");
printf("\t\t\t\t\t\t* * *\n");
printf("\t\t\t\t\t\t* * *\n");
printf("\t\t\t\t\t\t* * *\n");
printf("\t\t\t\t\t\t* * *\n");
printf("\t\t\t\t\t\t* * *\n");
printf("\t\t\t\t\t\t* * *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* 1 注册 2 登录 *\n");
printf("\t\t\t\t\t\t* 0 退出 *\n");
printf("\t\t\t\t\t\t************************************************\n");
//sleep(1);
}
void menu(char *name)
{
system("clear");
printf("\n\n\n\t\t\t\t\t\t***************************************\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t 昵称:%s \n",name);
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* 1.查看在线好友 *\n");
printf("\t\t\t\t\t\t* 2.私聊 *\n");
printf("\t\t\t\t\t\t* 3.群聊 *\n");
printf("\t\t\t\t\t\t* 4.申请管理员 *\n");
printf("\t\t\t\t\t\t* 5.禁言(管理员) *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* 0.退出 *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t* *\n");
printf("\t\t\t\t\t\t***************************************\n");
}
SelectClient.c
#include *-*"../chat.h"
int sockfd; //服务器fd
/*
作者:周甜
日期
功能描述:接收线程
形参:空
返回值:无
*/
void *(receive)(void *arg)
{
int ret;
while(1)
{
info i;
ret = recv(sockfd, &i, sizeof(info), 0);
if(-1 == ret)
{
perror("recv");
}
if(i.command == 3)
{
if (strcmp(i.toid,"over"))
{
printf("%s\n", i.toid);
}
else if(!strcmp(i.toid,"over"))
{
break;
}
else
{
printf("获取在线好友失败!\n");
}
}
else if (i.command == 4)
{
if(RES_FAILURE == i.result)
{
printf("您已被禁言!\n");
break;
}
else if(RES_FLAG == i.result)
{
printf("该好友已被禁言!\n");
break;
}
else if(RES_SUCCESS == i.result)
{
if(strcmp(i.text, "over"))
{
printf("收到%s发来的消息:\n%s\n", i.id, i.text);
}
else
{
break;
}
}
else if(RES_JUDGE == i.result)
{
printf("该好友已下线!\n");
break;
}
else
{
printf("私聊失败!\n");
break;
}
}
else if(i.command == 5)
{
if(RES_FAILURE == i.result)
{
printf("您已被禁言!\n");
break;
}
else if(RES_FLAG == i.result)
{
printf("该好友已被禁言!\n");
break;
}
else if(RES_SUCCESS == i.result)
{
if(strcmp(i.text, "over"))
{
printf("收到%s发来的消息:\n%s\n", i.id, i.text);
}
else
{
break;
}
}
else
{
printf("群聊失败!\n");
break;
}
}
else if(i.command == 6)
{
if(RES_SUCCESS == i.result)
{
printf("恭喜您已成功成为管理员!\n");
printf("现在可以禁言好友!\n");
}
else if(RES_FAILURE == i.result)
{
printf("您已注册过会员!\n");
}
else
{
printf("注册会员失败!\n");
}
sleep(1);
}
else if(i.command == 8)
{
if(RES_SUCCESS == i.result)
{
printf("您已成功禁言好友%s !\n", i.toid);
}
else if(RES_FAILURE == i.result)
{
printf("该好友已被禁言!\n");
}
else
{
printf("禁言好友%s失败!\n", i.toid);
}
sleep(1);
}
else if(i.command == 7)
{
if(RES_SUCCESS == i.result)
{
printf("您已成功退出!\n");
close(sockfd);
exit(1);
}
else
{
printf("退出聊天室失败!\n");
}
}
memset(&i,0,sizeof(info));
}
}
int main()
{
int flag = 1, root, forbidden;
char choose, choise;
char id[32] = {0};
char password[32] = {0};
char toid[32] = {0};
char text[128] = {0};
struct sockaddr_in server_addr;
char buf[32] = {0};
info i, in;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
perror("socket");
exit(1);
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = PF_INET;
server_addr.sin_port = 8000; //跟服务器保持一致
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //服务器地址
int ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (-1 == ret)
{
perror("connect");
exit(1);
}
while(flag) //注册、登录
{
welcome();
sleep(1);
printf("请输入您的选择:\n");
scanf("%c", &choose);
if('1' == choose)
{
i.command = REGISTER;
printf("请输入您的昵称:\n");
scanf("%s", id);
printf("请输入您的密码:\n");
scanf("%s", password);
strcpy(i.id, id);
strcpy(i.password, password);
ret = send(sockfd, &i, sizeof(i), 0);
if (-1 == ret)
{
perror("send");
}
ret = recv(sockfd, &i, sizeof(i), 0);
if (i.result == RES_SUCCESS)
{
printf("注册成功!\n");
flag = 0;
}
else if (RES_USER_EXIST == i.result)
{
printf("注册失败,帐号已被注册!\n");
printf("请重新输入!\n");
sleep(1);
getchar();
}
}
else if('2' == choose)
{
i.command = LOGIN;
printf("请输入您的昵称:\n");
scanf("%s", id);
printf("请输入您的密码:\n");
scanf("%s", password);
strcpy(i.id, id);
strcpy(i.password, password);
ret = send(sockfd, &i, sizeof(i), 0);
if (-1 == ret)
{
perror("send");
}
ret = recv(sockfd, &i, sizeof(i), 0);
if (i.result == RES_SUCCESS)
{
printf("登录成功!\n");
sleep(1);
ret = pthread_create(&tid[0], NULL, receive, NULL);
if(ret != 0)
{
perror("pthread_create");
exit(1);
}
flag = 0;
}
else if (RES_PASSWD_ERROR == i.result)
{
printf("登录失败!\n");
sleep(1);
printf("请重新输入!\n");
sleep(1);
}
else if (RES_USER_NOT_EXIST == i.result)
{
printf("登录失败!\n");
printf("请重新输入!\n");
sleep(1);
}
}
else if('0' == choose)
{
exit(1);
}
else
{
printf("请输入正确指令!\n");
sleep(1);
}
}
printf("welcome to 7\n");
sleep(1);
while(1)
{
menu(i.id); //菜单
printf("请输入您的选择:\n");
scanf("%s", &choise);
switch(choise)
{
case '1':
i.command = ONLINENUM;
ret = send(sockfd, &i, sizeof(info), 0);
if (-1 == ret)
{
perror("send");
}
printf("在线好友:\n");
break;
case '2':
i.command = PRIVATE;
printf("请输入好友昵称:\n");
scanf("%s", toid);
strcpy(i.toid, toid);
while(1)
{
printf("与%s私聊中:\n", i.toid);
printf("请输入您要输入的内容:\n");
scanf("%s", text);
if(!strcmp(text, "over"))
{
break;
}
strcpy(i.text, text);
ret = send(sockfd, &i, sizeof(i), 0);
if(-1 == ret)
{
perror("send");
}
}
break;
case '3':
i.command = GROUP;
printf("欢迎进入群聊!\n");
while(1)
{
printf("请输入您要群发的消息:\n");
scanf("%s", text);
if(!strcmp(text, "over"))
{
break;
}
strcpy(i.text, text);
ret = send(sockfd, &i, sizeof(i), 0);
if(-1 == ret)
{
perror("send");
}
}
break;
case '4':
i.command = ROOT;
ret = send(sockfd, &i, sizeof(i), 0);
if(-1 == ret)
{
perror("send");
}
break;
case '5':
i.command = FORBIDDEN;
printf("请输入您要禁言的对象:\n");
scanf("%s", toid);
strcpy(i.toid, toid);
ret = send(sockfd, &i, sizeof(i), 0);
if(-1 == ret)
{
perror("send");
}
break;
case '0':
i.command = EXIT;
ret = send(sockfd, &i, sizeof(i), 0);
if(-1 == ret)
{
perror("send");
}
break;
}
}
close(sockfd);
return 0;
}