一个基于UDP的简单通讯程序

要求:
类似QQ的局域网通信工具

功能:
1、多个客户端进行通信(一对一) bingo!
2、用户的上线、离线提醒(广播) bingo!
3、每一个用户应该有上线的用户列表 bingo!
4、用户之间收发消息: 既然地址会有重用,无法解决,就采取收信息时过滤,或者发信息过滤
一对多(可以用组播,问题:在一个组播组中,有多个用户时端口号不同怎么去发送数据;其他)
5、一对一收发文件 bingo!
6、退出 bingo!

选做功能:
1、每个用户有自己的用户名和密码,需要登录 %50 bingo!
2、防止ctrl+c强制结束时的错误处理 %20 bingo!
3、实现好友列表,可以添加删除好友 自动添加,不能删除,多加个链可以实现

注:在实现上可以使用网状拓扑(udp, 只有一个程序),还可以使用C/S架构(tcp, 一个服务器程序,一个客户端程序)
一个程序 bingo!

head.h

#ifndef __HEAD_H_
#define __HEAD_H_

//文件IO
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//进程
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//线程
#include 

//socket
#include 
#include 

#include "kernel_list.h"

#endif在这里插入代码片

kernal_list.h

#ifndef __KERNEL_LIST_H
#define __KERNEL_LIST_H

/* This file is from Linux Kernel (include/linux/list.h)
* and modified by simply removing hardware prefetching of list items.
* Here by copyright, credits attributed to wherever they belong.
* Kulesh Shanmugasundaram (kulesh [squiggly] isis.poly.edu)
*/

/*
* Simple doubly linked list implementation.
*
* Some of the internal functions (“__xxx”) are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
/**
 * container_of - cast a member of a structure out to the containing structure
 *
 * @ptr:	the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:	the name of the member within the struct.
 *
 */
 
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#define container_of(ptr, type, member) ({			\
        const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
        (type *)( (char *)__mptr - offsetof(type,member) );})
/*
 * These are non-NULL pointers that will result in page faults
 * under normal circumstances, used to verify that nobody uses
 * non-initialized list entries.
 */
//#define LIST_POISON1  ((void *) 0x00100100)
//#define LIST_POISON2  ((void *) 0x00200)

// 小结构体(内核链表)
struct list_head {
	struct list_head *next;
	struct list_head *prev;
};

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)

#define INIT_LIST_HEAD(ptr) do { \
	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)

/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new, // 要插入的节点
				struct list_head *prev,// 前节点 before
				struct list_head *next) // 后节点 after
{
	next->prev = new; // 头插法 ,插在头节点之后
	new->next = next;
	new->prev = prev;
	prev->next = new;
}

/**
* list_add – add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head. 在指定的头后插入新的节点
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}

/**
* list_add_tail – add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
	next->prev = prev;
	prev->next = next;
}

/**
* list_del – deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is in an undefined state.
*/
//函数实现仅仅是将节点从链表中取下,未释放
static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = (void *) 0;
	entry->prev = (void *) 0;
}

/**
* list_del_init – deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	INIT_LIST_HEAD(entry);
}

/**
* list_move – delete from one list and add as another’s head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list,
				struct list_head *head)
{
	__list_del(list->prev, list->next);
	list_add(list, head);
}

/**
* list_move_tail – delete from one list and add as another’s tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
					struct list_head *head)
{
	__list_del(list->prev, list->next);
	list_add_tail(list, head);
}

/**
 * list_replace - replace old entry by new one
 * @old : the element to be replaced
 * @new : the new element to insert
 *
 * If @old was empty, it will be overwritten.
 */
static inline void list_replace(struct list_head *old,
				struct list_head *new)
{
	new->next = old->next;
	new->next->prev = new;
	new->prev = old->prev;
	new->prev->next = new;
}

static inline void list_replace_init(struct list_head *old,
					struct list_head *new)
{
	list_replace(old, new);
	INIT_LIST_HEAD(old);
}


/**
* list_empty – tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(struct list_head *head)
{
	return head->next == head;
}

static inline void __list_splice(struct list_head *list,
					struct list_head *head)
{
	struct list_head *first = list->next;
	struct list_head *last = list->prev;
	struct list_head *at = head->next;

	first->prev = head;
	head->next = first;

	last->next = at;
	at->prev = last;
}

/**
* list_splice – join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head);
}

/**
* list_splice_init – join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head);
INIT_LIST_HEAD(list);
}
}

/**
* list_entry – get the struct for this entry
* @ptr:    the &struct list_head pointer.    小结构体对应的地址
* @type:    the type of the struct this is embedded in.  大结构体类型 (struct info)
* @member:    the name of the list_struct within the struct. 小结构体在大结构体里面的成员名list
*///返回值为大结构体地址
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

/**
* list_for_each    -    iterate over a list
* @pos:    the &struct list_head to use as a loop counter. // p
* @head:    the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next)
/**
* list_for_each_prev    -    iterate over a list backwards
* @pos:    the &struct list_head to use as a loop counter.
* @head:    the head for your list.
*/
//反向遍历
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); \
pos = pos->prev)

/**
* list_for_each_safe    -    iterate over a list safe against removal of list entry
* @pos:    the &struct list_head to use as a loop counter.
* @n:        another &struct list_head to use as temporary storage
* @head:    the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)

/**
* list_for_each_entry    -    iterate over list of given type
* @pos:    the type * to use as a loop counter.
* @head:    the head for your list.
* @member:    the name of the list_struct within the struct.
pos为大结构体,head为大结构体中的小结构体成员
*/
#define list_for_each_entry(pos, head, member)                \
for (pos = list_entry((head)->next, typeof(*pos), member);    \
&pos->member != (head);                     \
pos = list_entry(pos->member.next, typeof(*pos), member))

/**
* list_for_each_entry_safe – iterate over list of given type safe against removal of list entry
* @pos:    the type * to use as a loop counter.
* @n:        another type * to use as temporary storage
* @head:    the head for your list.
* @member:    the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member)            \
for (pos = list_entry((head)->next, typeof(*pos), member),    \
n = list_entry(pos->member.next, typeof(*pos), member);    \
&pos->member != (head);                     \
pos = n, n = list_entry(n->member.next, typeof(*n), member))

#endif
在这里插入代码片
/* *********************************
***date:2020-6-5
***类似QQ的局域网通信工具(拓扑结构)
***name:[email protected]
 **********************************/
#include "head.h"

struct client *head = NULL;
struct sockaddr_in recv_addr;
int socket_send;
int socket_rec;
char fun_port[20];

struct client
{
	char ip[30];
	unsigned short port;
	int accept_fd;
	char name[5];
	struct list_head list;//内核链表小结构体
};//分号


/* 初始化链表 */
struct client *init_list(void)
{
	struct client *head = malloc(sizeof(struct client));
	if(head != NULL)
	{
		INIT_LIST_HEAD(&head->list);
	}
	return head;
}

//发消息
bool sen_msg(char *msg, unsigned short port)
{
	struct list_head *pos;
	struct client *tmp;
	list_for_each(pos, &head->list)
	{
		tmp = list_entry(pos, struct client, list);
		if(tmp->port == port)
		{
			write(tmp->accept_fd, msg, strlen(msg));
			return true;
		}
	}
	return false;
}


// void send_exit()
// {
	// sendto(socket_send, "exit", 10, 0, (struct sockaddr *)&recv_addr, sizeof(recv_addr));
	
// }

//防止误按ctrl +c导致异常退出,对方不知道你是否在线的情况
void fun()
{
	char tmp_port[30]={0};
	sprintf(tmp_port,"exit:%s",fun_port);
	sendto(socket_send, tmp_port, 30, 0, (struct sockaddr *)&recv_addr, sizeof(recv_addr));
	exit(0);
}
void *routine(void *port)
{
	   /* 创建 套接字 */
		int socket_udp = socket(AF_INET, SOCK_DGRAM, 0);
		if(socket_udp == -1)
		{
			perror("socket");
			return NULL;
		}
		
		/* 绑定IP地址和端口号 */
		struct sockaddr_in server_addr;
		bzero(&server_addr, sizeof(server_addr));
		server_addr.sin_family = AF_INET;
		
		server_addr.sin_port = htons(atoi(port));  		
													printf("port: %s\n",(char*)port);
		// 1、inet_addr
		//server_addr.sin_addr.s_addr = inet_addr("192.168.14.203");
		//2、inet_aton
		//inet_aton("192.168.14.203", &server_addr.sin_addr);
		//3、INADDR_ANY  代表主机的所有IP地址
		server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
		// inet_aton(tmp->ip, &server_addr.sin_addr);
		//需要绑定的应该是自己的port套接字,加入监视用来看谁发来了消息
		//问题:不能将得到的广播信息作为监听套接字绑定,试试创建新的单播udp socket
						
		int ret = bind(socket_udp, (struct sockaddr *)&server_addr, sizeof(server_addr));
		if(ret == -1)
		{
			perror("bind");
													// printf("LINE:%d\n",__LINE__);
			// return NULL;
		}
		else
			printf("bind sucess\n");
	
		// 注册退出处理函数
	    // atexit(send_exit);
	 while(1)
	 {
			// 好友列表
			// /* 遍历链表*/
			// list_for_each(pos, &head->list)
			// {
				// tmp = list_entry(pos, struct client, list);
				// printf("好友列表\n");
				// printf("[%s]\n", tmp->name);
			// }
			printf("1 发送消息给某人 2 发文件 3 创建群聊 4 下线 5 刷新好友列表\n");
			int num;
			char r_msg[100] = {0};
			char w_msg[100] = {0};
			char *str = NULL;
			struct client *tmp;
			struct list_head *pos;
			int maxfd = 0;		
			
			/* 绑定自己的套接字 加入监视 ,接收输入*/
			fd_set set;
			FD_ZERO(&set);			
										
			FD_SET(socket_udp, &set);
			maxfd = maxfd > socket_udp ? maxfd : socket_udp;		
			
			FD_SET(STDIN_FILENO, &set);
			maxfd = maxfd > STDIN_FILENO ? maxfd : STDIN_FILENO;
			
			int flag = 0;
			/* 检测文件描述符是否有动作 */
			select(maxfd+1, &set, NULL, NULL, NULL);
			
			if(FD_ISSET(STDIN_FILENO, &set))//输入消息
			{
				scanf("%d",&num);
				switch(num)
				{
					case 1:
					{ 
						printf("请输入消息格式 名字:消息\n");
						read(0,w_msg,100);
						// scanf(w_msg, 100, stdin);
															// printf("what is read %s\n",w_msg);
						struct sockaddr_in server_addr;
						bzero(&server_addr, sizeof(server_addr));
						// str = strstr(w_msg, ":");
						char real_send[100]={0};
						sprintf(real_send,"%s%s",(char *)port,w_msg);
						
						list_for_each(pos, &head->list)
						{
							tmp = list_entry(pos, struct client, list);
							
							if(strncmp(tmp->name,w_msg,4) == 0)
							{
								/* 创建 套接字 */
								int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
								if(socket_fd == -1)
								{
									perror("socket");
									return NULL;
								}
	
								printf("find [%s:%d] name %s\n", tmp->ip, tmp->port,tmp->name);
								struct sockaddr_in server_addr;
								bzero(&server_addr, sizeof(server_addr));
								server_addr.sin_family = AF_INET;
								server_addr.sin_port = htons(tmp->port);
								server_addr.sin_addr.s_addr = inet_addr(tmp->ip);
								
								/* 发数据 最后两个参数说明这个消息发送到谁*/
								sendto(socket_fd,real_send, 100, 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
								// printf("how much sendto %d\n",ret);
								flag = 1;
								break;
							}		
						}
						if(flag == 0)
							printf("没找到该用户\n");
						break;
					}
					case 4:
					{     //进程结束,发送退出通知	
						  char tmp_port[30]={0};
						  sprintf(tmp_port,"exit:%s",(char*)port);
						  sendto(socket_send, tmp_port, 30, 0, (struct sockaddr *)&recv_addr, sizeof(recv_addr));
						  exit(0);
				    }
					break;
					//发文件
					case 2:
					{
						/* 创建 套接字 */
						int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
						if(socket_fd == -1)
						{
							perror("socket");
							return NULL;
						}
						char filename[50]={0};
						/* 输入名字:文件名 不要写当前路径 */
						printf("请输入消息格式 名字:file_文件名%s\n", filename);
						
						read(0,filename,50);  //read后面有\n,导致打开文件错误
						//将末尾\n换成\0
						for(unsigned int i = 0;i < strlen(filename);i++)
						{
							if(filename[i] == '\n')
							{
								filename[i] = '\0';
								break;
							}
						}
						char tmp_port[50]={0};
						sprintf(tmp_port,"%s%s",(char*)port,filename);
						
						struct sockaddr_in server_addr;
						bzero(&server_addr, sizeof(server_addr));						
						
						// str = strstr(w_msg, ":");
						list_for_each(pos, &head->list)
						{
							tmp = list_entry(pos, struct client, list);
							
							if(strncmp(tmp->name,filename,4) == 0)
							{
								printf("find [%s:%d] name %s\n", tmp->ip, tmp->port,tmp->name);
								server_addr.sin_family = AF_INET;
								server_addr.sin_port = htons(tmp->port);
								server_addr.sin_addr.s_addr = inet_addr(tmp->ip);
								/* 发数据 最后两个参数说明这个消息发送到谁*/
								sendto(socket_fd,tmp_port, 50, 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
								flag = 1;
								break;
							}		
						}
						if(flag == 0)
						{	
						    printf("没找到该用户\n");
						    break;
						}
						/* 打开文件 */
						str = strstr(filename, "_");
					    char filepath[20] = {0};
						strcpy(filepath,str+1);
																	printf("filepathname : %s\n",filepath);
						int file_fd = open(filepath, O_RDWR);    //??
						if(file_fd == -1)
						{
							perror("open() failed");
							break;
						}
						else
							printf("发送文件名成功,发送数据中\n");
						
						/* 发送文件 */
						int r, w;
						int nw = 0;
						char r_buf[100] = {0};
						
						while(1)
						{
							r = read(file_fd, r_buf, sizeof(r_buf));
							if(r == -1)
							{
								perror("read");
								break;
							}
							usleep(10000);
							w = sendto(socket_fd,r_buf, r, 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
							if(w == 0)
							{
								printf("sendto finish\n");
								break;
							}
							nw += w;
							printf("sendto %d byte\n", nw);
						}
					}
					case 5:
						{
							//好友列表
							/* 遍历链表*/
							printf("好友列表\n");
							list_for_each(pos, &head->list)
							{
								tmp = list_entry(pos, struct client, list);
								
								printf("[name:%s port:%d]\n", tmp->name,tmp->port);
							}
						}
						break;
					default:
							printf("没有该选项,请重新输入\n");
							break;
				}	
			}
			if(FD_ISSET(socket_udp, &set))//收消息
			{
				bzero(r_msg, 100);
				struct sockaddr_in client_addr;
				socklen_t size = sizeof(client_addr);
				recvfrom(socket_udp, r_msg, 100, 0, (struct sockaddr *)&client_addr, &size);
				//recv 端口号名字:信息
				str = strstr(r_msg,":");
				
				list_for_each(pos, &head->list)
				{
					tmp = list_entry(pos, struct client, list);
					if(tmp->port == atoi(r_msg)) //不能用接收来的port用来判断
					{
						//接收文件格式  端口号name:file_路径/文件名
					    //默认存到当前路径下
						if(strncmp(str+1,"file",4) == 0) //要收文件了
						{
							/* 选出文件名 */
							 // int k = 0;
							 int i = strlen(r_msg)-1;
							for(;i >= 0;i--)  
							{  
								if(r_msg[i] != '/')      
								{  
									// k++;  
								} 								
								else
									break;
							} 
							char filename[20] = {0};
							strcpy(filename,r_msg+i+1);  
															printf("filename: %s\n",filename);
				
							int file_fd = open(filename, O_RDWR|O_CREAT, 0777);
							if(file_fd == -1)
							{
								perror("creat() failed:");
								break ;
							}						
							else
							  printf("创建%s文件名成功,接收数据中\n",filename);
							/* 接收文件 */
							int r, w;
							int nr = 0;
							char r_buf[100] = {0};
							while(1)
							{
								bzero(r_buf,100);
								r = recvfrom(socket_udp, r_buf, 100, 0, (struct sockaddr *)&client_addr, &size);
								if(r == -1)
								{
									perror("recform");
									break;
								}
								nr += r;
								printf("read() %d byte\n", nr);
								
								w = write(file_fd, r_buf, r);
								if(w == -1)
								{
									perror("write() failed");
									break ;
								}
								if( w == 0)
								{
									printf("write finish \n");
									break;
								}
								
							}
						}
						else
							printf("%s:%s\n",tmp->name,str+1);
						break;
					}
				}	
			}
				
	 }
}

void link_to(char *name,char *tmp_port)
{
	//注册信号处理函数ctrl+c
	strcpy(fun_port,tmp_port);
	signal(SIGINT,fun);
	
	// 初始化头节点
	head = init_list();
	
	socket_send = -1;
	socket_rec = -1;
	struct list_head *pos, *n;
	struct client *tmp;
	char *str = NULL;
	// char s_name[10]={0};
	// strcpy(s_name,name);
	
	/* 创建 套接字 */
	socket_send = socket(AF_INET, SOCK_DGRAM, 0);
	if(socket_send == -1)
	{
		perror("socket");
		return ;
	}
	
	/* 设置可以发送广播 和端口重用*/
	int on = 1;
	setsockopt(socket_send, SOL_SOCKET,SO_BROADCAST, &on, sizeof(on));
	setsockopt(socket_send, SOL_SOCKET,SO_REUSEADDR, &on, sizeof(on));
	/* 设定广播地址和端口号 */
	
	bzero(&recv_addr, sizeof(recv_addr));
	recv_addr.sin_family = AF_INET;
	recv_addr.sin_port = htons(50002);
	recv_addr.sin_addr.s_addr = inet_addr("255.255.255.255");
	
	char tmp_name[25]={0};
	/* 发送用户名和单播通信端口号*/
	sprintf(tmp_name,"%s:%s",tmp_port,name);
																	printf("tmp_name__%s\n",tmp_name);
	sendto(socket_send,tmp_name, 20, 0, (struct sockaddr *)&recv_addr, sizeof(recv_addr));	
	
	
	    //建立线程
		pthread_t tid;
		// (void *)tmp_port;
		pthread_create(&tid,NULL,routine,(void *)tmp_port);
		
		
		/* 创建广播接收 套接字 */
		socket_rec = socket(AF_INET, SOCK_DGRAM, 0);
		if(socket_rec == -1)
		{
			perror("socket");
			return ;
		}
		/* 设置端口重用 */
		int in = 1;
		setsockopt(socket_rec, SOL_SOCKET,SO_REUSEADDR, &in, sizeof(in));
		
		/* 绑定IP地址和端口号 */
		struct sockaddr_in server_addr;
		bzero(&server_addr, sizeof(server_addr));
		server_addr.sin_family = AF_INET;
		server_addr.sin_port = htons(50002);
		// 1、inet_addr
		//server_addr.sin_addr.s_addr = inet_addr("192.168.14.203");
		//2、inet_aton
		//inet_aton("192.168.14.203", &server_addr.sin_addr);
		//3、INADDR_ANY  代表主机的所有IP地址
		server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
		int ret = bind(socket_rec, (struct sockaddr *)&server_addr, sizeof(server_addr));
		if(ret == -1)
		{
			perror("bind");
			return ;
		}
		//问题;第三个用户上线后,只能接收到一个用户的广播,尝试多路复用
	while(1)
	 {	
		int maxfd = 0;		
		
		/* 绑定套接字 加入监视 ,接收输入*/
		fd_set set;
		FD_ZERO(&set);			
									
		FD_SET(socket_rec, &set);
		maxfd = maxfd > socket_rec ? maxfd : socket_rec;		
			
		/* 检测文件描述符是否有动作 */
		select(maxfd+1, &set, NULL, NULL, NULL);
		
		if(FD_ISSET(socket_rec, &set))//输入消息
		{	
			char buf[30];
			struct sockaddr_in send_addr;
			socklen_t size = sizeof(send_addr);
			
			/* 数据收 */
			bzero(buf, 30);
			recvfrom(socket_rec, buf, 30, 0, (struct sockaddr *)&send_addr, &size);
																
			 str = strstr(buf,":");
																		
			//判断是否为下线通知
			if(strncmp(buf,"exit",4) == 0)
			{																													
																	// printf("bufread: %s\n",buf);																
				list_for_each_safe(pos, n, &head->list)
				{
					tmp = list_entry(pos, struct client, list);
																	// printf("tmp->port: %d\n",tmp->port);
					if(tmp->port == atoi(str+1))
					{
						printf("name %s已经下线\n",tmp->name);
						//从链表中删除
						list_del(&tmp->list);
						break;
						
					}
				}	
				continue;
			}	
				
			/* 接收上线通知,判断是否是新用户,新的话将将对端IP和端口号和名字存入链表中,并发送自己的在线通知给其他人*/					
			/*遍历链表*/
			int flag = 0;
			list_for_each_safe(pos, n, &head->list)
			{
				tmp = list_entry(pos, struct client, list);
				if(strcmp(tmp->name,str+1) == 0)								
				{
					flag = 1;
				}
				
			}
														// printf("str+1 : %s\n",str+1);
														// printf("tmp_port : %s\n",tmp_port);
			if(atoi(tmp_port) == atoi(buf))
					flag = 1;
			if(flag == 0)
			{
				//创建套接字绑定端口等信息用做单播通信
				printf("form [%s:%d] name %s已经上线\n", inet_ntoa(send_addr.sin_addr), ntohs(send_addr.sin_port), buf);
				struct client *new = calloc(1, sizeof(struct client));
				if(new != NULL)
				{								
					strcpy(new->ip, inet_ntoa(send_addr.sin_addr));
					new->port =  atoi(buf);
					// new->accept_fd = socket_udp;
					str = strstr(buf,":");
					strcpy(new->name,str+1);
					// port++;
				}
				// /* 添加到链表中 */
				list_add_tail(&new->list, &head->list);
				//发送自己的在线通知给其他人
				// usleep(10000);
				sendto(socket_send,tmp_name, 30, 0, (struct sockaddr *)&recv_addr, sizeof(recv_addr));
			}
		}
	}			
			
	/* 关闭套接字文件描述符 */
	shutdown(socket_rec, 2);	
	shutdown(socket_send, 2);
}

int main(int argc,char **argv)
{
	
	int num;
	int fd_open;
	int ret;
	
	if(argc != 2)
	{
		printf("请输入./link 单播端口起始号");
		return 0;
	}
	while(1)
	{	
		
		while(1)
			{	
		        printf("1 注册 2 登录 3 关闭\n");               
				ret = scanf("%d",&num);
				if (ret != 1)
				{
					continue;
				}
				else
					break;
			}			
		if(num == 3)
			return 0;
		switch(num)
		{
			case 1:
			{
				char name[5] = {0};
				while(1)
				{
					
					printf("请输入4位数用户名\n");
					scanf("%s",name);
					if(strlen(name) == 4)
						break;
					bzero(name,5);
				}
				//打开或新建一个文件
				fd_open = open("../image/name.txt",O_RDWR | O_CREAT,0777);
				//先读判断用户名是否存在
				char buf[5] = {0};
				while(1)
				{	
					
					read(fd_open,buf,5);
					//到文件末尾且没有相同的用户名 表示可以注册
					if(strcmp(buf,name) != 0  && read(fd_open,buf,5) == 0)
					{
						printf("用户名未用,可以使用\n");
						int ret_w = write(fd_open,name,5);
						if(ret_w == 5)
						{
							printf("注册成功,可以登录\n");
							close(fd_open);							
						}
						break;
					}
					//若有相同的,则需要重新注册
					else if(strcmp(buf,name) == 0)
					{
						printf("用户名已存在,请重新注册\n");
						close(fd_open);
						break;
					}
					bzero(buf,5);						
				}
			}
			break;
			
			case 2:
			{
				fd_open = open("../image/name.txt",O_RDWR | O_CREAT,0777);
				char name[5] = {0};
				char buf[5] = {0};
				while(1)
				{	
					printf("请输入4位用户名\n");
					scanf("%s",name);
				    if(strlen(name) == 4)
						break;
					bzero(name,5);
				}
							
				while(1)
				{
					int ret_r = read(fd_open,buf,5);
					if(strcmp(buf,name) == 0)
					{
						printf("登录成功\n");
						//进入函数
																printf("__%s\n",argv[1]);
						link_to(name,argv[1]);
					}
					bzero(buf,5);
					if(ret_r == 0)
				    {
						printf("输入的用户不存在,请注册\n");
						close(fd_open);
						break;
					}
				}
			}
			break;
			
			default:
			    printf("没有这个选项,请重新输入\n");
			     break;
		}
	  
	}
	
	return 0;
}在这里插入代码片

你可能感兴趣的:(链表,linux,udp,信号处理)