需求:
如果有人发送消息,其他用户可以收到这个人的群聊信息
如果有人下线,其他用户可以收到这个人的下线信息
服务器可以发送系统信息实现模型
模型:
代码:
//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 8888
char 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;
else
return 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;
}
效果: