基于linux下的聊天程序设计与实现 | 您所在的位置:网站首页 › linux聊天室程序代码 › 基于linux下的聊天程序设计与实现 |
[原文链接]
一、 实现的内容及要求: 用C语言编程实现linux简单的聊天室功能。 用户程序命名为client.c;服务器程序命名为server.c 绑定端口等信息见实验方法内容; 要求client可以通过socket连接server 在client,提示输入服务器ip 若连接server 的socket建立成功,返回提示信息 Client输入的聊天内容在client端(多个client端)和server端同时显示; 多个client可同时接入server,进入聊天室,最多支持20个client; Client端输入quit退出连接,server端提示client退出。 可选择使用多线程实现多客户端; 其他细节见输出结果;
二、方法及内容 1. 需要的头文件 #include #include #include #include #include #include #include #include #include #include #include
2. 主要的常量变量 客户端: #define TRUE 1 #define PORT 5000 int quit=0; //quit表示是否用户确定退出
服务器端: #define MAXLINE 1000 //在一条消息中最大的输出字符数 #define LISTENQ 20 //最大监听队列 #define PORT 5000 //监听端口 #define MAXFD 20 //最大的在线用户数量 void *get_client(void *); int i,maxi=-1;//maxi表示当前client数组中最大的用户的i值 int client[MAXFD];
3.主要模块 客户端: intmain(void) void*get_server(void* sockfd) //get_server函数,用于接受服务器转发的消息
服务器端: intmain() void*get_client(void *sockfd) //运行get_client函数,处理用户请求
三.完整代码实现
注:参考socket编程
/******* 客户端程序client.c ************/ #include #include #include #include #include #include #include #include
#define TRUE 1 #define PORT 5000
static int sockfd; void recvfromserver() //接受服务器消息线程入口函数 { charmes[1024]; int nbytes=0;
while(1) { memset(mes,0,sizeof(mes)); nbytes=read(sockfd,mes,sizeof(mes)); if(nbytes>0) { mes[nbytes]='\0'; printf("%s\n",mes); } } pthread_exit(NULL); }
int main(int argc, char *argv[]) { // int sockfd; charbuffer[1024]; structsockaddr_in server_addr; structhostent *host; intportnumber,nbytes; charstrhost[16]; charclientname[20]; charmes[1024]; int thr_id; /* thread ID for the newly createdthread */ pthread_t p_thread; /* thread's structure */ if(argc!=1) { fprintf(stderr,"Usage:%s\a\n",argv[0]); exit(1); } printf("请输入服务器ip地址\n"); scanf("%s",strhost); if((host=gethostbyname(strhost))==NULL) { fprintf(stderr,"Gethostnameerror\n"); exit(1); } /* 客户程序开始建立 sockfd 描述符 */ printf("正在建立套接口...\n"); if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) { fprintf(stderr,"SocketError:%s\a\n",strerror(errno)); exit(1); } /* 客户程序填充服务端的资料 */ bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family=AF_INET; server_addr.sin_port=htons(PORT); server_addr.sin_addr=*((structin_addr *)host->h_addr); printf("套接口创建成功,正在链接服务器...\n"); /* 客户程序发起连接请求 */
if(connect(sockfd,(structsockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) { fprintf(stderr,"ConnectError:%s\a\n",strerror(errno)); exit(1); } /* 连接成功了 */ printf("链接服务器成功\n欢迎来到聊天室\n"); printf("请输入你的用户昵称\n"); scanf("%s",clientname); // write(sockfd,clientname,sizeof(clientname));
printf("\n\n开始聊天吧(\"Quit\"断开连接)\n\n");
thr_id =pthread_create(&p_thread, NULL, recvfromserver, NULL); while(1) { memset(buffer,0,sizeof(buffer)); memset(mes,0,sizeof(mes)); scanf("%s",buffer); strcat(mes,clientname); strcat(mes,":"); strcat(mes,buffer); // printf("main thread %s\n",mes); if((write(sockfd,mes,sizeof(mes)))==-1) { fprintf(stderr,"WriteError:%s\n",strerror(errno)); exit(1); } if(strcmp(buffer,"Quit")==0) { break; } } /* 结束通讯 */ close(sockfd); exit(0); }
/******* 服务器程序(server.c) ************/ #include #include #include #include #include #include #include #include
#define MAXLINE 1000 //在一条消息中最大的输出字符数 #define LISTENQ 20 //最大监听队列 #define PORT 5000 //监听端口 #define MAXFD 20 //最大的在线用户数量 void *get_client(void *); int sockfd,i; static int maxi=0;//maxi表示当前client数组中最大的用户的i值 static int client[MAXFD];
void recvandsend(void) //监听转发线程入口函数 { int index=0; int nbytes=0; charbuffer[1024]; int len; intoutindex=0; while(1) { if(maxi>0) { memset(buffer,0,sizeof(buffer)); nbytes=0; //index++; nbytes=read(client[index++],buffer,sizeof(buffer)); // printf("%d,%d\n",index,client[index]); if(nbytes>0) { buffer[nbytes]='\0'; printf("%s\n",buffer); outindex=0; while(outindex=maxi) index=0; } pthread_exit(NULL); }
int main(int argc, char *argv[]) { // intclient_fd[LISTENQ],clientnum=0;; structsockaddr_in server_addr; structsockaddr_in client_addr; intsin_size,portnumber; charhello[]="Hello! Are You Fine?\n"; int thr_id; /* thread ID for the newly createdthread */ pthread_t p_thread; /* thread's structure */ int new_fd=0; memset(client,0,sizeof(client)); if(argc!=1) { fprintf(stderr,"Usage:%sportnumber\a\n",argv[0]); exit(1); }
/* 服务器端开始建立 socket 描述符 */ if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) { fprintf(stderr,"Socketerror:%s\n\a",strerror(errno)); exit(1); } /* 服务器端填充 sockaddr 结构 */ bzero(&server_addr,sizeof(structsockaddr_in)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=htonl(INADDR_ANY); server_addr.sin_port=htons(PORT); /* 捆绑 sockfd 描述符 */ if(bind(sockfd,(structsockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) { fprintf(stderr,"Binderror:%s\n\a",strerror(errno)); exit(1); } printf("服务器监听端口%d...\n",PORT); /* 监听 sockfd 描述符 */ if(listen(sockfd,LISTENQ)==-1) { fprintf(stderr,"Listenerror:%s\n\a",strerror(errno)); exit(1); } thr_id =pthread_create(&p_thread, NULL, recvandsend, NULL); printf("欢迎来到本聊天室\n"); while(1) { /* 服务器阻塞,直到客户程序建立连接 */ if(maxi>=20) { printf("以达到人数上线\n"); continue; } sin_size=sizeof(structsockaddr_in); if((new_fd=accept(sockfd,(structsockaddr *)(&client_addr),&sin_size))==-1) { fprintf(stderr,"Accepterror:%s\n\a",strerror(errno)); exit(1); } /*fprintf(stderr,"Serverget connection from %s\n",inet_ntoa(client_addr.sin_addr));*/ client[maxi++]=new_fd; printf("\n新用户进入聊天室%d\n",new_fd); } close(sockfd); exit(0); }
四、测试结果显示 1. 执行结果 l 服务器打开
l 客户端打开,并输入了地址,昵称
l 服务器端显示
l 客户端2进入
l 服务器显示
l 张三输入
l 李四端显示
l 服务器显示
l 李四输入
l 张三显示
l 服务器显示
2. 结果分析 这是一个聊天室程序,可以实现群聊的功能,即当某个客户发出消息后,服务器和其他个客户端都能收到此消息。且能够显示客户端的用户名。但客户端退出聊天室后,服务器和其他在线客户端会有提示。 实现群聊的机制是:当某个客户端需要发送消息是,它将此消息发送给服务器,服务器再将此消息转发给各客户端,各客户端之间是无连接的,即相互之间不能直接通信。因此,在服务器中,有两个线程,主线程用来监听是否有客户端登录服务器,若有,建立与其连接的套接字,并存入在线客户序列里,辅助线程是接收转发线程,其依次读取个客户端,看是否有消息送达,若有,取出,并转发给各其他客户端。在客户端也有两个线程,主线程用来向服务器发送消息,辅助线程用来接收服务器发出的消息。 存在的问题是:1.当有用户下线是,虽会在服务器和各客户端提示用户下线,但是并未删除其在服务器中的套接字,致使后来用户不能进入。2.服务器的辅助线程对各客户端采取轮流监听的策略,但是因为使用read() 函数会阻塞线程,致使出现各客户端必须按登陆顺序依次发言的尴尬情况。经过查找,可以使用select()函数跨过阻塞,正在试验中。 |
CopyRight 2018-2019 实验室设备网 版权所有 |