Linux——socket高级通信 select socket选项 Http

回顾:
    多进程的问题:数据共享。
    多进程的问题:进程的上下文环境(context)不能共享
    文件描述符号是整数以及对应上下文环境
    多进程的问题:上下文环境共享

一.SELECT TCP服务器编程模式
    1.select函数
        int select(
            int fds,//建议是监控的文件描述符号的最大值+1
            fd_set *readfds,//读文件描述符号集合
                    //该参数既是输入,也是输出
                    //输入:被监控的描述符号
                    //输出:有数据的描述符号
            fd_set *writefds,//
            fd_set *errfds,
            struct timeval*timeout);//指定阻塞时间限制
                                                            //为NULL,永久
        返回:
            >0:发生改变的文件描述符号个数
            =0:时间限制过期
            =-1:异常                                                            
    2.IO能否发出信号?异步IO就是通过信号工作.

    3.应用使用select

select.c

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/select.h>
main()
{	
	fd_set fds;
	int r;
	char buf[100];
	while(1)
	{
		FD_ZERO(&fds);//清空
		FD_SET(0,&fds);//加入读描述符
		r=select(1,&fds,0,0,0);
		printf("有数据输入!\n");
		r=read(0,buf,99);//只要0不读取 再次循环到select就返回 没有数据就阻塞
	}
}


    4.使用select实现TCP的多客户连接与处理

chatServer.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
main()
{
	int sfd;//服务器描述符号
	int fdall[100];//客户描述符号
	int count;//客户个数
	int r;//返回值(异常处理)
	struct sockaddr_in dr;//IP地址与端口
	fd_set fds;//被select监控的描述符号集合
	int maxfd;//最大文件描述符号
	int i,j;//循环变量
	char buf[1024];//客户聊天数据
	
	//1.建立socket
	sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd==-1) printf("1:%m\n"),exit(-1);
	printf("socket ok!\n");
	
	
	//2.绑定地址与端口
	dr.sin_family=AF_INET;
	dr.sin_port=htons(8866);
	inet_aton("192.168.1.116",&dr.sin_addr);
	r=bind(sfd,(struct sockaddr*)&dr,sizeof(dr));
	if(r==-1) printf("2:%m\n"),close(sfd),exit(-1);
	printf("bind ok!\n");
	
	
	//3.监听
	r=listen(sfd,10);
	if(r==-1) printf("3:%m\n"),close(sfd),exit(-1);
	printf("listen ok!\n");
	
	//初始化
	count=0;
	maxfd=0;
	FD_ZERO(&fds);//清空
	for(i=0;i<100;i++)
	{
		fdall[i]=-1;
	}
	while(1)
	{
		//4.构造监听的描述符号集合
		//4.1.清空
		FD_ZERO(&fds);
		maxfd=0;
		//4.2.加入服务器描述符号(服务器描述符号(有没有客户连接),客户代理描述符号(有无数据发送))
		FD_SET(sfd,&fds);
		maxfd=maxfd>=sfd?maxfd:sfd;
		//4.3.加入客户描述符号
		for(i=0;i<count;i++)
		{
			if(fdall[i]!=-1)
			{
				FD_SET(fdall[i],&fds);
				maxfd=maxfd>=fdall[i]?maxfd:fdall[i];
			}
		}
		//5.使用select选出改变的描述符号 
		 r=select(maxfd+1,&fds,0,0,0);
		if(r==-1) 
		{
			 printf("服务器崩溃!\n");
			 break;
		}		
		//6.分两种情况处理:		
		//6.1.有客户连接:服务器描述符号
		if(FD_ISSET(sfd,&fds))
		{
			fdall[count]=accept(sfd,0,0);
			if(fdall[count]==-1)
			{
				 printf("服务器崩溃!\n");
				//释放所有客户
				break;
			}
			printf("有客户连接!\n");
			count++;
		}
		//6.2.有客户发送数据:客户描述符号
		for(i=0;i<count;i++)
		{
			//判定改变描述符号是否存在
			 if( fdall[i]!=-1 &&
				FD_ISSET(fdall[i],&fds))
			{
				//读取数据
				r=recv(fdall[i],buf,1023,0);
				if(r==0){
					printf("有客户退出!\n");
					close(fdall[i]);
					fdall[i]=-1;
				}
				if(r==-1){
					printf("网络故障!\n");
					close(fdall[i]);
					fdall[i]=-1;
				}
				if(r>0)
				{
					 //广播数据
					buf[r]=0;
					printf("广播数据:%s\n",buf);
					for(j=0;j<count;j++)
					{
						if(fdall[j]!=-1)
						{
							send(fdall[j],buf,r,0);
						}
					}
				}
			}
		}
	}
}
    5.poll模式
        int poll(
                struct pollfd *fds,//监控的描述符号
                int nfds,//监控的描述符号的个数

                int timeout    );    //阻塞超时

poll.c

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/poll.h>
main()
{	
	struct pollfd fds[1];
	int r;
	char buf[100];
	
	fds[0].fd=0;
	fds[0].events=POLLIN;
	while(1)
	{
		
		r=poll(fds,1,-1);
		if(fds[0].revents & POLLIN)
		{
			printf("有数据输入!\n");
			r=read(0,buf,99);
		}
	}
}


            
二.Socket选项设置
        1.socket有哪些选项可以设置 man 7 socket
                    ARP
                     |
                    IP
                     |
            |-----------------|
         UDP              TCP             
        通用选项:
        SOL_SOCKET 选项
                        SO_BROADCAST        广播  这些都是选项名
                        SO_RCVBUF                描述符号的缓冲的大小
                        SO_SNDBUF                描述符号的缓冲的大小
                        SO_REUSEADDR        地址反复绑定
                        SO_TYPE                    描述符号类型SOCK_STREAM SOCK_DGRAM?
        ICMP选项
                IPPTOTO_ICMP
                        ICMP_FILTER
        IP选项(干预系统生成IP头)
                IPPROTO_IP
                        ......
                        ......
        UDP选项
                IPPROTO_UDP
                        ......
        
        TCP选项                
                IPPROTO_TCP
                        ......                        

         setsockopt设置选项

         getsockopt获取选项

                 int getsockopt(int sockfd,//文件描述符
                                         int level,//哪个选项
                                         int optname,//选项名
                                         void *optval,//返回选项值
                                         socklen_t *optlen);//选项值的长度 

       
案例:

        判定一个socket的数据类型AF_INET:SOCK_STREAM     SOCK_DGRAM SOCK_RAW

socketopt.c

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>

main()
{
	int fd;
	int type;
	int len;
	len=sizeof(type);
	
	fd=socket(AF_INET,SOCK_DGRAM,0);
	getsockopt(fd,SOL_SOCKET,SO_RCVBUF,&type,&len);//缓冲大小
	printf("缓冲大小:%u\n",type);
	/*
	 getsockopt(fd,SOL_SOCKET,SO_TYPE,&type,&len);
	
	printf("%u:%u\n",SOCK_STREAM,type);
	if(type & SOCK_STREAM)
	{
		printf("流!\n");
	}
	if(type & SOCK_DGRAM)
	{
		printf("报文!\n");
	}
	*/
}



案例:
        使用选项进行数据广播.
        cast_A发送
                建立socket
                设置广播选项

                发送数据(广播方式发送)

cast_A.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
	int fd;
	int opt=1;
	int r;
	struct sockaddr_in dr;
	
	//1.选项设置
	fd=socket(PF_INET,SOCK_DGRAM,0);
	if(fd==-1) printf("1:%m\n"),exit(-1);
	
	r=setsockopt(fd,SOL_SOCKET,SO_BROADCAST,
				&opt,sizeof(opt));
	if(r==-1) printf("2:%m\n"),exit(-1);
	dr.sin_family=AF_INET;
	dr.sin_port=htons(9999);
	//2.使用广播IP地址
	dr.sin_addr.s_addr=inet_addr("192.168.1.255");//广播地址
	
	r=sendto(fd,"Hello",5,0,
		(struct sockaddr*)&dr,sizeof(dr));
	if(fd==-1) printf("3:%m\n");
	
	close(fd);
}


                
        case_B接收
                建立socket
                设置地址可重用选项
                绑定地址

                接收数据

cast_B.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
	int fd;
	int opt=1;
	char buf[100];
	int r;
	struct sockaddr_in dr;
	
	fd=socket(PF_INET,SOCK_DGRAM,0);
	if(fd==-1) printf("1:%m\n"),exit(-1);
	//1.选项
	r=setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,
				&opt,sizeof(opt));
	if(r==-1) printf("2:%m\n"),exit(-1);
	
	dr.sin_family=AF_INET;
	dr.sin_port=htons(9999);
	//2.广播地址
	dr.sin_addr.s_addr=inet_addr("192.168.1.255");
	
	r=bind(fd,(struct sockaddr*)&dr,sizeof(dr));
	if(r==-1) printf("3:%m\n"),exit(-1);
	
	r=recv(fd,buf,100,0);//没有信息时会等待
	if(r>0)
	{
		buf[r]=0;
		printf("广播数据:%s\n",buf);
	}
	close(fd);
	
}
             
三.OOB数据(TCP)        
        优先(带外)数据
        send(,MSG_OOB);
        recv(,MSG_OOB);

案例:

oob_server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <fcntl.h>
int fd,cfd;//要在一个级别
void handle(int s)
{

	char data[100];
	int r;
	if(s==SIGURG)
	{
		r=recv(cfd,data,100,MSG_OOB);		
	
		data[r]=0;
		printf("$$%s\n",data);
	}
}
main()
{
	
	int opt=1;
	char buf[100];
	int r;
	struct sockaddr_in dr;
	
	fd=socket(PF_INET,SOCK_STREAM,0);
	if(fd==-1) printf("1:%m\n"),exit(-1);
	printf("1\n");
	dr.sin_family=AF_INET;
	dr.sin_port=htons(10000);	
	dr.sin_addr.s_addr=inet_addr("192.168.1.116");
	
	r=bind(fd,(struct sockaddr*)&dr,sizeof(dr));
	if(r==-1) printf("2:%m\n"),exit(-1);
	printf("2\n");
	r=listen(fd,10);
	if(r==-1) printf("3:%m\n"),exit(-1);
	printf("3\n");
	signal(SIGURG,handle);
	cfd=accept(fd,0,0);
	fcntl(cfd,F_SETOWN,getpid());//带外数据将发送信号发送到本进程 默认是系统根进程
	if(cfd==-1) printf("4:%m\n"),exit(-1);
	printf("4\n");
	while(1)
	{

		r=recv(cfd,buf,100,0/*MSG_OOB*/);//如果接收到带外数据则发送个信号SIGURG
		if(r>0)
		{
			buf[r]=0;
			printf("接收数据:%s\n",buf);
		}		
		else
		{
			break;
		}		
	}
	close(cfd);
	close(fd);
	
}



                  

oob_client.c 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
	int fd;
	int opt=1;
	char buf[100];
	int r;
	struct sockaddr_in dr;
	fd_set fds;
	
	fd=socket(PF_INET,SOCK_STREAM,0);
	if(fd==-1) printf("1:%m\n"),exit(-1);
	printf("1\n");
	dr.sin_family=AF_INET;
	dr.sin_port=htons(10000);	
	dr.sin_addr.s_addr=inet_addr("192.168.1.116");
	
	r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));
	if(r==-1) printf("2:%m\n"),exit(-1);	
	while(1)
	{
		FD_ZERO(&fds);
		FD_SET(fd,&fds);
		select(fd+1,0,&fds,0,0);
		send(fd,"Hello",5,MSG_OOB);//如果频繁往缓冲写 缓冲内容过多而由于
		                           //只有一个带外数据 体现不出
		sleep(1);
	}
	close(fd);
	
}
        1.OOB数据只能接收/发送成功一个字符
        2.普通数据使用一般方式接收与发送,OOB数据使用MSG_OOB接收与发送
        3.一个数据使用MSG_OOB,则最后一个是OOB,其他非OOB数据
        4.问题:OOB数据是优先数据。优先体现在什么地方?
            
四.HTTP协议以及应用                                                
    1.HTTP协议版本HTTP1.0 HTTP1.1    
    2.HTTP是应用协议
    3.HTTP协议分成:
                请求协议 浏览器发给Web服务器的

                响应协议 Web服务器发给浏览器的

index.html

<html>
<head>
	<title>Http Protocol</title>
</head>
<body>
	<form action="http://192.168.1.116:10000/index.php" target="_self" method="post">
		姓名:<input type="text" name="user" value="tom"/><br/>
		
		口令:<input type="password" name="pass" value="tom"/><br/>
		
		<input type="submit" value="登录"/>
	</form>
</body>
</html>
用浏览器打开这个网页 就向服务器发送信息 服务器就收到以下信息


    4.请求协议的格式:
            请求行(请求方法 请求资源 协议版本)  
            请求体(请求头:请求值)
            空行

            数据(querystring:key=value&key=value)

http_c.c//类似浏览器功能 服务器会跟据请求给出响应

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

main()
{
	int fd;
	struct sockaddr_in dr;
	char strreq[1024];
	char buf[10*1024];
	int r; 
	//建立socket
	fd=socket(AF_INET,SOCK_STREAM,0);
	
	//连接服务器192.168.0.72
	dr.sin_family=AF_INET;
	dr.sin_port=htons(80);
	dr.sin_addr.s_addr=inet_addr("192.168.0.72");
	r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));
	
	//构建http请求字符串
	sprintf(strreq,
		"GET /index.php HTTP/1.1\r\n"
		"Host: 192.168.0.72:80\r\n"
		"User-Agent: Tarena5.0\r\n"
		"Accept: text/html,image/png\r\n"//浏览器能接收的
		"Accept-Language: zh-cn\r\n"
		"Accept-Charset: gb2312,utf-8\r\n"
		 "Keep-Alive: 300\r\n"
		 "Connection: keep-alive\r\n"
		 "\r\n");
	  //发送http请求字符串
	  r=send(fd,strreq,strlen(strreq),0);//向服务器发送请求
	 //等待服务器响应
	 //while(1)
	 //{
		 r=recv(fd,buf,1024,0);//接收服务器响应
		//if(r<=0) break;
		printf("========================\n");
		 printf("%s\n",buf);
		printf("========================\n");
	//}
	 close(fd);
}


    5.响应协议的格式
            响应行(协议版本 响应码 响应码的文本描述)
            响应体(响应头: 响应值)
            空行

            数据(普通数据/分块数据)

http_s.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
int fd,cfd;
main()
{
	char buf[1024];
	int r;
	struct sockaddr_in dr;
	char strres[1024];
	
	fd=socket(PF_INET,SOCK_STREAM,0);
	if(fd==-1) printf("1:%m\n"),exit(-1);
	printf("1\n");
	dr.sin_family=AF_INET;
	dr.sin_port=htons(10000);	
	dr.sin_addr.s_addr=inet_addr("192.168.1.116");
	
	r=bind(fd,(struct sockaddr*)&dr,sizeof(dr));
	if(r==-1) printf("2:%m\n"),exit(-1);
	printf("2\n");
	r=listen(fd,10);
	if(r==-1) printf("3:%m\n"),exit(-1);
	printf("3\n");	
	cfd=accept(fd,0,0);	
	if(cfd==-1) printf("4:%m\n"),exit(-1);
	printf("4\n");
	sprintf(strres,
		"HTTP/1.1 200 OK\r\n"
		"Server: tarena2.0\r\n"
		"Content-Type: text/html\r\n"
		"Content-Length: 28\r\n"
		"Connection: keep-alive\r\n"
		"\r\n"
		"<font color=red>靓崽!</font>");
	while(1)
	{
		r=recv(cfd,buf,1024,0);
		if(r>0)
		{
			buf[r]=0;
			printf("接收数据:%s\n",buf);
			send(cfd,strres,strlen(strres),0);
		}		
		else
		{
			break;
		}
				
	}	
	close(cfd);
	close(fd);
	
}

用浏览器访问http://192.168.1.116:10000/index.html
                    
         1XX     正在处理
         2XX        响应成功200
         3XX        继续处理
         4XX        客户错误

         5XX        服务器错误


五.ioctl函数
        实现ifconfig工具
        
总结:
        重点:
                select
                广播
                
        了解:
                OOB数据
                HTTP协议
        
        应用:
                独立编写TCP服务器端的select模式
                编写广播
                能够请求一个网页,并且解析响应
作业:
        1.把聊天程序使用poll实现
        2.使用UDP的广播,发送一个文件                
        3.随意挑选网站,把主页下载并保存成html文件

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SelectSocket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。下面详细介绍一下! Select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明): int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 先说明两个结构体: 第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。 第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。 具体解释select的参数: int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。 fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。 fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。 fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。 struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。 返回值: 负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值