栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Linux网络编程——多路复用select

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux网络编程——多路复用select

对比tcp和udp

  1. tcp面向连接可靠的通信方式
    面向连接:tcp客户端在连接服务器的时候,tcp底层会经历三次握手
    三次握手:目的就是通信双方互相确认身份
    客户端连接服务器的时候
    四次握手:tcp通信双方断开连接的时候

可靠:tcp协议支持错误重传,校验数据
不容易丢失数据包
应用场合:通信要求比较高,传输账号密码 tcp占用的带宽资源多
udp无连接的不可靠的通信方式
无连接:没有三次握手的过程
不可靠:网络状况不好的时候,容易丢失数据包
应用场合:比如视频点播,udp占用的带宽资源少

多路复用

  1. 作用:监测文件描述符的状态变化(监测文件描述符是否有数据可读写)
    状态变化: 有数据可读,有数据可写,异常
    多路复用代码的实现思路:
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 思想类似
    int sigemptyset(sigset_t *set);
    int sigfillset(sigset_t *set);
    int sigaddset(sigset_t *set, int signum);
    int sigdelset(sigset_t *set, int signum);
    int sigismember(const sigset_t *set, int signum);

有可能需要监测多个文件描述符–》多个文件描述符该如何存储–》linux中定义了一个变量类型fd_set (文件描述符集合, 专门用来存放要监测的文件描述符)
第一步:定义文件描述符集合变量
fd_set myset;
第二步:往集合中添加要监测的文件描述符
FD_ZERO(&myset);
FD_SET(你要监测的文件描述符, &myset);
第三步:调用select去监测刚才你添加的文件描述符状态
第四步(重点):判断你监测的文件描述符是否发生了状态改变
通过判断文件描述符在不在集合中即可实现
if(FD_ISSET(文件描述符,&myset)) //说明文件描述符在集合中–》说明是这个文件描述符发生了状态改变
{

                         }
  1. 相关的接口函数
    int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
    返回值:成功 >0
    失败 -1
    超时 0
    参数:nfds(重点) --》你要监测的所有的文件描述符中最大的文件描述符+1
    readfds --》我想监测文件描述符是否有数据可读
    select(最大+1,&myset,NULL,NULL) //表示我只想监测myset中的文件描述符是否有数据可读
    writefds --》我想监测文件描述符是否有数据可写
    select(最大+1,NULL,&myset,NULL) //表示我只想监测myset中的文件描述符是否有数据可写
    exceptfds --》我想监测文件描述符是否发生异常
    select(最大+1,NULL,NULL,&myset) //表示我只想监测myset中的文件描述符是否发生异常
    timeout --》超时时间,设置NULL表示永久阻塞等待
    struct timeval
    {
    tv_sec; //秒
    tv_usec; //微秒
    }
    特点:
    第一:select阻塞当前程序,直到想要监测的文件描述符发生了状态改变才解除阻塞
    第二:如果select监测的了多个文件描述符(比如:A,B,C三个),如果某个文件描述符发生了状态改变,那么select会自动将没有发生状态改变的文件描述符从集合中剔除
    也就是说:select会将发生状态改变的文件描述符保留在集合,其它的删除

    void FD_CLR(int fd, fd_set *set); //从set中把fd删除
    int FD_ISSET(int fd, fd_set *set); //判断fd在不在set集合中 返回1 在集合中 返回0 不在集合中
    void FD_SET(int fd, fd_set *set); //把fd添加都set集合中
    void FD_ZERO(fd_set *set); //清空set集合

    1. 以tcp单向通信代码为例,演示多路复用使用的套路
      客户端发送信息给服务器
      客户端使用多路复用的思路
      fd_set myset;
      FD_ZERO(&myset);
      FD_SET(键盘0, &myset); //不会写,想不清楚究竟要监测哪些文件描述符
      思考方向:根据你代码的功能去思考–》客户端需要使用键盘输入(要监测键盘)
      select(0+1,&myset,NULL,NULL,NULL);

select实现tcp单向通信

#include "myhead.h"


int main()
{
	int tcpsock;
	int newsock;
	int ret;
	char buf[100];
	
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定服务器自己的ip地址
	bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号

	struct sockaddr_in clientaddr;
	bzero(&clientaddr,sizeof(clientaddr));
	int addrsize=sizeof(clientaddr);
	
	
	//创建tcp套接字
	tcpsock=socket(AF_INET,SOCK_STREAM,0);
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!n");
		return -1;
	}
	printf("旧的文件描述符:%dn",tcpsock);
	//设置端口重复使用
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	//监听
	ret=listen(tcpsock,8);
	if(ret==-1)
	{
		perror("监听失败!n");
		return -1;
	}
	//接收客户端的连接请求
	newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
	if(newsock==-1)
	{
		perror("接收客户端的连接请求失败!n");
		return -1;
	}
	
	//定义文件描述符集合
	fd_set myset;
	FD_ZERO(&myset);
	FD_SET(newsock,&myset);
	while(1)
	{
		//调用select监测
		ret=select(newsock+1,&myset,NULL,NULL,NULL);
		if(ret>0) //说明监测到了状态改变
		{
			//进一步判断究竟是哪个文件描述符发生了状态改变
			if(FD_ISSET(newsock,&myset))
			{
				bzero(buf,100);
				read(newsock,buf,100);
				printf("客户端发送过来的信息是:%sn",buf);
			}
		}
		else 
		{
			perror("select失败!n");
			return -1;
		}
	}
	
	
}
#include "myhead.h"



int main()
{
	int tcpsock;
	int ret;
	char buf[100];
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定客户端自己的ip地址
	bindaddr.sin_port=htons(10000); //绑定客户端自己的端口号
	
	//定义结构体变量存放对方(服务器)的ip和端口号
	struct sockaddr_in serveraddr;
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr("192.168.24.3");  //服务器的ip地址
	serveraddr.sin_port=htons(10086); //服务器的端口号
	//创建tcp套接字
	tcpsock=socket(AF_INET,SOCK_STREAM,0);
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!n");
		return -1;
	}
	
	//设置端口重复使用
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	
	//连接服务器
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
	if(ret==-1)
	{
		perror("连接服务器!n");
		return -1;
	}

	//定义文件描述符集合变量
	fd_set myset;
	//添加要监测的文件描述符
	FD_ZERO(&myset);
    FD_SET(0,&myset); 
	printf("阻塞在select!n");
	while(1)
	{
		//调用select去监测   电子警察
		ret=select(0+1,&myset,NULL,NULL,NULL);
		if(ret>0) //监测成功,说明有文件描述符发生了状态变化
		{
			//进一步去判断究竟是哪个文件描述符发生了状态改变
			if(FD_ISSET(0,&myset))
			{
				printf("相信我,键盘一定有输入,不骗你的!n");
				//主动调用scanf读取键盘输入的内容
				scanf("%s",buf);
				//发送给服务器
				write(tcpsock,buf,strlen(buf));
			}
		}	
		else //失败
		{
			perror("select监测失败!n");
			return -1;
		}
	}
	
	close(tcpsock);
	return 0;
}

select实现双向通信

#include "myhead.h"


int main()
{
	int tcpsock;
	int newsock;
	int ret;
	char buf[100];
	char otherbuf[100];
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定服务器自己的ip地址
	bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号

	struct sockaddr_in clientaddr;
	bzero(&clientaddr,sizeof(clientaddr));
	int addrsize=sizeof(clientaddr);
	
	
	//创建tcp套接字
	tcpsock=socket(AF_INET,SOCK_STREAM,0);
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!n");
		return -1;
	}
	printf("旧的文件描述符:%dn",tcpsock);
	//设置端口重复使用
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	//监听
	ret=listen(tcpsock,8);
	if(ret==-1)
	{
		perror("监听失败!n");
		return -1;
	}
	//接收客户端的连接请求
	newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
	if(newsock==-1)
	{
		perror("接收客户端的连接请求失败!n");
		return -1;
	}
	
	//定义文件描述符集合
	fd_set myset;
	
	while(1)
	{
		FD_ZERO(&myset);
		FD_SET(0,&myset);
		FD_SET(newsock,&myset); //一定要放在循环中,因为select会自动剔除没有发生状态改变的文件描述符
		//调用select监测
		ret=select(newsock+1,&myset,NULL,NULL,NULL);
		if(ret>0) //说明监测到了状态改变
		{
			//进一步判断究竟是哪个文件描述符发生了状态改变
			if(FD_ISSET(newsock,&myset))
			{
				bzero(buf,100);
				read(newsock,buf,100);
				printf("客户端发送过来的信息是:%sn",buf);
			}
			//进一步判断究竟是哪个文件描述符发生了状态改变
			if(FD_ISSET(0,&myset))
			{
				bzero(otherbuf,100);
				//获取键盘输入的字符串,发送给客户端
				scanf("%s",otherbuf);
				write(newsock,otherbuf,strlen(otherbuf));
			}
		}
		else 
		{
			perror("select失败!n");
			return -1;
		}
	}
	
	
}
#include "myhead.h"



int main()
{
	int tcpsock;
	int ret;
	char buf[100];
	char otherbuf[100];
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定客户端自己的ip地址
	bindaddr.sin_port=htons(10000); //绑定客户端自己的端口号
	
	//定义结构体变量存放对方(服务器)的ip和端口号
	struct sockaddr_in serveraddr;
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr("192.168.24.3");  //服务器的ip地址
	serveraddr.sin_port=htons(10086); //服务器的端口号
	//创建tcp套接字
	tcpsock=socket(AF_INET,SOCK_STREAM,0);
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!n");
		return -1;
	}
	
	//设置端口重复使用
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	
	//连接服务器
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
	if(ret==-1)
	{
		perror("连接服务器!n");
		return -1;
	}

	//定义文件描述符集合变量
	fd_set myset;
	
	while(1)
	{
		//添加要监测的文件描述符
		FD_ZERO(&myset);
		FD_SET(0,&myset); 
		FD_SET(tcpsock,&myset);  //一定要放在循环中,因为select会自动剔除没有发生状态改变的文件描述符
		//调用select去监测   电子警察
		ret=select(tcpsock+1,&myset,NULL,NULL,NULL);
		if(ret>0) //监测成功,说明有文件描述符发生了状态变化
		{
			//进一步去判断究竟是哪个文件描述符发生了状态改变
			if(FD_ISSET(0,&myset))
			{
				printf("相信我,键盘一定有输入,不骗你的!n");
				//主动调用scanf读取键盘输入的内容
				scanf("%s",buf);
				//发送给服务器
				write(tcpsock,buf,strlen(buf));
			}
			//进一步去判断究竟是哪个文件描述符发生了状态改变
			if(FD_ISSET(tcpsock,&myset))
			{
				bzero(otherbuf,100);
				//读取服务器发送过来的信息
				read(tcpsock,otherbuf,100);
				printf("服务器发送过来的信息是:%sn",otherbuf);
			}
		}	
		else //失败
		{
			perror("select监测失败!n");
			return -1;
		}
	}
	
	close(tcpsock);
	return 0;
}

UDP组播

#include "myhead.h"

int main()
{
	char buf[100];
	int ret;
	int udpsock;
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定自己的ip地址
	bindaddr.sin_port=htons(10086); //绑定自己的端口号

	struct sockaddr_in otheraddr;
	bzero(&otheraddr,sizeof(otheraddr));
	otheraddr.sin_family=AF_INET;
	otheraddr.sin_addr.s_addr=inet_addr("225.168.24.10"); //组播专用地址 D类地址
	otheraddr.sin_port=htons(20000); //对方的端口号
	 
	//创建udp套接字
	udpsock=socket(PF_INET,SOCK_DGRAM,0);
	if(udpsock==-1)
	{
		perror("创建udp套接字!n");
		return -1;
	}
	//设置端口重复使用
	int on=1;
	setsockopt(udpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(udpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	
	//设置套接字的属性为可以广播
	setsockopt(udpsock,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
	
	//发送广播信息
	while(1)
	{
		bzero(buf,100);
		printf("请输入要发送组播的信息!n");
		scanf("%s",buf);
		sendto(udpsock,buf,strlen(buf),0,(struct sockaddr *)&otheraddr,sizeof(otheraddr));
	}
}	
	
#include "myhead.h"

int main()
{
	char buf[100];
	char ip[16]={0};
	int ret;
	int udpsock;
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定自己的ip地址
	bindaddr.sin_port=htons(20000); //绑定自己的端口号

	struct sockaddr_in otheraddr;
	bzero(&otheraddr,sizeof(otheraddr));
	int size=sizeof(otheraddr);
	 
	//创建udp套接字
	udpsock=socket(PF_INET,SOCK_DGRAM,0);
	if(udpsock==-1)
	{
		perror("创建udp套接字!n");
		return -1;
	}
	//设置端口重复使用
	int on=1;
	setsockopt(udpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(udpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	
	//加入组播组
	struct ip_mreq mreq;  //定义结构体变量存放组播地址
	bzero(&mreq, sizeof(mreq));
	mreq.imr_multiaddr.s_addr = inet_addr("225.168.24.10"); //  D类地址  
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);  // 必须用这个宏定义
	setsockopt(udpsock, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq));
	
	//接收信息
	while(1)
	{
		bzero(buf,100);
		recvfrom(udpsock,buf,100,0,(struct sockaddr *)&otheraddr,&size);
		printf("ip  %s  端口是 %hu 这个人发送的信息是:%sn",inet_ntop(AF_INET,&(otheraddr.sin_addr.s_addr),ip,sizeof(ip)),ntohs(otheraddr.sin_port),buf);
	}
}

多路复用实现TCP多路广播

#include "myhead.h"



int main()
{
	int tcpsock;
	int newsock[50]={0};  //最多可以存放50个新的客户端的套接字
	int i,j;
	int n=0; //统计目前有多少给连接成功的客户端
	int ret;
	char buf[100]; 
	
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定服务器自己的ip地址
	bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号

	struct sockaddr_in clientaddr;
	bzero(&clientaddr,sizeof(clientaddr));
	int addrsize=sizeof(clientaddr);
	
	
	//创建tcp套接字
	tcpsock=socket(AF_INET,SOCK_STREAM,0);
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!n");
		return -1;
	}
	printf("旧的文件描述符:%dn",tcpsock);
	
	//设置端口重复使用
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	//绑定ip和端口号
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	//监听
	ret=listen(tcpsock,8);
	if(ret==-1)
	{
		perror("监听失败!n");
		return -1;
	}
	
	//定义集合变量
	fd_set myset;
	int maxfd=tcpsock; //保存最大文件描述符
	while(1)
	{
		FD_ZERO(&myset);
		FD_SET(tcpsock,&myset);
		//把目前所有新的套接字添加到集合中(关键点)
		for(i=0; i0)
		{
			if(FD_ISSET(tcpsock,&myset)) //说明tcpsock发生了读就绪--》有新的客户端连接服务器
			{
				//主动调用接收客户端的连接请求
				newsock[n]=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
				if(newsock[n]==-1)
				{
					perror("接收客户端的连接请求失败!n");
					return -1;
				}
				printf("新套接字是:%dn",newsock[n]);
				
				//求集合中最大的文件描述符
				if(newsock[n]>maxfd)
					maxfd=newsock[n];
				
				n++;
			}
			//循环判断之前添加的文件描述符在不在集合中
			for(i=0; i
				if(FD_ISSET(newsock[i],&myset)) //说明新的套接字发生了读就绪--》有新的客户端发送信息给服务器了
				{
					bzero(buf,100);
					//接收客户端的信息
					recv(newsock[i],buf,100,0);
					printf("服务器接收的信息是:%sn",buf);
					//转发给其它所有的客户端
					for(j=0; j
						send(newsock[j],buf,strlen(buf),0);
					}
				}
			}
		}
		else{
			perror("select失败!n");
			return -1;
		}
	}
	
}在这里插入代码片
#include "myhead.h"



int main()
{
	int tcpsock;
	int ret;
	char buf[100];
	char otherbuf[100];
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定客户端自己的ip地址
	bindaddr.sin_port=htons(20000); //绑定客户端自己的端口号
	
	//定义结构体变量存放对方(服务器)的ip和端口号
	struct sockaddr_in serveraddr;
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr("192.168.24.3");  //服务器的ip地址
	serveraddr.sin_port=htons(10086); //服务器的端口号
	//创建tcp套接字
	tcpsock=socket(AF_INET,SOCK_STREAM,0);
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!n");
		return -1;
	}
	
	//设置端口重复使用
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	
	//连接服务器
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
	if(ret==-1)
	{
		perror("连接服务器!n");
		return -1;
	}

	//定义文件描述符集合变量
	fd_set myset;
	
	while(1)
	{
		//添加要监测的文件描述符
		FD_ZERO(&myset);
		FD_SET(0,&myset); 
		FD_SET(tcpsock,&myset);  //一定要放在循环中,因为select会自动剔除没有发生状态改变的文件描述符
		//调用select去监测   电子警察
		ret=select(tcpsock+1,&myset,NULL,NULL,NULL);
		if(ret>0) //监测成功,说明有文件描述符发生了状态变化
		{
			//进一步去判断究竟是哪个文件描述符发生了状态改变
			if(FD_ISSET(0,&myset))
			{
				printf("相信我,键盘一定有输入,不骗你的!n");
				//主动调用scanf读取键盘输入的内容
				scanf("%s",buf);
				//发送给服务器
				write(tcpsock,buf,strlen(buf));
			}
			//进一步去判断究竟是哪个文件描述符发生了状态改变
			if(FD_ISSET(tcpsock,&myset))
			{
				bzero(otherbuf,100);
				//读取服务器发送过来的信息
				read(tcpsock,otherbuf,100);
				printf("服务器发送过来的信息是:%sn",otherbuf);
			}
		}	
		else //失败
		{
			perror("select监测失败!n");
			return -1;
		}
	}
	
	close(tcpsock);
	return 0;
}

TCP广播

#include "myhead.h"


pthread_attr_t myattr;
void fun(int num)
{
	pthread_attr_destroy(&myattr);
	exit(0);
}
//定义内核链表
struct clientlist
{
	char ip[16]; //存放ip
	unsigned short someport; //存放端口号
	int sock; //存放套接字
	struct list_head mypoint;
};

struct clientlist *myhead;
//初始化链表头
struct clientlist *init_list()
{
	struct clientlist *head=malloc(sizeof(struct clientlist));
	INIT_LIST_HEAD(&(head->mypoint));
	return head;
}
void *routine(void *arg)
{
	int ret;
	char msg[100];
	char buf[100];
	struct clientlist *p=(struct clientlist *)arg;
	struct clientlist *pos,*n;
	char allmsg[2048]={0};
	while(1)
	{
		bzero(buf,100);
		bzero(msg,100);
		bzero(allmsg,2048);
		ret=read(p->sock,buf,100); //接收某个客户端发送的信息
		if(ret==0) //说明客户端退出
		{
			//删除断开的客户端
			list_for_each_entry_safe(pos, n,&(myhead->mypoint),mypoint)
			{
				if(pos->sock==p->sock)
					list_del(&(pos->mypoint));
			}
			break;
		}
		printf("客户端%s,端口号:%hu  发送过来的信息是:%sn",p->ip,p->someport,buf);
		//判断收到的信息是不是getlist
		if(strcmp(buf,"getlist")==0)
		{
			//遍历链表,把链表中存放的信息拼接一起发送给客户端
			list_for_each_entry(pos,&(myhead->mypoint),mypoint)
			{
				sprintf(allmsg+strlen(allmsg),"%s@%hu@",pos->ip,pos->someport);
			}
			//发送出去
			write(p->sock,allmsg,strlen(allmsg));
			continue;
		}
		
		
		//切割字符串,判断究竟是群发还是点播
		char *q=strtok(buf,":");  //all:hello或者20000:hello
		strcpy(msg,strtok(NULL,":"));
		if(strcmp(q,"all")==0) //群发
		{
			//遍历链表转发出去
			list_for_each_entry(pos,&(myhead->mypoint),mypoint)
			{
				//排除自己
				if(pos->sock!=p->sock)
					write(pos->sock,msg,strlen(msg));
			}
		}
		else //点播
		{
			//遍历链表找到要点播的对象
			list_for_each_entry(pos,&(myhead->mypoint),mypoint)
			{
				if(pos->someport==atoi(q))
					write(pos->sock,msg,strlen(msg));
			}
		}
		
		
	}
	pthread_exit(NULL);
}
int main()
{
	signal(SIGINT,fun);
	pthread_t id;
	int tcpsock;
	int newsock;
	int ret;
	char buf[100];
	
	//初始化表头
	myhead=init_list(); 
	
	//定义线程属性变量并初始化
	pthread_attr_init(&myattr);
	
	//调用你想设置的属性对应的接口函数
	pthread_attr_setdetachstate(&myattr,PTHREAD_CREATE_DETACHED);
	
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定服务器自己的ip地址
	bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号

	struct sockaddr_in clientaddr;
	bzero(&clientaddr,sizeof(clientaddr));
	int addrsize=sizeof(clientaddr);
	
	
	//创建tcp套接字
	tcpsock=socket(AF_INET,SOCK_STREAM,0);
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!n");
		return -1;
	}
	printf("旧的文件描述符:%dn",tcpsock);
	//设置端口重复使用
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	//监听
	ret=listen(tcpsock,8);
	if(ret==-1)
	{
		perror("监听失败!n");
		return -1;
	}
	while(1)
	{
		//接收客户端的连接请求
		newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
		if(newsock==-1)
		{
			perror("接收客户端的连接请求失败!n");
			return -1;
		}
		printf("有新的客户端连接成功了,新的文件描述符:%dn",newsock);
		printf("有新的客户端连接成功了,他的IP地址是:%sn",inet_ntoa(clientaddr.sin_addr));
		printf("有新的客户端连接成功了,他的端口号是:%hun",ntohs(clientaddr.sin_port));
		//把文件描述符存放到链表中
		struct clientlist *newnode=malloc(sizeof(struct clientlist));
		newnode->sock=newsock;
		strcpy(newnode->ip,inet_ntoa(clientaddr.sin_addr));
		newnode->someport=ntohs(clientaddr.sin_port);
		INIT_LIST_HEAD(&(newnode->mypoint));
		//插入到链表的尾部
		list_add_tail(&(newnode->mypoint),&(myhead->mypoint));
		
		//立马新建线程接收这个客户端发送的信息
		pthread_create(&id,&myattr,routine,newnode);
	}
	
	
}
#include "myhead.h"


int tcpsock;
void *routine(void *arg)
{
	char buf[2048];
	int ret;
	while(1)
	{
		bzero(buf,2048);
		ret=read(tcpsock,buf,2048);
		if(ret==0) //说明服务器断开连接
		{
			printf("服务器挂了!n");
			exit(-1); //结束整个进程
		}
		printf("服务器转发过来的信息是:%sn",buf);
	}
}
int main()
{
	pthread_t id;
	int ret;
	char buf[100];
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定客户端自己的ip地址
	bindaddr.sin_port=htons(10000); //绑定客户端自己的端口号
	
	//定义结构体变量存放对方(服务器)的ip和端口号
	struct sockaddr_in serveraddr;
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr("192.168.24.3");  //服务器的ip地址
	serveraddr.sin_port=htons(10086); //服务器的端口号
	//创建tcp套接字
	tcpsock=socket(AF_INET,SOCK_STREAM,0);
	if(tcpsock==-1)
	{
		perror("创建tcp套接字!n");
		return -1;
	}
	
	//设置端口重复使用
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	
	//连接服务器
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
	if(ret==-1)
	{
		perror("连接服务器!n");
		return -1;
	}
	//创建线程专门接收服务器的信息
	pthread_create(&id,NULL,routine,NULL);
	//发送信息给服务器
	while(1)
	{
		bzero(buf,100);
		printf("请输入要发送给服务器的信息,按照如下格式输入!n");
		printf("getlist 表示你想获取服务器上客户端的信息!n");
		printf("all:真实的信息--》群发!n");
		printf("端口号:真实的信息--》点播!n");
		scanf("%s",buf);
		write(tcpsock,buf,strlen(buf));
	}
	close(tcpsock);
	return 0;
}

UDP发送接收

#include "myhead.h"

int main()
{
	char buf[100];
	char ip[16]={0};
	int ret;
	int udpsock;
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定自己的ip地址
	bindaddr.sin_port=htons(20000); //绑定自己的端口号

	struct sockaddr_in otheraddr;
	bzero(&otheraddr,sizeof(otheraddr));
	int size=sizeof(otheraddr);
	 
	//创建udp套接字
	udpsock=socket(PF_INET,SOCK_DGRAM,0);
	if(udpsock==-1)
	{
		perror("创建udp套接字!n");
		return -1;
	}
	//设置端口重复使用
	int on=1;
	setsockopt(udpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(udpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	
	//接收信息
	bzero(buf,100);
	recvfrom(udpsock,buf,100,0,(struct sockaddr *)&otheraddr,&size);
	printf("ip  %s  端口是 %hu 这个人发送的信息是:%sn",inet_ntop(AF_INET,&(otheraddr.sin_addr.s_addr),ip,sizeof(ip)),ntohs(otheraddr.sin_port),buf);
	
	//回复I get
	sendto(udpsock,"I get",strlen("I get"),0,(struct sockaddr *)&otheraddr,sizeof(otheraddr));
}
#include "myhead.h"

int main()
{
	char buf[100];
	int ret;
	char ip[16]={0};
	int udpsock;
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //绑定自己的ip地址
	bindaddr.sin_port=htons(10086); //绑定自己的端口号

	struct sockaddr_in otheraddr;
	bzero(&otheraddr,sizeof(otheraddr));
	otheraddr.sin_family=AF_INET;
	otheraddr.sin_addr.s_addr=inet_addr("192.168.24.255"); //广播专用地址
	otheraddr.sin_port=htons(20000); //对方的端口号
	 
	struct sockaddr_in addr;
	bzero(&addr,sizeof(addr));
	int size=sizeof(addr);
	//创建udp套接字
	udpsock=socket(PF_INET,SOCK_DGRAM,0);
	if(udpsock==-1)
	{
		perror("创建udp套接字!n");
		return -1;
	}
	//设置端口重复使用
	int on=1;
	setsockopt(udpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号
	ret=bind(udpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定失败!n");
		return -1;
	}
	
	//设置套接字的属性为可以广播
	setsockopt(udpsock,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
	
	//发送广播信息
	sendto(udpsock,"I am coming!",strlen("I am coming!"),0,(struct sockaddr *)&otheraddr,sizeof(otheraddr));
	
	//接收信息
	while(1)
	{
		bzero(buf,100);
		recvfrom(udpsock,buf,100,0,(struct sockaddr *)&addr,&size);
		printf("ip  %s  端口是 %hu 回复了信息给我:%sn",inet_ntop(AF_INET,&(addr.sin_addr.s_addr),ip,sizeof(ip)),ntohs(addr.sin_port),buf);
	}
	
	return 0;
}	
	

select设置超时

#include "myhead.h"


int main()
{
	int ret;
	//定义超时时间
	struct timeval mytime;
	bzero(&mytime,sizeof(mytime));
	mytime.tv_sec=5; //5秒超时时间
	//定义文件描述符集合变量
	fd_set myset;
	//添加要监测的文件描述符
	FD_ZERO(&myset);
    FD_SET(0,&myset); 
	printf("阻塞在select!n");
	//调用select去监测   电子警察
	ret=select(0+1,&myset,NULL,NULL,&mytime);
	if(ret>0) //监测成功,说明有文件描述符发生了状态变化
	{
		//进一步去判断究竟是哪个文件描述符发生了状态改变
		if(FD_ISSET(0,&myset))
		{
			printf("相信我,键盘一定有输入,不骗你的!n");
		}
	}
	else if(ret==0)
	{
		printf("恭喜你,5秒到了,不给你输入!n");
		return -1;
	}	
	else
	{
		perror("select监测失败!n");
		return -1;
	}
	return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1004004.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号