5.26 基于UDP的网络聊天室

需求:

如果有人发送消息,其他用户可以收到这个人的群聊信息

如果有人下线,其他用户可以收到这个人的下线信息

服务器可以发送系统信息实现模型

模型:

代码:

//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;
}

效果:

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值