TCP广播与点播简单模拟

更多资料请点击:我的目录
本篇仅用于记录自己所学知识及应用,代码仍可优化,仅供参考,如果发现有错误的地方,尽管留言于我,谢谢。

任务要求:
①链表分别存储不同客户端产生的新套接字;
②群发功能(广播),客户端A发消息,其他所有客户端都能收到A的消息,如果客户端A断开连接,所有其他客户端收到A的下线通知,聊天仍然继续,需要删除客户端A的信息;
③服务器断开,所有在线客户端收到“服务器出错”提示,并要求所有在线客户端都退出;
④私聊功能(点播),客户端A以 “IP:端口号:消息”格式发送消息,服务器收到消息先进行判断IP号、端口号对应的是哪个客户端,在进行对应消息的发送。
例如:客户端B IP: 123456 端口号:8848
客户端A向服务器发送:123456:8848:jiajiage
那么结果是所有客户端中,只有客户端B收到:jiajiage

示例效果:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

具体代码如下:

服务器部分:

/*
	服务器部分:
*/

#include "myhead.h"

int tcpsock;
int newsock;
struct node *list;

void finish()
{
	struct node *p = list->next;
	while(p != list)													//循环读取每个节点
	{	
		write(p->data,"quit",4);										//给q发送退出信息	
		p = p->next;													//下一节点
	}
	close(tcpsock);
	close(newsock);
	exit(0);
}
struct arg																//定义结构体存储文件描述符
{
	struct node *list1;													//存放所有服务器的链表
	struct node *p1;													//存放新连接的服务器节点
};

void *transfer(void *arg)
{
	
	struct arg *arg1 = arg;
	struct node *head = arg1->list1;
	struct node *q = arg1->p1;
	
	struct node *p ;
	struct node *p1 ;
	char buf[100] = {0};												//存放客户端发送的消息
	char *to_ip;															//存放客户端发送给指定客户端的ip地址
	int to_port;														//存放客户端发送给指定客户端的端口号
	char to_buf[100] = {0};												//存放客户端发送给指定客户端的消息
	
	while(1)
	{	
		p = head->next;
		
		bzero(&buf,100);
		if(read(q->data,buf,100))										//q客户没有断开
		
		while(p!=head)													//循环读取每个节点
		{
			if(p->data != q->data )										//判断是否为p节点
			{
				if(strstr(buf,":") != NULL)								//判断是否指定接收的客户端
				{
					to_ip = (strtok(buf,":"));							//获取发送给指定客户端的ip地址
					to_port = atoi(strtok(NULL,":"));					//获取发送给指定客户端的端口号
					sprintf(to_buf,"%s",(strtok(NULL,":")));			//获取发送给指定客户端的消息
					
					p1 = head->next;									
					while(p1 != head)									//新的循环
					{
						if(strstr(p1->ip,to_ip)!=NULL 					//比较ip
							&& p1->port == to_port)						//比较端口号
						{
							printf("%d receive: %s\n",p1->data,to_buf);	//输出q的文件描述符
							char newbuf[110] = {0};	
							sprintf(newbuf,"%d:  %s",q->data,to_buf);	
							write(p1->data,newbuf,strlen(newbuf));		//给指定客户端发送p的信息
							break;										//退出此次while循环
						}
						p1 = p1->next;									//下一个节点
					}
					break;												//退出此次循环
				}

				
				if(strstr(buf,":") == NULL )							//判断是否指定接收的客户端
				{
					printf("%d receive: %s\n",p->data,buf);				//输出q的文件描述符
					char newbuf[110] = {0};
					sprintf(newbuf,"%d:  %s",q->data,buf);
					write(p->data,newbuf,strlen(newbuf));				//给q发送p的信息
				}
			}
			p = p->next;												//下一节点
		}
	}
}

int main()
{
	int ret;
	struct sockaddr_in bindaddr;										//定义ipv4地址结构体变量
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=inet_addr("192.168.23.26");  				//绑定服务器自己的ip地址
	bindaddr.sin_port=htons(10086); 									//绑定服务器自己的端口号

	struct sockaddr_in clientaddr;
	bzero(&clientaddr,sizeof(clientaddr));
	int addrsize=sizeof(clientaddr);
	
	tcpsock=socket(AF_INET,SOCK_STREAM,0);								//创建tcp套接字
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!\n");
		return -1;
	}
	
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));	//绑定ip和端口号
	if(ret==-1)
	{
		perror("绑定失败!\n");
		return -1;
	}
	//监听
	ret=listen(tcpsock,8);
	if(ret==-1)
	{
		perror("监听失败!\n");
		return -1;
	}
	
	list = list_init();
	signal(SIGINT,finish);
	int i = 0;
	while(1)
	{
		newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);//接收客户端的连接请求
		if(newsock==-1)
		{
			perror("接收客户端的连接请求失败!\n");
			return -1;
		}
		printf("ip == %s\n",inet_ntoa(clientaddr.sin_addr));
		printf("端口号 == %d\n",ntohs(clientaddr.sin_port));
		
		struct node *new = malloc(sizeof(struct node));					//创建新节点
					
		new = newnode(newsock,											//新节点存放新的文件描述符
						inet_ntoa(clientaddr.sin_addr),					//存放新连接的服务器的ip地址
						ntohs(clientaddr.sin_port));						
						
		addnode(new,list);												//插入新节点
		printf("新的文件描述符:%d\n",newsock);
		
		struct arg *arg1 = malloc(sizeof(struct arg));					//定义结构体存放参数,传给新线程任务函数
		arg1->list1 = list;												
		arg1->p1 = new;

		pthread_t id[i];
		pthread_create(&id[i],NULL,&transfer,arg1);
		i++;
	}
}

客户端1部分:

/*
	客户端1部分:
*/

#include "myhead.h"

int tcpsock;	
pthread_t id;
	
void *fun()									
{
	char recv[100];
	while(1)
	{
		bzero(recv,100);
		if(read(tcpsock,recv,100))											//接收服务器发送的消息
		{
			if(strstr(recv,"quit") != NULL)									//判断时候收到“quit”(服务器中断)
			{	
				printf("服务器出错!\n");
				close(tcpsock);
				exit(0);
			}
			printf("receive %s\n",recv);
		}
	}
}
	
void finish()
{
	write(tcpsock,"sign out!",9);
	close(tcpsock);
	pthread_cancel(id);
	exit(0);
}
	
int main()
{
	int ret;
	char buf[100];
	
	struct sockaddr_in bindaddr;											//定义ipv4地址结构体变量
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=inet_addr("192.168.23.26"); 	 				//绑定客户端自己的ip地址
	bindaddr.sin_port=htons(10000); 										//绑定客户端自己的端口号
	
	struct sockaddr_in serveraddr;											//定义结构体变量存放对方(服务器)的ip和端口号
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr("192.168.23.26");  				//服务器的ip地址
	serveraddr.sin_port=htons(10086); 										//服务器的端口号
	
	tcpsock=socket(AF_INET,SOCK_STREAM,0);									//创建tcp套接字
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!\n");
		return -1;
	}
	
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));		//绑定ip和端口号
	if(ret==-1)
	{
		perror("绑定失败!\n");
		return -1;
	}
	
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));	//连接服务器
	if(ret==-1)
	{
		perror("连接服务器!\n");
		return -1;
	}
	
	signal(SIGINT,finish);
	
	pthread_create(&id,NULL,&fun,NULL);
	
	printf("请输入要发送给服务器的信息!\n");								//发送信息给服务器
	while(1)
	{
		bzero(buf,100);
		scanf("%s",buf);
		if(strstr(buf,"quit")!=NULL)
		{
			finish();
		}
		write(tcpsock,buf,strlen(buf));
	}
	close(tcpsock);
	return 0;
}

客户端2部分:

/*
	客户端1部分:
*/

#include "myhead.h"

int tcpsock;	
pthread_t id;
	
void *fun()									
{
	char recv[100];
	while(1)
	{
		bzero(recv,100);
		if(read(tcpsock,recv,100))											//接收服务器发送的消息
		{
			if(strstr(recv,"quit") != NULL)									//判断时候收到“quit”(服务器中断)
			{	
				printf("服务器出错!\n");
				close(tcpsock);
				exit(0);
			}
			printf("receive %s\n",recv);
		}
	}
}
	
void finish()
{
	write(tcpsock,"sign out!",9);
	close(tcpsock);
	pthread_cancel(id);
	exit(0);
}
	
int main()
{
	int ret;
	char buf[100];
	
	struct sockaddr_in bindaddr;											//定义ipv4地址结构体变量
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=inet_addr("192.168.23.26"); 	 				//绑定客户端自己的ip地址
	bindaddr.sin_port=htons(20000); 										//绑定客户端自己的端口号
	
	struct sockaddr_in serveraddr;											//定义结构体变量存放对方(服务器)的ip和端口号
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr("192.168.23.26");  				//服务器的ip地址
	serveraddr.sin_port=htons(10086); 										//服务器的端口号
	
	tcpsock=socket(AF_INET,SOCK_STREAM,0);									//创建tcp套接字
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!\n");
		return -1;
	}
	
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));		//绑定ip和端口号
	if(ret==-1)
	{
		perror("绑定失败!\n");
		return -1;
	}
	
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));	//连接服务器
	if(ret==-1)
	{
		perror("连接服务器!\n");
		return -1;
	}
	
	signal(SIGINT,finish);
	
	pthread_create(&id,NULL,&fun,NULL);
	
	printf("请输入要发送给服务器的信息!\n");								//发送信息给服务器
	while(1)
	{
		bzero(buf,100);
		scanf("%s",buf);
		if(strstr(buf,"quit")!=NULL)
		{
			finish();
		}
		write(tcpsock,buf,strlen(buf));
	}
	close(tcpsock);
	return 0;
}

客户端3部分:

/*
	客户端1部分:
*/

#include "myhead.h"

int tcpsock;	
pthread_t id;
	
void *fun()									
{
	char recv[100];
	while(1)
	{
		bzero(recv,100);
		if(read(tcpsock,recv,100))											//接收服务器发送的消息
		{
			if(strstr(recv,"quit") != NULL)									//判断时候收到“quit”(服务器中断)
			{	
				printf("服务器出错!\n");
				close(tcpsock);
				exit(0);
			}
			printf("receive %s\n",recv);
		}
	}
}
	
void finish()
{
	write(tcpsock,"sign out!",9);
	close(tcpsock);
	pthread_cancel(id);
	exit(0);
}
	
int main()
{
	int ret;
	char buf[100];
	
	struct sockaddr_in bindaddr;											//定义ipv4地址结构体变量
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=inet_addr("192.168.23.26"); 	 				//绑定客户端自己的ip地址
	bindaddr.sin_port=htons(30000); 										//绑定客户端自己的端口号
	
	struct sockaddr_in serveraddr;											//定义结构体变量存放对方(服务器)的ip和端口号
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr("192.168.23.26");  				//服务器的ip地址
	serveraddr.sin_port=htons(10086); 										//服务器的端口号
	
	tcpsock=socket(AF_INET,SOCK_STREAM,0);									//创建tcp套接字
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!\n");
		return -1;
	}
	
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));		//绑定ip和端口号
	if(ret==-1)
	{
		perror("绑定失败!\n");
		return -1;
	}
	
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));	//连接服务器
	if(ret==-1)
	{
		perror("连接服务器!\n");
		return -1;
	}
	
	signal(SIGINT,finish);
	
	pthread_create(&id,NULL,&fun,NULL);
	
	printf("请输入要发送给服务器的信息!\n");								//发送信息给服务器
	while(1)
	{
		bzero(buf,100);
		scanf("%s",buf);
		if(strstr(buf,"quit")!=NULL)
		{
			finish();
		}
		write(tcpsock,buf,strlen(buf));
	}
	close(tcpsock);
	return 0;
}

头文件部分:

#ifndef _MY_HEAD_H
#define	_MY_HEAD_H

#include <stdio.h>	
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <linux/input.h>
#include <errno.h>
#include <dirent.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <error.h> 
#include <strings.h>
#include <pthread.h>
#include "list.h"

#endif

链表部分:

#ifndef _LIST_H
#define _LIST_H

//设置节点
struct node
{
	char *ip;								//节点存放IP地址
	int port;								//节点存放端口号
	int data;
	struct node *next;
	struct node *prev;
};

//初始化链表
struct node *list_init()
{
	struct node *head = malloc(sizeof(struct node));
	head->next = head;
	head->prev = head;
	return head;
}

//创建新节点
struct node *newnode(int newdata,char *IP, int port)
{
	struct node *new = malloc(sizeof(struct node));
	new->ip = IP;
	new->port = port;
	new->data = newdata;
	new->next = NULL;
	new->prev = NULL;
}

//加入新节点
int addnode(struct node *new,struct node *list)
{
	struct node *p = list;
	while(p->next != list)
	{
		p = p->next;
	}
	new->prev = list->prev;
	new->next = list;
	list->prev = new;
	new->prev->next = new;
}

//删除节点
int delnode(struct node *q, struct node *head)
{
	struct node *p = head;
	struct node *p1 = NULL;
	while(p->next != head)
	{
		if(p == q)
		{
			p1 = p->prev;
			p->prev->next = p->next;
			p->next->prev = p->prev;
			p->next = NULL;
			p->prev = NULL;
			free(p);
			p = p1;
		}
		p = p->next;
	}
	
	if(p->next == head && p == q)
	{
		p->prev->next = head;
		head->prev = p->prev;
		p->next = NULL;
		p->prev = NULL;
	}
}
#endif
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

佳佳鸽

若文章帮到你,能不能请我喝杯茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值