一:要求
1.通过一个服务器实现最多5个客户之间的信息群发。
2.服务器显示客户的登录与退出;
3.客户连接后首先发送客户名称,之后发送群聊信息;
4.客户输入bye代表退出,在线客户能显示其他客户的登录于退出。
二:提示
1、服务器端:
主线程:
定义一个全局客户信息表ent,每个元素对应一个客户,存储:socket描述符、客户名、客户IP、客户端口、状态(初值为0)。
主线程循环接收客户连接请求,在ent中查询状态为0的元素,
如果不存在状态为0的元素(即连接数超过最大连接数),向客户发送EXIT标志;
否则,修改客户信息表中该元素的socket描述符、客户IP、客户端口号,状态为1(表示socket可用);
同时创建一个通信线程并将客户索引号index传递给通信线程。
通信线程:
首先向客户端发送OK标志
循环接收客户发来信息,若信息长度为0,表示客户端已关闭,向所有在线客户发送该用户退出;
若信息为用户名,修改全局客户信息表ent中index客户的用户名name,并显示该用户登录;
若信息为退出,修改全局客户信息表ent中index客户状态为0,并显示该用户退出,终止线程;
同时查询全局客户信息表ent,向状态为1的客户发送接收的信息。
2、客户端:
根据用户从终端输入的服务器IP地址及端口号连接到相应的服务器;
连接成功后,接收服务端发来的信息,若为EXIT,则达到最大用户量,退出;
若为OK,可以通讯,首先先发送客户名称;
主进程循环从终端输入信息,并将信息发送给服务器;
当发送给服务器为bye后,程序退出。
同时创建一个线程负责接收服务器发来的信息,并显示,当接收的长度小于等于0时终止线程;
三:程序
客户端与服务端的头文件:
ifndef _GCS_H #define _GCS_H #include <netinet/in.h> #define MSGLEN 1024 #define OK 1 #define EXIT 2 #define MSG 3 #define USER 4 struct CLIENTMSG { int op; char user[20]; char buf[MSGLEN]; }; struct CLIENTS { int sockfd; int port; char user[20]; struct sockaddr_in client; int stat; }; struct CLIENTS ent[5]; #endif
服务端:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/ipc.h> #include <pthread.h> #include "group_chat_system.h" extern struct CLIENTS ent[5]; void* func(void *arg); void sever_process(int index); int main(int argc, char **argv) { int sever_fd,client_fd; struct sockaddr_in sever,client; char ip[20]; int port,clientlen; unsigned char i,index; struct CLIENTMSG clientMSG; pthread_t tid; int *arg; for(i=0; i<5; i++) { ent[i].stat = 0; } /***************创建服务器sockfd************/ if((sever_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket error"); exit(errno); } /***************输入ip与端口*****************/ printf("please input ip:"); scanf("%s", ip); printf("\nplease input port:"); scanf("%d", &port); printf("\n"); /******************bind***********************/ bzero(&sever, sizeof(sever)); sever.sin_family = AF_INET; sever.sin_port = htons(port); sever.sin_addr.s_addr= inet_addr(ip); if(bind(sever_fd, (struct sockaddr*)&sever, sizeof(struct sockaddr)) == -1) { perror("bind error"); exit(errno); } /******************listen***********************/ if(listen(sever_fd, 5) == -1) { perror("listen error"); exit(errno); } while(1) { clientlen = sizeof(client); if((client_fd = accept(sever_fd, (struct sockaddr*)&client, &clientlen)) == -1) { perror("accept error"); exit(errno); } printf("accept ok\n"); index = 5; for(i=0; i<5; i++) { if(ent[i].stat ==0){ index = i; break; } } if(index <= 4) { printf("client_fd : %d\n", client_fd); ent[index].sockfd = client_fd; ent[index].port = port; ent[index].client = client; ent[index].stat = 1; arg = malloc(sizeof(int)); *arg = index; pthread_create(&tid, NULL, func, (void*)arg); } else { printf("the client already has 5\n"); bzero(&clientMSG, sizeof(clientMSG)); clientMSG.op = EXIT; send(client_fd, &clientMSG, sizeof(clientMSG), 0); close(client_fd); } } close(sever_fd); return 0; } void *func(void *arg) { int *info; info = (int *)arg; sever_process(*info); pthread_exit(NULL); } void sever_process(int index) { struct CLIENTMSG sendMSG; struct CLIENTMSG recvMSG; int len,i; /*首先发送邋OK标志*/ sendMSG.op = OK; send(ent[index].sockfd, &sendMSG, sizeof(sendMSG), 0); while(1) { bzero(&sendMSG, sizeof(sendMSG)); bzero(&recvMSG, sizeof(recvMSG)); len = recv(ent[index].sockfd, &recvMSG, sizeof(recvMSG), 0); if(len == 0) { sendMSG.op = EXIT; for(i=0; i<5; i++) { if(ent[i].stat == 1) send(ent[i].sockfd, &sendMSG, sizeof(sendMSG), 0); } } if(len > 0) { if(recvMSG.op == USER) { bcopy(recvMSG.user, ent[index].user, strlen(recvMSG.user)); printf("user %s login form ip: %s port: %d\n", ent[index].user, inet_ntoa(ent[index].client.sin_addr), ntohs(ent[index].client.sin_port)); sendMSG.op = USER; } if(recvMSG.op == EXIT) { //printf("recv exit\n"); sendMSG.op = EXIT; bcopy(ent[index].user, sendMSG.user, strlen(recvMSG.user)); for(i=0; i<5; i++) { if(ent[i].stat == 1) send(ent[i].sockfd, &sendMSG, sizeof(sendMSG), 0); } break; } if(recvMSG.op == MSG) { //printf("recv msg\n"); //printf("%s: %s", recvMSG.user,recvMSG.buf); sendMSG.op= MSG; } bcopy(recvMSG.user, sendMSG.user, strlen(recvMSG.user)); bcopy(recvMSG.buf, sendMSG.buf, strlen(recvMSG.buf)); for(i=0; i<5; i++) { //printf("\n%d: %s\n", sendMSG.op, sendMSG.user); if(ent[i].stat == 1) { if(strncmp(ent[i].user, sendMSG.user, strlen(sendMSG.user))) send(ent[i].sockfd, &sendMSG, sizeof(sendMSG), 0); } } } } }
客户端:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/ipc.h> #include <pthread.h> #include "group_chat_system.h" struct client_arg { int sockfd; struct CLIENTMSG clientMSG; }; struct client_arg *arg; void *func(void* arg); void client_process(int sockfd, struct CLIENTMSG clientMSG); int main(int argc, char **argv) { int sockfd; struct sockaddr_in sever; char ip[20]; unsigned int port,len; struct CLIENTMSG sendMSG; pthread_t tid; if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket error"); exit(1); } printf("please input ip:"); scanf("%s", ip); printf("\nplease input port:"); scanf("%d", &port); printf("\n"); bzero(&sever, sizeof(sever)); sever.sin_family = AF_INET; sever.sin_port = htons(port); inet_aton(ip, &sever.sin_addr); if(connect(sockfd, (struct sockaddr*)&sever, sizeof(struct sockaddr)) == -1) { perror("connect error"); exit(errno); } len = recv(sockfd, &sendMSG, sizeof(sendMSG), 0); if(len > 0){ if(sendMSG.op == EXIT) { printf("exceed the numble of users\n"); } if(sendMSG.op == OK) { bzero(&sendMSG, sizeof(sendMSG)); printf("please input client name: "); fgets(sendMSG.user, MSGLEN, stdin); //scanf("%s", sendMSG.user); sendMSG.op = USER; send(sockfd, &sendMSG, sizeof(sendMSG), 0); arg = (struct client_arg*)malloc(sizeof(struct client_arg)); arg->sockfd = sockfd; pthread_create(&tid, NULL, func, (void*)arg); while(1) { // bzero(&sendMSG, sizeof(sendMSG)); sendMSG.op = MSG; //printf("waiting..."); fgets(sendMSG.buf, MSGLEN, stdin); //scanf("%s", sendMSG.buf); //printf("%s: %s %d\n", sendMSG.user,sendMSG.buf, sendMSG.op); if(!strncasecmp(sendMSG.buf, "bye", 3)) { sendMSG.op = EXIT; send(sockfd, &sendMSG, sizeof(sendMSG), 0); break; } send(sockfd, &sendMSG, sizeof(sendMSG), 0); } } pthread_cancel(tid); } close(sockfd); return 0; } void *func(void* arg) { struct client_arg *info; info = (struct client_arg*)arg; client_process(info->sockfd, info->clientMSG); free(arg); pthread_exit(NULL); } void client_process(int sockfd, struct CLIENTMSG clientMSG) { int len; while(1) { bzero(&clientMSG, sizeof(clientMSG)); len = recv(sockfd, &clientMSG, sizeof(clientMSG), 0); if(len > 0) { if(clientMSG.op==USER){ printf("the user %s is login.\n",clientMSG.user); }else if(clientMSG.op == EXIT){ printf("the user %s is logout.\n",clientMSG.user); }else if(clientMSG.op == MSG){ printf("%s: %s\n",clientMSG.user,clientMSG.buf); } } } }
Makefile文件:
main:sever.o client.o gcc sever.o -o sever -lpthread gcc client.o -o client -lpthread sever.o:sever.c gcc -c sever.c group_chat_system.h gcc -c client.c group_chat_system.h clean: rm *.o sever client
四:现象