select版的TCP通信

Posted 梅诺

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了select版的TCP通信相关的知识,希望对你有一定的参考价值。

        编写代码之前,大概先说一下利用select编写tcp的思路及select特点。

        select系统调用是用来让程序监视多个文件句柄的状态变化的,程序会停在select这里等待,直到被监视的句柄有一个或者多个发生了状态改变。

select函数为:int select(int nfds,fd_set *readfds,fd_set* writefds,异常文件描述符,时间长度)//默认时间长度异常事件描述符一般都为NULL

在select中提出四个宏来处理描述数组的方式:(fd为文件描述符集set为描述数组)

FD_SET(fd,fd_set* set):用来设置set中fd位

FD_ISSET(fd,fd_set* set):用来测试set中额相关fd位是否为真

FD_ZERO(fd,fd_set* set):用来清除set的全部位

FD_CLR(fd_set*set):用来清除set中相关的fd的位

当然select的模型最终的特点如下:(1)select下可监控的文件描述符的个数取决于系统的大小

(2)每次检测到一个新的fd,都要手动的将fd添加到set中,并且使用一个数据结构arrar来保存select监控集中的set

(3)select模型必须在设了select前循环array


在大致了解了select的特点之后,现在就来说一说select 版的tcp的编写思路:

1:搭建好基本的tcp的框架,在服务器端编写-->生成套接字(starup),绑定端口(bind),监听(listen)模块,接受客户端访问(accept)模块

2.客户端实现连接请求模块(connect)

3.定义一个数组arrary,初始值为-1,然后把步骤1 中生成的监听套接字(listen_sock)添加到该数组

4.编写select函数监听来自客户端的请求,若监听到客户端连接,就会生成一个new_sock套接字,那么当然把这个套接字添加到数组arrary中。当下次遍历数组时得到的是new_sock套接字,那它就被作为数据传输套接字进行数据接收。

5.在步骤4的基础上读取客户端放在缓冲区的内容。


好啦!以上大致就是编写思路的说明了,下面贴出代码:

服务器端:server.c

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

#define port 8080
#define MAX_FD_NUM 20
int array_fd[MAX_FD_NUM];//for watch fd

int startup()

	int sock=socket(AF_INET,SOCK_STREAM,0);
	if(sock<0)
	
		perror("socket");
		exit(1);
	
	struct sockaddr_in local;
	local.sin_family=AF_INET;
	local.sin_port=htons(port);
	local.sin_addr.s_addr=htonl(INADDR_ANY);
	if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
	
		perror("bind");
		exit(1);
	
	if(listen(sock,5)<0)
	
		perror("listen");
		exit(1);
	
	return sock;

int main()

	int listen_sock=startup();

	struct sockaddr_in client;
        socklen_t len =sizeof(client);

	fd_set read_set;//READ 
	int max_fd=listen_sock;

	//init array
	int i=0;
	for(;i<MAX_FD_NUM;i++)
	
	     array_fd[i]=-1;//set init key=-1
	
	array_fd[0]=listen_sock;
	while(1)
	
	  FD_ZERO(&read_set);//empty read_set
          for(i=0;i<MAX_FD_NUM;i++)
         
           if(array_fd[i]>0)
          
	      FD_SET(array_fd[i],&read_set);//set array_fd in red_set
              if(max_fd<array_fd[i])
		  
			  max_fd=array_fd[i];//get max_fd
		  
	  
	 
	  switch(select(max_fd+1,&read_set,NULL,NULL,NULL))
	  
		case 0://timeout
			printf("timeout...");
			break;
		case -1://error
			perror("select");
                        break;
		default:
			for(i=0;i<MAX_FD_NUM;i++)
			
				if(array_fd<0)
				
					continue;
                
				//new connect
				else if(array_fd[i]==listen_sock && FD_ISSET(array_fd[i],&read_set))
				
					int new_sock=accept(array_fd[i],(struct sockaddr*)&client,&len);
					if(new_sock<0)
					
						continue;
					
					printf("get new connect: %d\\n",new_sock);
					for(i=0;i<MAX_FD_NUM;i++)
					
                                           if(array_fd[i]==-1)
					  
						  array_fd[i]=new_sock;
						  break;
					  
					
					  if(i==MAX_FD_NUM)
					  
						  close(new_sock);
					  
				
				else
				
					for(i=1;i<MAX_FD_NUM;i++)
					
						if(array_fd[i]>0 && FD_ISSET(array_fd[i],&read_set))
						
							char buf[1024];
							memset(buf,'\\0',sizeof(buf));
							ssize_t _size=read(array_fd[i],buf,sizeof(buf)-1);
							if(_size<0)
									
								printf("read failed");
								close(array_fd[i]);
							
							else if(_size==0)
							
								printf("client close...\\n");
								close(array_fd[i]);
							
							else
							
								printf("client: %s\\n",buf);
							
						
					
				
			
			break;
	  
	

	close(listen_sock);
	return 0;

客户端:client.c

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<errno.h>
int main()

   int read_fd=0;
   int write_fd=1;
   int max_fd=0;
   fd_set read_set;
   fd_set write_set;

   int sock=socket(AF_INET,SOCK_STREAM,0);
   if(sock<0)
   
	   perror("socket");
	   exit(1);
   
        struct sockaddr_in remote;
	remote.sin_family=AF_INET;
	remote.sin_port=htons(8080);
	remote.sin_addr.s_addr=inet_addr("192.168.198.128");//本机IP

	int new_sock=connect(sock,(struct sockaddr *)&remote,sizeof(remote));
	if(new_sock<0)
	
		perror("connect");
		exit(1);
	
	if(sock >read_fd)
	   max_fd=sock;
	else
		max_fd=read_fd;
	 while(1)
	 
		 FD_ZERO(&read_set);//empty
		 FD_ZERO(&write_set);
		 FD_SET(read_fd,&read_set);//set
		 FD_SET(sock,&write_set);//

		 switch(select(max_fd+1,&read_set,&write_set,NULL,NULL))
		 
			 case 0://timeout	
			 printf("timeout...\\n");
				 break;
			case -1://error
			 printf("there are bug\\n");
			 printf("error...\\n");
				 break;
			default://data ready
				 
				 if(FD_ISSET(read_fd,&read_set))//judge
				 
					 char buf[1024];
						 ssize_t _size=read(read_fd,buf,sizeof(buf)-1);
						 if(_size>0)
						 
						 buf[_size]='\\0';
						 printf("echo:%s\\n",buf);
						 
				    	 if(FD_ISSET(sock,&write_set))
					     
							 //printf("write successful\\n");
						  send(sock,buf,strlen(buf),0);
                       // printf("%d\\n",_size);
						 
				
					 
				 
				 break;
		 
	 
	return 0;

Makefie文件:

.PHONY:all
all:server client
client:client.c
	gcc -o $@ $^
server:server.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -r server client



















以上是关于select版的TCP通信的主要内容,如果未能解决你的问题,请参考以下文章

epoll实现TCP通信

socket编程。怎么实现数据包的转发?C语言版的。

TCP服务器如何使用select处理多客户连接

HTTP - TCP实现HTTP GET请求

HTTP - TCP实现HTTP GET请求

关于TCP和UDP的优缺点