网络编程day060901

目录

网络聊天室

服务器

库:

源文件:

测试文件:

客户端:

测试文件:

实现效果:

思维导图


网络聊天室
服务器:
库:
#ifndef __CHATROOM_H__
#define __CHATROOM_H__
#include<myhead.h>
struct msg
{
	char type;
	char name[20];
	char text[128];
};

#define ERR_MSG(msg) do{\
	printf("%d:",__LINE__);\
	perror(msg);\
	}while(0);

/*服务器的所需*/
struct addmsg{
	char name[20];
	struct sockaddr_in cin;
};

typedef struct Link
{
	union{
		int len;
		struct addmsg data;
	};

	struct Link *next;
}linkList,*linkListPtr;

//创建UDP服务器模型函数
int UDP_ser_create(int *psfd,const char *pIP, unsigned short port);
//用户信息保存链表创建函数
linkListPtr linklist_creat();
//创建数据发送函数
int msg_send(int sfd,linkListPtr L, char *text, char *name, int mode);
//创建头插函数
int cli_addrmsg_save(linkListPtr L, char *name, struct sockaddr_in cin);
//创建链表的按值查找并任意位置删除函数
int linkList_selete_value(linkListPtr L, struct sockaddr_in cin);
//查找对应的地址信息是否已存在函数
int linkList_search_value(linkListPtr L, struct sockaddr_in cin);

/****************服务器所需***************/

/********客户端所需**************/


/********客户端所需**************/
#endif
源文件:
#include<myhead.h>
#include"chatroom.h"

int UDP_ser_create(int *psfd, const char *pIP, unsigned short port){
	//创建套接字文件
	if((*psfd = socket(AF_INET, SOCK_DGRAM, 0))<0){
		ERR_MSG("socket error");
		return -1;
	}

	//允许端口被快速复用
	int resue = 1;
	if(-1 == setsockopt(*psfd, SOL_SOCKET, SO_REUSEADDR, &resue, sizeof(resue))){
		ERR_MSG("setsockopt error");
		return -1;
	}

	//定义地址信息结构体用于绑定bind
	struct sockaddr_in sin;
	sin.sin_family    = AF_INET;
	sin.sin_port      = htons(port);
	sin.sin_addr.s_addr = inet_addr(pIP);
	//绑定
	if(bind(*psfd, (struct sockaddr*)&sin, sizeof(sin))==-1){
		ERR_MSG("bind error");
		return -1;
	}

	return 0;
}

//用户信息保存链表创建函数
linkListPtr linklist_creat(){
	linkListPtr L = (linkListPtr)malloc(sizeof(linkList));
	if(NULL == L){
		return NULL;
	}

	printf("用户链表申请成功\n");

	//申请成功,初始化
	L->len = 0;
	L->next = NULL;

	return L;
}

//创建数据发送函数
int msg_send(int sfd, linkListPtr L, char *text, char *name, int mode){
	if(NULL == L){
		printf("链表不存在\n");
		return -1;
	}
	linkListPtr p = L->next;
	//printf("%s,%ld\n",text,strlen(text));
	char buf[] = "**********系统消息**********";
	if(1 == mode){
		//说明是服务器发送的系统消息
		while(p!=NULL){
			if(strcmp(name,p->data.name)){
				sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&p->data.cin, sizeof(p->data.cin));
				sendto(sfd, text, strlen(text), 0, (struct sockaddr*)&p->data.cin, sizeof(p->data.cin));
				sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&p->data.cin, sizeof(p->data.cin));
			}
			p = p->next;
		}
	}else{
		//说明是来自用户的,需要转发的消息
		while(p!=NULL){
			if(strcmp(name,p->data.name)){
				sendto(sfd, text, 150, 0, (struct sockaddr*)&p->data.cin, sizeof(p->data.cin));
			}
			p = p->next;
		}	
	}
	return 0;
}

//创建头插函数
int cli_addrmsg_save(linkListPtr L, char *name, struct sockaddr_in cin){
	linkListPtr p = (linkListPtr)malloc(sizeof(linkList));
	if(NULL == p){
		return -1;
	}

	//保存数据
	strcpy(p->data.name,name);
	p->data.cin = cin;

	p->next = L->next;
	L->next = p;

	return 0;
}

//创建链表的按值查找并任意位置删除函数
int linkList_selete_value(linkListPtr L, struct sockaddr_in cin){
	if(L == NULL){
		printf("链表不存在\n");
		return -1;
	}
	linkListPtr p = L;
	linkListPtr q = NULL;
	while(p->next!=NULL){
		if(p->next->data.cin.sin_family == cin.sin_family&&\
			p->next->data.cin.sin_port == cin.sin_port&&\
			p->next->data.cin.sin_addr.s_addr == cin.sin_addr.s_addr){
			q = p->next;
			p->next = q->next;
			free(q);
			q = NULL;
		}
		if(p->next == NULL){
			break;
		}
			p=p->next;
	}

	return 0;

}

//查找对应的地址信息是否已存在函数
int linkList_search_value(linkListPtr L, struct sockaddr_in cin){
	if(L == NULL){
		printf("链表不存在\n");
		return -1;
	}
	linkListPtr p = L;

	while(p->next!=NULL){
		if(p->next->data.cin.sin_family == cin.sin_family&&\
			p->next->data.cin.sin_port == cin.sin_port&&\
			p->next->data.cin.sin_addr.s_addr == cin.sin_addr.s_addr){
			
			return 1;
					
		}
		p=p->next;
	}

	return 0;
}
测试文件:
#include<myhead.h>
#include"chatroom.h"

int main(int argc, const char *argv[])
{
	//通过外部传参传入IP地址
	if(argc!=2){
		printf("the number of arguments is wrong!\n");
		printf("usage:./xxx IP\n");
		return -1;
	}

	//一些准备用的变量
	int sfd = -1;
	unsigned short port = 8888;
	//创建UDP服务器模型
	if(-1==UDP_ser_create(&sfd, argv[1], port)){
		printf("服务器创建失败\n");
		return -1;
	}
	printf("服务器创建成功\n");

	//创建保存用户信息的链表
	linkListPtr L;
	if((L = linklist_creat()) == NULL){
		printf("链表申请失败\n");
		return -1;	
	}

	char name11[20] = "";

	//子进程号
	pid_t pid;
	pid = fork();
	if(pid <0){
		ERR_MSG("fork error");
		return -1;
	}else if(0 == pid){
		char buf[128] = "";
		while(1){
			bzero(buf,sizeof(buf));
			//阻塞等待从终端获取消息以发送
			//fgets(buf, sizeof(buf), stdin);
			scanf("%s",buf);
			//去除垃圾字符
			//getchar();
			buf[strlen(buf)] = 10;

			//调用发送函数
			if(-1 == msg_send(sfd, L, buf, name11, 1)){
				printf("发送失败\n");

			}
		}

		exit(EXIT_SUCCESS);
	}else{
		//创建存储收到的信息的结构体变量
		struct msg climsg;
		//存储获取用户地址信息的结构体变量
		struct sockaddr_in cin;
		socklen_t cinlen = sizeof(cin);
		//用来存储登录信息
		char loadmsg[128] = "";
		char buf1[151] = "";
		while(1){
			bzero(loadmsg,sizeof(loadmsg));
			bzero(buf1,sizeof(buf1));
			bzero(climsg.name,sizeof(climsg.name));
			bzero(climsg.text,sizeof(climsg.text));

			recvfrom(sfd, &climsg, sizeof(climsg), 0, (struct sockaddr*)&cin, &cinlen);
			//printf("我们收到消息\n");

			if('L' == climsg.type){
				if(linkList_search_value(L, cin)){
					//说明该地址信息已存在,提示无需重复登录
					snprintf(loadmsg, sizeof(loadmsg), "您已在线,无需重复登录\n");
					
					sendto(sfd, loadmsg, strlen(loadmsg), 0, (struct sockaddr*)&cin, sizeof(cin));

				}
				//登录
				else{
					if(-1 == cli_addrmsg_save(L, climsg.name, cin)){
						printf("登录失败\n");
						return -1;
					}
					snprintf(loadmsg, sizeof(loadmsg), "[%s]:上线了!", climsg.name);
					if(-1 == msg_send(sfd, L, loadmsg, climsg.name, 1)){
						printf("发送失败\n");
					}
				}
			}else if('C' == climsg.type){
				if(linkList_search_value(L, cin)){
					//如果地址信息已保存在链表中,说明在线
					//群聊
					snprintf(buf1, sizeof(buf1), "[%s] :%s", climsg.name, climsg.text);
					if(-1 == msg_send(sfd, L, buf1, climsg.name , 0)){
						printf("发送失败\n");
					}
				}else{
					//如果地址信息不在链表中,提示先登录
					snprintf(loadmsg, sizeof(loadmsg), "请登录\n");
					sendto(sfd, loadmsg, strlen(loadmsg), 0, (struct sockaddr*)&cin, sizeof(cin));

				}

			}else{
				if(linkList_search_value(L, cin)){
					//如果已存在就退出
					//退出
					linkList_selete_value(L, cin);
					snprintf(loadmsg, sizeof(loadmsg), "[%s]退出群聊!", climsg.name);
					if(-1 == msg_send(sfd, L, loadmsg, name11, 1)){
						printf("发送失败\n");

					}
				}
				//如果不存在无需退出

			}

		}


	}

	kill(pid,SIGKILL);
	wait(NULL);
	close(sfd);
	return 0;
}
客户端:
测试文件:
#include<myhead.h>
#include"chatroom.h"

#define PORT 8888
#define IP "192.168.114.139"

int main(int argc, const char *argv[])
{
	int cfd;
	//调用函数创建客户端模型
	if((cfd = socket(AF_INET, SOCK_DGRAM, 0))<0){
		ERR_MSG("socket error");
		return -1;
	}	

	//登录
	struct msg climsg;
	climsg.type = 'L';
	printf("请输入您的昵称:");
	scanf("%s",climsg.name);
	
	//组装服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family      = AF_INET;
	sin.sin_port        = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);

	if(sendto(cfd, &climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin))<0){
		ERR_MSG("sendto error");
		return -1;
	}
	
	//登录成功后,发送的消息就是群聊消息
	climsg.type = 'C';
	
	//需要的变量
	char buf[150] = "";


	//创建子进程
	pid_t pid;
	pid = fork();
	if(pid < 0){
		ERR_MSG("fork error");
		return -1;
	}else if(0 == pid){
		//子进程
		while(1){
			//清空数组
			bzero(buf,sizeof(buf));
			//从终端获取
			scanf("%s",climsg.text);
			if(!strcmp(climsg.text,"quit")){
				climsg.type = 'Q';
				if(sendto(cfd, &climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin))<0){
					ERR_MSG("sendto error");
					return -1;
				}
				kill(getppid(),SIGKILL);
				break;
			}else{
				if(sendto(cfd, &climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin))<0){
					ERR_MSG("sendto error");
					return -1;
				}			
			}
		}
		exit(EXIT_SUCCESS);

	}else{
		//父进程
		while(1){
			//清空数组
			bzero(buf,sizeof(buf));
			//接收信息
			recv(cfd, buf, sizeof(buf),0);
			//输出读取到的数据
			printf("%s\n",buf);
		}
	}

	wait(NULL);
	close(cfd);

	return 0;
}
实现效果:

思维导图

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值