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