基于UDP的网络聊天室

 客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>        
#include <pthread.h>
#include <stdlib.h>

#define PORT 7777               
#define IP "192.168.100.11"                       

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:__%d__\n",__LINE__);\
	perror(msg);\
}while(0)

//创建结构体封装数据包
struct msg
{
	char type; 			//封装协议
	char name[20];
	char text[128];
}recvdata,snddata;

//存储服务器信息,供给sendto使用
struct sockaddr_in rcv;
struct sockaddr_in ser;
//recvfrom接收到的数据包地址信息
socklen_t addrlen=sizeof(rcv);//数据包大小
//创建双线程用于收发
pthread_t tid1,tid2;
//发
void *callback1(void *arg)   //void *arg=&sfd 
{
	int sfd=*(int *)arg;
	ssize_t res=0;
	while(1)
	{
		bzero(snddata.text,sizeof(snddata.text));
		snddata.type = 'C';//封装聊天协议
		fgets(snddata.text,sizeof(snddata.text),stdin);
		snddata.text[strlen(snddata.text)-1]='\0';
		if(strcmp(snddata.text, "quit") == 0)
		{
			//组下线数据包
			snddata.type = 'Q';
			if(sendto(sfd,&snddata, sizeof(snddata), 0, (struct sockaddr*)&ser, sizeof(ser))< 0)
			{
				ERR_MSG("sendto");
				return NULL;
			}
			exit(0);
			//break;
		}
		if(sendto(sfd, &snddata, sizeof(snddata), 0, (struct sockaddr*)&ser, sizeof(ser)) < 0)
		{
			ERR_MSG("sendto");
			return NULL;
		}
		printf("sendto success\n");
	}
}
//收
void *callback2(void *arg) //void *arg=&sfd
{
	int sfd=*(int *)arg;
	char s=0;
	ssize_t res=0;
	while(1)
	{
		//接收
		bzero(&recvdata, sizeof(recvdata));
		//接收数据包                                                                   
		res = recvfrom(sfd,&recvdata, sizeof(recvdata),0,NULL,NULL);
		if(res < 0)
		{
			ERR_MSG("recvfrom");
			return NULL;
		}
		s=recvdata.type;
		switch(s)
		{
		case 'L':
			printf("[%s]已经上线---\n",recvdata.name);
			break;
		case 'C': 
			printf("[%s]:[%s]\n",recvdata.name,recvdata.text);
			break;
		case 'Q': 
			printf("[%s]已经下线---\n",recvdata.name);
			break;
		}
	}
	pthread_exit(&tid2);
}

int main(int argc, const char *argv[])
{

	//创建报式套接字                                               
	int sfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}

	//填充服务器地址信息结构体                      
	ser.sin_family=AF_INET;
	ser.sin_port=htons(PORT);
	ser.sin_addr.s_addr=inet_addr(IP);

	char buf[128]="";
	ssize_t res=0;
	printf("请输入登录用户名>>>\n");
	snddata.type='L';
	scanf("%s",snddata.name);
	while(getchar() != 10);
	if(sendto(sfd,&snddata,sizeof(snddata),0,(struct sockaddr *)&ser,sizeof(ser))<0)
	{
		ERR_MSG("sendto");
		return -1;
	}                                                  
	//	printf("sendto success\n");

	//创建callback1线程用于服务器发
	if(pthread_create(&tid1,NULL,callback1,(void *)&sfd)!=0)
	{
		fprintf(stderr,"pthread_create error\n");
		return -1;
	}
	pthread_detach(tid1); //分离线程

	//创建callback2线程用于服务器收
	if(pthread_create(&tid2,NULL,callback2,(void *)&sfd)!=0)
	{
		fprintf(stderr,"pthread_create error\n");
		return -1;
	}

	//阻塞线程
	pthread_join(tid2,NULL);

	//关闭文件描述符
	close(sfd);

	return 0;
}

 服务器

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>


#define PORT 7777          //指定端口号,1024~49151
#define IP "192.168.100.11"   //填自己的本机IP ifconfig查看

#define ERR_MSG(msg) do{\
    fprintf(stderr, "line:%d\n", __LINE__);\
    perror(msg);\
}while(0)
  
//创建数据结构体
typedef struct data{
	char type; 	      //存储交互协议
	char name[20]; 	  //用户名
	char text[128]; 
}msg;

msg recvdata;   //用于收数据
msg	snddata;    //用于发数据
 
//定义用户信息结构体
typedef struct Node{
	
	union{
		int len;        //头结点数据域
		struct sockaddr_in data;  //用户地址信息结构体
	};
	struct Node *next;  
}Linklist;

//链表头结点
Linklist *L;

int sfd = 0;
Linklist *list_create();                                   //创建链表
int list_empty(Linklist *L);                               //判空函数
struct sockaddr_in cin;                                    //存储用户信息结构体
socklen_t addr = sizeof(cin);                              //用户信息结构体的大小
void list_show(Linklist *L,msg p);                         //登录遍历链表函数
Linklist *buy_node(struct sockaddr_in e);                  //申请节点封装数据函数
int list_insert_tail(Linklist *L,struct sockaddr_in e);    //尾插函数
void list_show1(Linklist *L,msg p,struct sockaddr_in sin); //发送信息遍历链表
int list_search_value(Linklist *L,struct sockaddr_in e);   //按值进行查找函数
int list_delete_pos(Linklist *L,int pos);                  //任意位置删除
void list_show2(Linklist *L,msg p);                        //用户下线遍历链表
void *callback1(void *p);                                  //接收线程函数
void *callcback2(void *p);                                 //发送线程函数
Linklist *find_node(Linklist *L,int pos);                  //按位置查找返回结点
//接收线程
void *callback1(void *arg)   //void *arg=&sfd
{
	int sfd = *(int *)arg;
	ssize_t res = 0;
	char c = 0;
	int a = 0;
	while(1)
	{
		res = recvfrom(sfd,&recvdata,sizeof(recvdata),0,(struct sockaddr *)&cin,&addr);
		if(res < 0)
		{
			ERR_MSG("recvfrom");
			return NULL;
		}
		c = recvdata.type;
		switch(c)
		{
		case 'L':
 
			//打印信息
			printf("[%s]上线---\n",recvdata.name);
			//遍历函数发送数据
			list_show(L,recvdata);
			//添加结点
			list_insert_tail(L,cin);
			break;
		case 'C':
			//打印信息
			printf("[%s]:%s\n",recvdata.name,recvdata.text);
			//遍历转发数据
			list_show1(L,recvdata,cin);
			break;
		case 'Q':
			printf("[%s]下线\n",recvdata.name);
			//按值查找返回位置
			a = list_search_value(L,cin);
			//任意位置删除
			list_delete_pos(L,a);
			//遍历输出数据
			list_show2(L,recvdata);
			break;
		}
	}
}
 
//发送线程
void *callcback2(void *arg)  //void *arg=&sfd
{
	int sfd = *(int *)arg;
	char str[128] = "";
	snddata.type = 'C';
	strcpy(snddata.name,"system");
	while(1)
	{
		bzero(str,sizeof(str));
		fgets(str,sizeof(str),stdin);
		str[strlen(str)-1] = 0;
		strcpy(snddata.text,str);
		list_show2(L,snddata);
	}
}

int main(int argc, const char *argv[])
{	
	//创建报式套接字
	sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
 
	//允许端口快速重用--->允许程序退出后,端口号能被新的进程快速覆盖
	//必须放在bind之前
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) <0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	printf("允许端口快速重用成功\n");
 
	//填充服务器自身的地址信息结构体
	cin.sin_family 		= AF_INET;
	cin.sin_port 		= htons(PORT);
	cin.sin_addr.s_addr = inet_addr(IP);
 
	//绑定IP和端口
	if(bind(sfd,(struct sockaddr *)&cin,sizeof(cin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	//创建链表
	L = list_create();
	if(NULL == L)
	{
		printf("%d:创建失败\n",__LINE__);
		return -1;
	}	
	pthread_t tid1,tid2;
	//创建收线程callback1;
	if(pthread_create(&tid1,NULL,callback1,&sfd) < 0)
	{
		ERR_MSG("pthread_create");
		return -1;
	}
	//创建发线程callback2;
	if(pthread_create(&tid2,NULL,callcback2,&sfd) < 0)
	{
		ERR_MSG("pthread_create");
		return -1;
	}
 
	//阻塞线程
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	
    //关闭文件描述符
	close(sfd);
	return 0;
}
  
//遍历链表
void list_show(Linklist *L,msg p)
{
	//判断逻辑
	if(NULL == L || list_empty(L))
	{
		return;
	}
	//遍历逻辑
	Linklist *q = L->next; //定义指针指向第一个结点
	//puts("此时链表元素分别是:");
	while(q != NULL)
	{
		//printf("%c\t",q->data);
		if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
		{
			ERR_MSG("sendto");
			return ;
		}
		q = q->next;
	}
	//putchar(10);
}
 
//申请节点封装数据函数
Linklist *buy_node(struct sockaddr_in e)
{
	//在堆区申请一个结点的空间
	Linklist *p = (Linklist *)malloc(sizeof(Linklist));
	if(NULL == p)
	{
		puts("空间申请失败");
		return NULL;
	}
	//将数据封装到结点的数据域中
	p->data = e;
	p->next = NULL;
	return p;
}
 
//尾插函数
int list_insert_tail(Linklist *L,struct sockaddr_in e)
{
	//判断逻辑
	if(NULL == L)
	{
		puts("所给链表不合法");
		return 0;
	}
	//申请节点封装数据
	Linklist *p = buy_node(e);
	//定义遍历指针,指向最后一个结点
	Linklist *q = L;
	while(q->next != NULL)
	{
		q = q->next;
	}
	//尾插操作
	p->next = q->next;
	q->next = p;
	//表长变化
	L->len++;
	//puts("尾插成功");
	return 1;
}
//遍历链表
void list_show1(Linklist *L,msg p,struct sockaddr_in sin)
{
	//判断逻辑
	if(NULL == L || list_empty(L))
	{
		return;
	}
	//遍历逻辑
	Linklist *q = L->next; //定义指针指向第一个结点
	//puts("此时链表元素分别是:");
	while(q != NULL)
	{
		//printf("%c\t",q->data);
		if(memcmp(&q->data,&sin,sizeof(cin)) != 0)
		{
			if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
			{
				ERR_MSG("sendto");
				return ;
			}
		}
 
		q = q->next;
	}
	//putchar(10);
}
 
//按值进行查找函数
int list_search_value(Linklist *L,struct sockaddr_in e){
	//判断逻辑
	if(NULL == L || list_empty(L)){
		puts("查找失败");
		return -1;
	}
	//查找逻辑
	Linklist *q = L->next;  //从第一个结点出发
	for(int i=1;i<=L->len;i++){
		if(memcmp(&q->data,&e,sizeof(cin)) == 0){
			return i;
		}
		q = q->next; 
	}
	return 0;
}
 
//任意位置删除
int list_delete_pos(Linklist *L,int pos){
	//判断逻辑
	if(NULL == L || list_empty(L) || pos<1 || pos>L->len){
		puts("删除失败");
		return 0;
	}
	//找到要删除结点的前驱
	Linklist *q = find_node(L,pos-1);
	if(NULL == q){
		puts("删除失败");
		return 0;
	}
	//删除操作
	Linklist *p = q->next;
	q->next = p->next;
	free(p);
	p = NULL;
	//表长变化
	L->len--;
	puts("删除成功");
	return 1;
}
 
//遍历链表
void list_show2(Linklist *L,msg p)
{
	//判断逻辑
	if(NULL == L || list_empty(L))
	{
		return;
	}
 
	//遍历逻辑
	Linklist *q = L->next; //定义指针指向第一个结点
	//puts("此时链表元素分别是:");
	while(q != NULL)
	{
		//printf("%c\t",q->data);
		if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
		{
			ERR_MSG("sendto");
			return ;
		}
		q = q->next;
	}
	//putchar(10);
}
 
//创建链表
Linklist *list_create(){
	Linklist *L = (Linklist *)malloc(sizeof(Linklist));
	if(NULL == L)
{
		puts("创建失败");
		return NULL;
	}
	//创建成功进行初始化
	L->len = 0;
	L->next = NULL;
 
	puts("创建成功");
	return L;
}
 
//判空函数
int list_empty(Linklist *L)
{
	return L->next == NULL;
}
 
//按位置查找返回结点
Linklist *find_node(Linklist *L,int pos){
	//判断逻辑
	if(pos<0 || pos>L->len)
    {
		puts("查找失败");
		return NULL;
	}
	//查找逻辑
	Linklist *q=L;	//定义指针从头结点出发
	for(int i=0;i<pos;i++){
		q = q->next;
	}
	return q;  //将找到的结点返回
}

 运行结果如下:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值