实例--聊天室

注:整理资料的时候整理出来的,很早的东西了,里面很多东西都有问题(没有校验,没有出错判断,strcpy、sprintf被使用等),不可用户正规的项目开发中,仅仅用来理解链表的简单操作和UDP网络编程的简单实现

功能:聊天室的实现,实现多人同时在线聊天

说明:以下是工程全部代码


文件linklist.h

#ifndef __LINKLIST_H__
#define __LINKLIST_H__


#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef struct sockaddr_in DateType;
typedef struct sockaddr	SA;

typedef struct _linknode_{
	DateType addr;
	struct _linknode_ *next;
} LinkNode;


extern LinkNode *creat_linklist(void);
extern int insert_linknode(LinkNode *head, DateType *value);
extern int delete_linknode(LinkNode *head, DateType *value);

#endif		/* end of __LINKLIST_H__ */

文件head.h

/*head files*/
#ifndef __HEAD_H__
#define __HEAD_H__

#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stddef.h>
#include <signal.h>
#include "linklist.h"

#define NAME_LEN	16
#define DATA_LEN	512

#define CMD_LOGIN	'I'
#define CMD_LOGOUT	'O'
#define CMD_CHAT	'C'

typedef struct _data_package_{
	int type;				/* 标识登陆类型 */
	char name[NAME_LEN];	/* 用户名 */
	char data[DATA_LEN];	/* 实际要传送的数据 */
}DatePack;

#define error_handler(EMESG)	\
	do{perror(EMESG); exit(EXIT_FAILURE);\
	}while(0);

#define SER_ADDR	"192.168.146.156"
#define SER_PORT	10001

extern int broadcast(int sockfd, DatePack *package, LinkNode *userlist);
extern int user_login(int sockfd, LinkNode *userlist, SA *addr, DatePack *package);
extern int user_logout(int sockfd, LinkNode *userlist, SA *addr, DatePack *package);


#endif		/* end of __HEAD_H__*/

文件linklist.c

#include "linklist.h"

LinkNode *creat_linklist(void)
{
	LinkNode *head = NULL;

	head = (LinkNode *)malloc(sizeof(LinkNode));
	head->next = NULL;
	
	return head;
}

int insert_linknode(LinkNode *head, DateType *value)
{
	LinkNode *node = NULL;

	node = (LinkNode *)malloc(sizeof(LinkNode));
	node->addr = *value;
	node->next = head->next;
	head->next = node;

	return 0;
}

int delete_linknode(LinkNode *head, DateType *value)
{
	LinkNode *p = head;
	LinkNode *tmp = NULL;
	
	while(NULL != p->next)
	{
		if(! memcmp( p->next, value, sizeof(SA)))
			break;
		p = p->next;
	}
	if(NULL == p->next)
		return -1;
	tmp = p->next;
	p->next = tmp->next;
	free(tmp);

	return 0;
}

文件clent.c

#include "head.h"

int main()
{
	int sockfd;
	struct sockaddr_in serveraddr = {0};
	DatePack *package = NULL;
	pid_t pid;
	int packsize;
	
	package = (DatePack *) malloc (sizeof(DatePack));

	/*init network*/
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons (SER_PORT);
	serveraddr.sin_addr.s_addr = inet_addr (SER_ADDR);	/* 将点分十进制的IP转换成一个长整型数 */
	
	sockfd = socket (AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		error_handler ("socket");	
	}
	
	if (0 > connect(sockfd, (SA *)&serveraddr, sizeof(SA)))
	{
		error_handler ("connect");
	}

	/* login server */
	puts ("enter your name:");
	gets (package->name);				/* 初始化登陆用户名 */
	package->type = CMD_LOGIN;			/* 初始化登陆类型 */
	
	if (0 > send (sockfd, package, offsetof(DatePack, data), 0))	/* 传送登陆信息 */
	{
		error_handler ("login:send");
	}

	pid = fork();						/* 创建一个进程,用于处理不同操作 */
	if (pid > 0)							/* 父进程,用于接收用户输入的信息并发送给服务器端 */
	{
		while (1)
		{
			puts (">>");					/* 提示用户输入可以输入信息了 */
			gets (package->data);		/* 得到用户输入的信息并存于将要发送信息的结构体中 */
			
			if (!strcmp(package->data, "#quit"))		/* 匹配结束标识,如果接收到该标识退出用户登陆 */
			{
				package->type = CMD_LOGOUT;			/* 匹配到结束标识,将登陆类型设置为登出 */
				packsize = offsetof(DatePack, data) + strlen(package->data) + 1;	/* 计算要发送的数据包的大小 */
				send (sockfd, package, packsize, 0);	/* 向服务器端发送打包好的数据 */
				sleep (1);							/* 睡一秒,保证数据发送完全 */
				kill (pid, 9);						/* 调用系统kill(),杀死子进程 */
				exit (0);							/* 退出 */
			}
			
			/* 用户正常通信 */
			package->type = CMD_CHAT;	/* 设置为聊天状态 */
			packsize = offsetof(DatePack, data) + strlen(package->data) + 1;	/* 计算要发送的数据包的大小 */
			send (sockfd, package, packsize, 0);		/* 向服务器端发送打包好的数据 */
		}
	}
	else if (pid == 0)					/* 子进程,用于接收服务器端的信息,并展示给用户 */
	{
		while (1)
		{
			recv (sockfd, package, sizeof(DatePack), 0);			/* 循环接受服务器端发送过来的数据 */
			printf ("%s:\n\t%s\n", package->name, package->data);
		}
	}
	else
	{
		error_handler ("fork");
	}
		
	return 0;
}

文件server.c

#include "head.h"

int main()
{
	int sockfd;
	struct sockaddr_in serveraddr = {0},
					   clientaddr = {0};
	LinkNode *userlist = NULL;
	DatePack *package = NULL;
	socklen_t addrlen = sizeof(SA);

	//init network
	sockfd = socket (AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
		error_handler("socket");

	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons (SER_PORT);
	serveraddr.sin_addr.s_addr = inet_addr (SER_ADDR);

	if (0 > bind(sockfd, (SA *)&serveraddr, sizeof(SA)))
		error_handler("bind");

	//define data 
	userlist = creat_linklist ();
	package = (DatePack *) malloc (sizeof(DatePack));
	
	
	while (1)
	{
		//recv user data package
		recvfrom (sockfd, package, sizeof(DatePack), 0, (SA *)&clientaddr, &addrlen);

		switch (package->type)
		{
			case CMD_CHAT:
				broadcast (sockfd, package, userlist);
				break;
			case CMD_LOGIN:
				printf ("%s login !", package->name);
				user_login (sockfd, userlist, (SA *)&clientaddr, package);
				break;
			case CMD_LOGOUT:
				printf ("%s logout !", package->name);
				user_logout (sockfd, userlist, (SA *)&clientaddr, package);
				break;
		}
	}
	return 0;
}

int broadcast (int sockfd, DatePack *package, LinkNode *userlist)
{
	LinkNode *p = userlist->next;
	int packsize;

	packsize = offsetof(DatePack, data) + strlen(package->data) + 1;
	
	while (NULL != p)		/* 循环给在链上的所有用户发送信息,实现广播的功能 */
	{
		sendto (sockfd, package, packsize, 0, (SA *)&p->addr, sizeof(p->addr));
		p = p->next;
	}
	return 0;
}

int user_login (int sockfd, LinkNode *userlist, SA *addr, DatePack *package)
{
	int packsize;	
	char username[NAME_LEN];

	strcpy (username, package->name);
	
	sprintf (package->data, "%s welcom !", username);
	strcpy (package->name, "system");
	
	packsize = offsetof(DatePack, data) + strlen(package->data) + 1;
	sendto (sockfd, package, packsize, 0, addr, sizeof(SA)); /* 发回给发送了信息的用户 */

	sprintf (package->data, "%s login !", username);
	package->type = CMD_CHAT;
	
	broadcast (sockfd, package, userlist); 	/* 广播给其他用户,通知该用户上线 */

	insert_linknode (userlist, (DateType *)addr);	/* 将该用户添加到用户链中 */
	
	return 0;
}

int user_logout (int sockfd, LinkNode *userlist, SA *addr, DatePack *package)
{
	int packsize;	
	char username[NAME_LEN];

	strcpy (username, package->name);
	
	sprintf (package->data, "%s see you later !", username);
	strcpy (package->name, "system");
	
	packsize = offsetof(DatePack, data) + strlen(package->data) + 1;
	
	sendto (sockfd, package, packsize, 0, addr, sizeof(SA));
	
	delete_linknode (userlist, (DateType *)addr);	/* 将该用户从用户链中删除 */

	sprintf (package->data, "%s logout !", username);
	package->type = CMD_CHAT;
	
	broadcast (sockfd, package, userlist);        /* 广播给其他用户,通知该用户下线 */

	return 0;
}


猜你喜欢

转载自blog.csdn.net/deggfg/article/details/22853617