网络编程 并发执行 线程 进程 IO复用 select poll

并发服务器实现  线程 进程 函数 select poll  代码实现   具体理论细节还需要补充

1. //并发服务器 创建线程负责数据交互

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

void *routine(void *arg)
{
   int newfd=(int)arg;
   char buf[100];
   while(1)
   {
       int cnt1=read(newfd,buf,100);
       printf("threadid=%u,fd=%d\n",pthread_self(),newfd);
       write(1,buf,cnt1);
       if(strncmp(buf,"quit",4)==0)
              break;
   }
   printf("threadid %u exit\n",pthread_self());
   close(newfd);
   pthread_exit();
   return NULL;
}

int main()
{
   //socket
   int fd=socket(AF_INET,SOCK_STREAM,0);
   //绑定地址  协议 端口 ip
   struct sockaddr_in servaddr;
   servaddr.sin_family = AF_INET;
   servaddr.sin_port = htons(1234);
   servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
   printf("server:  ip:%s   port:%d\n",inet_ntoa(servaddr.sin_addr),ntohs(servaddr.sin_port));
   int ret=bind(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
   if(ret<0)
   {
      fprintf(stderr,"error:%s\n",strerror(errno));
      close(fd);
      return -1;
   }
   //服务器监听
   listen(fd,5);
 
   while(1)
   {
	   pthread_t tid;
	   int newfd;
	   struct sockaddr_in peer;
	   socklen_t size=sizeof(peer);
	   printf("waiting for client connectting.......\n");
	   //响应客户端连接
	   newfd=accept(fd,(struct sockaddr *)&peer,&size);  
	   //客户端  ip  端口
	   printf("client:  ip:%s   port:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port));
	   //建立线程 负责接收客户端数据
	   pthread_create(&tid,NULL,routine,(void*)newfd);
  }
    return 0;
}


2. //并发服务器 创建线程负责数据交互

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

void *routine(void *arg)
{
   int newfd=(int)arg;
   char buf[100];
   while(1)
   {
       int cnt1=read(newfd,buf,100);
       printf("threadid=%u,fd=%d\n",pthread_self(),newfd);
       write(1,buf,cnt1);
       if(strncmp(buf,"quit",4)==0)
              break;
   }
   printf("threadid %u exit\n",pthread_self());
   close(newfd);
   pthread_exit();
   return NULL;
}
int main()
{
   //socket
   int fd=socket(AF_INET,SOCK_STREAM,0);
   //绑定地址  协议 端口 ip
   struct sockaddr_in servaddr;
   servaddr.sin_family = AF_INET;
   servaddr.sin_port = htons(1234);
   servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
   printf("server:  ip:%s   port:%d\n",inet_ntoa(servaddr.sin_addr),ntohs(servaddr.sin_port));
   int ret=bind(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
   if(ret<0)
   {
      fprintf(stderr,"error:%s\n",strerror(errno));
      close(fd);
      return -1;
   }
   //服务器监听
   listen(fd,5);
 
   while(1)
   {
	   pthread_t tid;
	   int newfd;
	   struct sockaddr_in peer;
	   socklen_t size=sizeof(peer);
	   printf("waiting for client connectting.......\n");
	   //响应客户端连接
	   newfd=accept(fd,(struct sockaddr *)&peer,&size);  
	   //客户端  ip  端口
	   printf("client:  ip:%s   port:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port));
	   //建立线程 负责接收客户端数据
	   pthread_create(&tid,NULL,routine,(void*)newfd);
  }
    return 0;
}


3. 并发服务器 select函数完成IO复用   

   服务器接收多个客户端请求,接收客户端的数据并打印在服务器端 

</pre></blockquote></blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><pre name="code" class="cpp">#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define LISTEN_PORT 1234
#define MAX_CLIENT	5
#define BUFFSIZE	256
int fds[MAX_CLIENT];  //存储连接的文件描述符

int main(int argc, char **argv)
{
	//监听套接字    连接套接字
	int listenfd, newfd;
	// 地址结构   : 服务器地址信息,客户端地址信息
	struct sockaddr_in servaddr, peeraddr;
	socklen_t addrsize;
					//
	if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("socket");
		exit(1);
	}

	//设置套接字的选项  Address reuse
	//允许在同一个端口启动服务器的多个实例
	int ret;
	int on = 1;
					//   级别:普通套接字  地址复用
	ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
					&on, sizeof(int));
	// if ret

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(LISTEN_PORT);
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	ret = bind(listenfd, (struct sockaddr *)&servaddr,
			sizeof(servaddr));
	// if ret

	ret = listen(listenfd, MAX_CLIENT);
	// if ret

	//文件描述符集
	fd_set rdfds;	// 可读的集合
	struct timeval tv;  //定时

	int maxsockfd;  // rdfds集合中最大文件描述符加1
	maxsockfd = listenfd + 1;
	int i;

	char buff[BUFFSIZE];

	while(1)
	{
		// 初始化文件描述符集合
		FD_ZERO(&rdfds);
		FD_SET(listenfd, &rdfds);  //将listenfd加入rdfds

		for(i = 0; i < MAX_CLIENT; i++)
			if(fds[i] != 0) // 表示对应一个客户端连接
				FD_SET(fds[i], &rdfds); // 添加活动的连接
		// 超时设定
		tv.tv_sec = 30;
		tv.tv_usec = 0;

		puts("no change ........");
		//select 监控fd发生变化
		ret = select(maxsockfd, &rdfds, NULL, NULL, &tv);
		puts("change ..........");
		// ret < 0 : error
		// ret ==0 : timout
		// ret > 0 , IO change

		if(ret < 0)
		{
			perror("select");
			break;
		}
		if(ret == 0)
		{
			printf("time out\n");
			continue;
		}
		// ret > 0
		// new connection
		addrsize = sizeof(peeraddr);
		//服务器响应客户端请求
		if(FD_ISSET(listenfd, &rdfds)) //有客人已经来访,
		{
			newfd = accept(listenfd,
						(struct sockaddr *)&peeraddr,
						&addrsize);  //开门
			// 添加新的fd到数组中
			for(i = 0; i < MAX_CLIENT; i++){
				if(fds[i] == 0)
				{
					fds[i] = newfd;
					break;
				}
			}

			 // maxsockfd 保证大于集合中的所有文件描述符
			 if(newfd > maxsockfd - 1)
				maxsockfd = newfd + 1;

		}

		// fds[]  socket临时存储
		// rdfds,  FD_SET 集合
		// read data   fds[]
		// 遍历客户端fd  查找是否有可读的fd
		for(i = 0; i < MAX_CLIENT; i++)
		{
			if(FD_ISSET(fds[i], &rdfds))
			{	// can read
				// recv read
				//ret = read(fds[i], buff, BUFFSIZE);
				ret = recv(fds[i], buff, BUFFSIZE, 0);
				//客户端连接关闭,清除fds相应元素
				if(ret <=0 ){
					close(fds[i]);
					FD_CLR(fds[i], &rdfds);
					fds[i] = 0;
				}
				else{
					write(1, buff, ret);
					puts("");
				}

			} // end if
		} // end for
	}// end while


}










4.并发服务器 poll函数完成IO复用 

        
<span style="font-size:10px;font-weight: normal;">#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netdb.h>
#define MYPORT 1234 //连接时使用的端口
#define MAXCLINE 5 //连接队列中的个数
#define BUF_SIZE 256
#define MAXUSER 256
void showclient(struct pollfd fds[])
{
  	int i;
  	printf("client amount:%d\n", conn_amount);

	for(i = 0; i < MAXCLINE; i++)
  	{
  		printf("[%d]:%d ",i,fds[i].fd);
  	}
 		printf("\n\n");
}
int main(int argc, char **argv)
{
  	int sock_fd,new_fd; //监听套接字 连接套接字
  	struct sockaddr_in server_addr; // 服务器的地址信息
  	struct sockaddr_in client_addr; //客户端的地址信息
  	socklen_t sin_size; //当前的连接数
	int conn_amount=0;
  	int yes = 1;
  	char buf[BUF_SIZE];
  	int ret;
  	int i;
  if((sock_fd = socket(AF_INET,SOCK_STREAM, 0))==-1)
  {
    perror("socket");
    exit(1);
  }
  //设置套接口的选项 SO_REUSEADDR 允许在同一个端口启动服务器的多个实例
  //setsockopt的第二个参数SOL SOCKET 指定系统中,解释选项的级别 普通套接字
  //fcntl(sock_fd, F_SETFL, O_NONBLOCK);
  if(setsockopt(sock_fd,SOL_SOCKET, SO_REUSEADDR,&yes, sizeof(int))==-1)
  {
    perror("setsockopt error \n");
    exit(1);
  }
  memset(&server_addr, 0, sizeof(server_addr));
  server_addr.sin_family = AF_INET; //主机字节序
  server_addr.sin_port = htons(MYPORT);
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//通配IP
 // memset(server_addr.sin_zero,'\0',sizeof(server_addr.sin_zero));
  if(bind(sock_fd,(struct sockaddr *)&server_addr,sizeof(server_addr)) == -1)
  {
    perror("bind error!\n");
    exit(1);
  }
  if(listen(sock_fd, MAXCLINE)== -1)
  {
    perror("listen error!\n");
    exit(1);
  }

  printf("listen port %d\n", MYPORT);

    struct pollfd fdsr[MAXUSER];
    int maxsock = sock_fd;
 
    conn_amount =0;
    sin_size = sizeof(client_addr);
	/*将数组中的第一个元素设置成监听描述字*/
    fdsr[0].fd = sock_fd;
    //fdsr[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
    fdsr[0].events = POLLIN; 
    /*数组中的其它元素将暂时设置成不可用*/
    for(i = 1; i < MAXUSER; i++)
        fdsr[i].fd = -1;
	int nready;
	int recv_bytes;
  while(1){
        //将进程阻塞在poll上
        printf("wait client \n");
        nready = poll(fdsr, maxsock + 1, -1);  // INFTIM
        printf("out\n"); 
        /*先测试监听描述字*/
        if( fdsr[0].revents & POLLIN)
		{
            printf("wait for new connect...\n");
            new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
            
            /*将新连接加入到测试数组中*/
            printf("new connect... %d\n", new_fd);
            for(i = 1; i < MAXUSER; i++)
			{
                if(fdsr[i].fd < 0)
				{
                    fdsr[i].fd = new_fd;
                    fdsr[i].events = POLLIN;/*测试条件普通数据可读*/
                    break;
                }
            }
            
            if(i == MAXUSER)
			{
                printf("too many client\n");
				continue;
            }
            if(i > maxsock)
                maxsock = i;
            
            if(--nready <= 0 )
                continue;
           //如果没有可读的描述符了,就重新监听连接
        } 
        /*测试除监听描述字以后的其它连接描述字*/
        for( i = 1; i <= maxsock; i++)
		{
             if(fdsr[i].fd < 0)
                 continue;
             if(fdsr[i].revents & (POLLIN | POLLERR))
			 {
                 memset(buf, 0, BUF_SIZE);
                 recv_bytes = recv(fdsr[i].fd, buf, BUF_SIZE, 0);
                if(recv_bytes < 0)
				 {
                    if( errno == ECONNRESET)
					{ //如果连接断开,就关闭连接,并设当前描述符不可用
                        close(fdsr[i].fd);
                        fdsr[i].fd = -1;
                    }
                    else
                        perror("read error");
                 }
                else if(recv_bytes == 0)
				 { //如果数据读取完毕,关闭连接,设置当前描述符不可用
                    close(fdsr[i].fd);
                    fdsr[i].fd = -1;
                 }
                else
                    printf("newfd = %d : %s\n",fdsr[i].fd, buf);
            }
                        
        }     
		showclient(fdsr);
    }
 } 
</span>

5.客户端测试代码    客户端向服务器发送数据

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
extern int errno;
int main()
{
   
   int fd=socket(AF_INET,SOCK_STREAM,0);
   fprintf(stderr,"client id=%d\n",fd);
   
   struct sockaddr_in servaddr;
   servaddr.sin_family = AF_INET;
   servaddr.sin_port = htons(1234);
   servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
   
   int ret=connect(fd,(struct scokaddr *)&servaddr,sizeof(servaddr));
   if(ret<0)
    {   
       fprintf(stderr,"error:%s\n",strerror(errno));
       close(fd);
       return -1;
    }
   char  buf[100];
   while(1)
   {   
       fgets(buf,100,stdin);
       write(fd,buf,strlen(buf));
       if(strncmp(buf,"quit",4)==0)
       {
          close(fd);
          return 0;
       }
   }
   close(fd);
   return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值