需求:
如果有人发送消息,其他用户可以收到这个人的群聊信息
如果有人下线,其他用户可以收到这个人的下线信息
服务器可以发送系统信息实现模型
模型:
代码:
//chatser.c -- 服务器端实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <poll.h>#define SER_IP "192.168.2.25"
#define SER_PORT 8888char tm[64];typedef enum msgType{LOGIN,LOGOUT,CHAT,
}msgType;typedef struct usrMsg{msgType type;char usrName[32];char text[1024];
}usrMsg;typedef struct cliNode{char usrName[32];struct sockaddr_in cin;struct cliNode* next;
}cnode_t, *pcnode_t;typedef struct cliHead{unsigned curUsrNum;pcnode_t next;
}chead_t, *pchead_t;const char* getTime();
void createNode(pcnode_t* p, const char* name, const struct sockaddr_in* pcin);
void clinkInit(pchead_t* h);
bool isEmpty(const pchead_t h);
void clinkHeadInsert(pchead_t h, pcnode_t p);
void clinkRemoveByVal(pchead_t h, const struct sockaddr_in* pcin);
void clinkDestory(pchead_t* h);
void printErr(const char* s);int main(int argc, const char *argv[])
{pchead_t h = NULL;clinkInit(&h);int sfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == sfd){printErr("socket");return 1;}printf("[%s]main: Socket created(sfd = %d)\n", getTime(), sfd);struct sockaddr_in sin = {.sin_family = AF_INET,.sin_port = htons(SER_PORT),.sin_addr = { inet_addr(SER_IP) },};if(-1 == bind(sfd, (struct sockaddr*)&sin, sizeof(sin))){printErr("bind");return 1;}printf("[%s]main: Success to bind(IP:%s PORT:%hu)\n", getTime(),SER_IP, SER_PORT);struct pollfd pfd[2] = {{ STDIN_FILENO, POLLIN },{ sfd, POLLIN },};struct sockaddr_in cin;socklen_t cin_len = sizeof(cin);char buf[2048] = {};while(1){int ret = poll(pfd, 2, -1);if(-1 == ret){printErr("poll");return 1;}else if(0 == ret){fprintf(stderr, "[%s]poll: Time out\n", getTime());return 1;}if(POLLIN == pfd[0].revents){char input[512] = {};bzero(buf, sizeof(buf));sprintf(buf, "[%s]System:", getTime());fgets(input, sizeof(input), stdin);if(!strcmp(input, "quit\n")){printf("[%s]System: Start to shutdown...\n", getTime());clinkDestory(&h); break;}if(isEmpty(h)){printf("[%s]System: No user online now\n", getTime());continue;}else{strcat(buf, input);pcnode_t tmp = h->next;while(tmp){sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&tmp->cin, sizeof(tmp->cin));tmp = tmp->next;}printf("[%s]System: send msg: %s", getTime(), input);}}if(POLLIN == pfd[1].revents){bzero(buf, sizeof(buf));struct usrMsg msg = {};recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&cin, &cin_len);switch(msg.type){case LOGIN:pcnode_t p = NULL;createNode(&p, msg.usrName, &cin);printf("[%s] %s:%hu has connected\n", getTime(), inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));if(!isEmpty(h)){bzero(buf, sizeof(buf));sprintf(buf, "[%s] %s is online now!\n", getTime(), msg.usrName);pcnode_t tmp = h->next;while(tmp){sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&tmp->cin, sizeof(tmp->cin));tmp = tmp->next;}}clinkHeadInsert(h, p);break;case LOGOUT:printf("[%s] %s:%hu has disconnected\n", getTime(), inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));if(!isEmpty(h)){bzero(buf, sizeof(buf));sprintf(buf, "[%s] %s is offline\n", getTime(), msg.usrName);pcnode_t tmp = h->next;while(tmp){sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&tmp->cin, sizeof(tmp->cin));tmp = tmp->next;}}clinkRemoveByVal(h, &cin);break;case CHAT:printf("[%s] %s:%hu is chatting\n", getTime(), inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));sprintf(buf, "[%s] %s:\n%s\n", getTime(), msg.usrName, msg.text);pcnode_t tmp = h->next;while(tmp){const char* ip_1 = inet_ntoa(tmp->cin.sin_addr);const char* ip_2 = inet_ntoa(cin.sin_addr);if(!strcmp(ip_1, ip_2) && tmp->cin.sin_port == cin.sin_port){tmp = tmp->next;continue;}sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&tmp->cin, sizeof(tmp->cin));tmp = tmp->next;}break;default:fprintf(stderr, "[%s]System: Unknown msg type\n", getTime());break;}}}close(sfd);return 0;
}void printErr(const char* s){fprintf(stderr, "[%s]", getTime());perror(s);
}const char* getTime(){bzero(tm, sizeof(tm));time_t sec;time(&sec);struct tm* loc_tm = localtime(&sec);const char* fmt = "%Y-%m-%d %H:%M:%S";strftime(tm, sizeof(tm), fmt, loc_tm);return tm;
}void createNode(pcnode_t* p, const char* name, const struct sockaddr_in* pcin){if(NULL == p || NULL == name || NULL == pcin){fprintf(stderr, "[%s]createNode: Invalid argument\n", getTime());return;}*p = (pcnode_t)malloc(sizeof(cnode_t));if(NULL == *p){fprintf(stderr, "[%s]createNode: Fail to malloc\n", getTime());return;}else{strcpy((*p)->usrName, name);(*p)->cin = *pcin;(*p)->next = NULL;return;}
};void clinkInit(pchead_t* h){if(NULL == h){fprintf(stderr, "[%s]clinkInit: Invalid argument\n", getTime());return;}*h = (pchead_t)malloc(sizeof(chead_t));if(NULL == *h){fprintf(stderr, "[%s]clinkInit: Fail to malloc\n", getTime());return;}else{(*h)->curUsrNum = 0;(*h)->next = NULL;return;}
}bool isEmpty(const pchead_t h){if(NULL == h){fprintf(stderr, "[%s]isEmpty: Invalid argument\n", getTime());return false;}if(0 == h->curUsrNum)return true;elsereturn false;
}void clinkHeadInsert(pchead_t h, pcnode_t p){if(NULL == h || NULL == p){fprintf(stderr, "[%s]clinkHeadInsert: Invalid argument\n", getTime());return;}else{p->next = h->next;h->next = p;h->curUsrNum++;return;}
}void clinkRemoveByVal(pchead_t h, const struct sockaddr_in* pcin){if(NULL == h || NULL == pcin){fprintf(stderr, "[%s]clinkRemoveByVal: Invalid argument\n", getTime());return;}else if(isEmpty(h))return;else{void* i = h;pcnode_t j = h->next;while(j){const char* ip_1 = inet_ntoa(j->cin.sin_addr);const char* ip_2 = inet_ntoa(pcin->sin_addr);if(!strcmp(ip_1, ip_2) && j->cin.sin_port == pcin->sin_port){pcnode_t tmp = NULL;if(i == h)((pchead_t)i)->next = j->next;else((pcnode_t)i)->next = j->next;tmp = j;j = tmp->next;printf("[%s]clinkRemoveByVal: Cleaning the node[%s:%hu]...\n", getTime(),inet_ntoa(tmp->cin.sin_addr), ntohs(tmp->cin.sin_port));free(tmp);h->curUsrNum--;}else{i = j;j = j ->next;}}}
}void clinkDestory(pchead_t* h){if(NULL == h || NULL == *h){fprintf(stderr, "[%s]clinkDestory: Invalid argument\n", getTime());return;}else{pcnode_t tmp = (*h)->next;while(tmp){printf("[%s]clinkDestory: Cleaning the node[%s:%hu]...\n", getTime(),inet_ntoa(tmp->cin.sin_addr), ntohs(tmp->cin.sin_port));(*h)->next = tmp->next;free(tmp);tmp = (*h)->next;}free(*h);printf("[%s]clinkDestory: Cleaning the head...\n", getTime());*h = NULL;return;}
}
//charcli.c -- 客户端实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <poll.h>
#include <sys/socket.h>#define SER_IP "192.168.2.25"
#define SER_PORT 8888
#define CLI_IP "192.168.2.82"char tm[64];typedef enum msgType{LOGIN,LOGOUT,CHAT,
}msgType;typedef struct usrMsg{msgType type;char usrName[32];char text[1024];
}usrMsg;const char* getTime();
void printErr(const char* s);int main(int argc, const char *argv[])
{char usrName[32] = {};puts("Please enter your name: ");fgets(usrName, sizeof(usrName), stdin);usrName[strlen(usrName) - 1] = '\0';printf("Welcome, %s!\n", usrName);int cfd = socket(AF_INET, SOCK_DGRAM, 0);if(-1 == cfd){printErr("socket");return 1;}printf("[%s]main: Socket created(cfd = %d)\n", getTime(), cfd);struct sockaddr_in sin = {.sin_family = AF_INET,.sin_port = htons(SER_PORT),.sin_addr = { inet_addr(SER_IP) },};struct pollfd pfd[2] = {{STDIN_FILENO, POLLIN},{cfd, POLLIN},};struct usrMsg msg = {};msg.type = LOGIN;strcpy(msg.usrName, usrName);sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin));printf("[%s]main: Start to login...\n", getTime());while(1){int ret = poll(pfd, 2, -1);if(-1 == ret){printErr("poll");return 1;}else if(0 == ret){fprintf(stderr, "[%s]poll: Time out\n", getTime());return 1;}if(POLLIN == pfd[0].revents){bzero(msg.text, sizeof(msg.text));fgets(msg.text, sizeof(msg.text), stdin);msg.text[strlen(msg.text) - 1] = '\0';if(!strcmp(msg.text, "quit")){msg.type = LOGOUT;sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin));break;}msg.type = CHAT;sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin));}if(POLLIN == pfd[1].revents){char buf[2048] = {};recv(cfd, buf, sizeof(buf), 0);printf("%s", buf);}}close(cfd);return 0;
}void printErr(const char* s){fprintf(stderr, "[%s]", getTime());perror(s);
}
const char* getTime(){bzero(tm, sizeof(tm));time_t sec;time(&sec);struct tm* loc_tm = localtime(&sec);const char* fmt = "%Y-%m-%d %H:%M:%S";strftime(tm, sizeof(tm), fmt, loc_tm);return tm;
}
效果: