实例--聊天室

来源:互联网 发布:淘宝网食品流通许可证 编辑:程序博客网 时间:2024/05/29 10:30

注:整理资料的时候整理出来的,很早的东西了,里面很多东西都有问题(没有校验,没有出错判断,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 sockaddrSA;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_LEN16#define DATA_LEN512#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_PORT10001extern 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 networksockfd = 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 packagerecvfrom (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;}


0 0
原创粉丝点击