网络编程(基于UDP的网络聊天室)

服务器:

#include <myhead.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%d__:", __LINE__);\
	perror(msg);\
}while(0)

#define PORT 8888
#define IP "192.168.1.3"

//收发信息的结构体
typedef struct{
	char type;
	char name[20];
	char text[128] ;

}MSG;

//定义保存客户端网络信息结构体的链表类型
typedef struct node{
	struct sockaddr_in cin;
	struct node* next;
}Node,*LinklistPtr;

int do_recv(int sfd, LinklistPtr head);
int do_login(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin);
int do_quit(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin);
int do_chat(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin);
int do_system(int sfd, struct sockaddr_in sin);

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd < 0){
		perror("socket");
		return -1;
	}

	//绑定服务器ip和端口号
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET;
	sin.sin_port 		= htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);

	if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0){
		perror("bind");
		return -1;
	}
	
	//创建进程
	int pid = 0;
	pid = fork();
	if(pid > 0){
		//父进程, 接收消息
		LinklistPtr head = (LinklistPtr)malloc(sizeof(Node));
		head->next = NULL;
		do_recv(sfd , head);
	}
	else if(pid==0){
		//子进程,发送系统消息
		do_system(sfd, sin);
	}

	//关闭套接字
	close(sfd);
	return 0;
}

int do_system(int sfd, struct sockaddr_in sin){
	MSG msg2 = {'C', "--system--"};
	while(1){
		bzero(msg2.text, sizeof(msg2.text));
		fgets(msg2.text, sizeof(msg2.text), stdin);
		msg2.text[strlen(msg2.text)-1] = '\0';

		//将当前进程当做客户端,父进程当做服务器,发送信息;
		if(sendto(sfd, &msg2, sizeof(msg2), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){
			ERR_MSG("sendto");
			return -1;
		}
	}
	return 0;
}

int do_recv(int sfd, LinklistPtr head){
	MSG msg1;
	int recv_len = 0;
	struct sockaddr_in cin;
	socklen_t clen = sizeof(cin);

	while(1){
		//接收消息
		recv_len = recvfrom(sfd, &msg1, sizeof(msg1), 0, (struct sockaddr*)&cin, &clen);
		if(recv_len < 0){
			ERR_MSG("recvfrom");
			return -1;
		}

		switch(msg1.type){
		case 'L':
			//登录函数
			do_login(sfd, head, msg1, cin);
			break;
		case 'C':
			//群聊函数
			do_chat(sfd, head, msg1, cin);
			break;
		case 'Q':
			//退出函数
			do_quit(sfd, head, msg1, cin);
			break;
		}
	}
}

int do_chat(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin){	
	printf("%s [%s:%d]chat成功\n", msg1.name,\
			(char*)inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));

	char buf[200] = "";
	sprintf(buf, "%s:%s", msg1.name, msg1.text);
	strcpy(msg1.text, buf);

	//循环发送,除了自己以外的ip地址
	while(head->next != NULL){
		head = head->next;
		if(memcmp(&cin, &head->cin, sizeof(cin)) != 0){	
			if(sendto(sfd, &msg1, sizeof(msg1),0, (struct sockaddr*)&(head->cin), sizeof(head->cin))<0){
				ERR_MSG("sendto");
				return -1;
			}
		}
	}
	return 0;	
}

int do_quit(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin){
	printf("%s [%s:%d]下线了\n", msg1.name,\
			(char*)inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));

	sprintf(msg1.text, "-----%s已下线-----", msg1.name);

	//遍历链表 是自己就将自己在链表中删除
    //不是自己 就发送 退出群聊的数据
	while(head->next != NULL){
		if(memcmp(&cin, &head->next->cin, sizeof(cin)) == 0){
			//从链表中删除该客户端信息
			LinklistPtr p = head->next;
			head->next = p->next;
			free(p);
			p=NULL;
		}
		else{
			head = head->next;
			if(sendto(sfd, &msg1, sizeof(msg1),0, (struct sockaddr*)&(head->cin), sizeof(head->cin))<0){
				ERR_MSG("sendto");
				return -1;
			}
		}
	}
	return 0;
}


int do_login(int sfd, LinklistPtr head, MSG msg1, struct sockaddr_in cin){
	printf("%s [%s:%d]登录成功\n", msg1.name,\
			(char*)inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));

	sprintf(msg1.text, "-----%s登录成功-----", msg1.name);

	//先遍历链表 将新用户加入群聊的消息发给所有人
	while(head->next != NULL){
		head = head->next;
		if(sendto(sfd, &msg1, sizeof(msg1), 0, (struct sockaddr*)&(head->cin), sizeof(head->cin))<0){
			ERR_MSG("sendto");
			return -1;
		}
	}

    //将登录成功的客户端封装成结点
	LinklistPtr p = (LinklistPtr)malloc(sizeof(Node));
	p->cin = cin;
	p->next= NULL;
	
	//将登录成功的客户端信息添加到链表中
	head->next = p;
	return 0;

}

客户端:

#include <myhead.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%d__:",__LINE__);\
	perror(msg);\
}while(0)

#define PORT 8888
#define IP "192.168.1.3"

typedef struct{
	char type;
	char name[20];
	char text[128];
}MSG;

struct ARG{
	MSG msg;
	int cfd;
	struct sockaddr_in sin;
};

void *task(void *arg);

int main(int argc, const char *argv[])
{

	//创建报式套接字
	int cfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(-1 == cfd)
	{
		ERR_MSG("socket");
		return -1;
	}

	//填充地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port   = htons(PORT);     //端口号
	sin.sin_addr.s_addr = inet_addr(IP); //本机IP

	//发送登录信息给服务器
	MSG msg;
	memset(&msg, 0, sizeof(msg));
	printf("请输入用户名:");
	fgets(msg.name, sizeof(msg.name), stdin);
	msg.name[strlen(msg.name)-1]='\0';
	msg.type = 'L';

	if(sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){
		ERR_MSG("sendto");
		return -1;
	}

	pthread_t tid;

	struct ARG arg;
	arg.msg = msg;
	arg.cfd = cfd;
	arg.sin = sin;
    
    //创建线程
	if(pthread_create(&tid, NULL, task, &arg)){
		ERR_MSG("pthread create");
		return -1;
	}

	while(1){
		if(recvfrom(cfd, &msg, sizeof(msg), 0, NULL, NULL) < 0){
			perror("recvfrom");
			return -1;
		}
		printf("%s\n",msg.text);
	}

	pthread_detach(tid);

    //关闭文件描述符
	close(cfd);
	return 0;
}


void *task(void *arg){
	MSG msg = ((struct ARG*)arg)-> msg;
	int cfd = ((struct ARG*)arg)-> cfd;
	struct sockaddr_in sin = ((struct ARG*)arg)-> sin;
	while(1){
		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 = 'Q';
		}
		else
		{
			msg.type = 'C';
		}
		if(sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin))<0)
		{
			ERR_MSG("sendto");
			return NULL;
		}
		if(msg.type == 'Q')
		{
			return NULL;
		}

	}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值