首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

关于增高socket效率,select+多进程/多线程 付源码

2014-01-09 
关于提高socket效率,select+多进程/多线程 付源码hi,小弟刚玩socket,日前完成了一个单个线程+select 的程

关于提高socket效率,select+多进程/多线程 付源码
hi,
    小弟刚玩socket,日前完成了一个单个线程+select 的程序,大致功能就是可以将一个服务器A发送过来的内容转发到另一个服务器B同时将另一台服务器B发送过来的内容转发到这个服务器A。测试发现:
1.单向传输1G文件,速率有8MB(百兆网口),但是双向同时传输文件,速率只有4MB
2.希望能将程序改成多进程的或者多线程的来提高速率(即吞吐量)

源码如下,小弟尝试在connect之后fork了2个子程序来实现(即一个进程只负责A-B,另一个只负责B-A)的操作,但是发现传文件的时候传不完就程序就断了,下面是我单个线程+select 的程序,有没高手指点一下如何修改???

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdarg.h>
#include <netdb.h>

#define SERVPORT 8090
#define MAXDATASIZE 1024



int do_con_n(char *address, int port)
{
int s;  
struct sockaddr_in sin;
fd_set set;
struct timeval timeo = {15, 0}; //time ou struct
socklen_t len = sizeof(timeo);
int retval;
int error;
int code;
  
  s=socket(AF_INET, SOCK_STREAM, 0);  
 // x=fcntl(s,F_GETFL,0);              // Get socket flags  
  fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK); //设置为非阻塞模式  
  memset(&sin, 0, sizeof(struct sockaddr_in));  
  sin.sin_family=AF_INET;  
  sin.sin_port=htons(port);  
  //sin.sin_addr.s_addr=name_resolve(address);  
  sin.sin_addr.s_addr = inet_addr(address);
  if(!sin.sin_addr.s_addr) return(-1);  
  //printf("ip: %s\n",inet_ntoa(sin.sin_addr));  
  if(connect(s, (struct sockaddr *)&sin, sizeof(sin))!=0)
  {
  if (errno != EINPROGRESS) 
  { 
       printf("connect:normal network unreach!!");
       return -1;
    }
   
     FD_ZERO(&set);/*将set清零使集合中不含任何fd*/ 
   FD_SET(s,&set); /*将一个给定的文件描述符加入集合之中*/ 
   retval = select(s + 1, NULL, &set, NULL, &timeo);
   if (retval == -1) 
   {
     printf("select");
     return -1;
   } 
   else if(retval == 0) 
   {
     printf("timeout\n"); //这样的select等于是变成了再timeout时间内是阻塞模式,超过timeout就直接返回
     printf("%d\n", time(NULL));
     return 0;
   }
   else
   {
   //printf("connected--->:[%d]\n",retval);
   if (FD_ISSET(s, &set)) 
   {     
   error=0;
    len = sizeof(error);     
    code = getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len);     
    /* 如果发生错误,Solaris实现的getsockopt返回-1,   
     * 把pending error设置给errno. Berkeley实现的   
     * getsockopt返回0, pending error返回给error.    
     * 我们需要处理这两种情况 */    
    if (code < 0 || error) 
    {     
        close(s);     
        if (error)      
            errno = error;
        printf("[getsockopt1]ERROR: code= %d ,errno = %d, strerror = %s \n"  
                                , code,errno, strerror(errno));  
        perror("getsockopt1:");
        return -1;     
    }
    printf("\nconnect to [%s] ok!\n",inet_ntoa(sin.sin_addr));

else 
{     
    fprintf(stderr, "select error: sockfd not set");     
    exit(0);     
}     
   }
  } 


   
return(s);  
}

int SeanSend(int fd, void *buffer, int length)  
{  
    int bytes_left;   
    int written_bytes;   
    char *ptr=NULL;   
    ptr=(char *)buffer;   
    bytes_left=length;  
    
    while( bytes_left>0)   
    {   
        /* 开始写*/
        written_bytes=write(fd, ptr, bytes_left);
        //printf("\nin seasend:written_bytes=[%d]\n",written_bytes);
        if(written_bytes<=0) /* 出错了*/   
        {   
            if(errno==EINTR) /* 中断错误 继续写?*/   
            {  
            printf("[%s]:error errno==EINTR continue\n",__FUNCTION__); 
                continue;  
                 
            }  
            else if(errno==EAGAIN) /* EAGAIN : Resource temporarily unavailable*/   
            {  
            //printf("[SeanSend]error errno==EAGAIN continue\n");
                usleep(1000);//等待防止cpu %100,希望发送缓冲区能得到释放 
                continue;  
                  
            }  
            else /* 其他错误 */   
            {  
                printf("[%s]:ERROR: errno = %d, strerror = %s \n",__FUNCTION__  
                                , errno, strerror(errno));  
                return(-1);  
            }  
        }  
        bytes_left-=written_bytes;   
        ptr+=written_bytes;/* 从剩下的地方继续写?? */   
    }   
    return length;   


int main(int argc, char **argv)
{
int sockfd, recvbytes;
int sockfd1;
int ret;
char rcv_buf[MAXDATASIZE];
char snd_buf[MAXDATASIZE];
char buf[MAXDATASIZE];
struct hostent *host;
struct sockaddr_in serv_addr;
struct sockaddr_in serv_addr1;
fd_set rdfds;
fd_set rdfds1;
int srvport,srvport1;
struct timeval timeo1 = {3, 0}; //time1 ou struct
int rcvlen=0;
int wrtlen=0;
int rcvlen1=0;
int wrtlen1=0;
struct linger1
{
int l_onoff;
int l_linger;
}so_linger;

so_linger.l_onoff=1;
so_linger.l_linger=10;

//setsockopt(sockfd,SOL_SOCKET, SO_LINGER, (const char *) &so_linger, sizeof(so_linger));


if(argc != 7)
{
fprintf(stderr, "Error!.\n");
exit(1);
}

sockfd=do_con_n(argv[2],atoi(argv[3]));
sockfd1=do_con_n(argv[4],atoi(argv[5]));

if( (-1==sockfd) || (-1==sockfd1) )
{
printf("\nconnect error\n");
return -1;
}
else
{
//printf("\nargv[6]=[%s],len=[%d]\n",argv[6],strlen(argv[6]));
SeanSend(sockfd1,argv[6],strlen(argv[6]));
}

setsockopt(sockfd,SOL_SOCKET, SO_LINGER, (const char *) &so_linger, sizeof(so_linger));
setsockopt(sockfd1,SOL_SOCKET, SO_LINGER, (const char *) &so_linger, sizeof(so_linger));

while(1)
{
FD_ZERO(&rdfds);
FD_SET(sockfd, &rdfds);
FD_SET(sockfd1, &rdfds);

//printf("\nsock=[%d],sock1=[%d],res=[%d]\n",sockfd,sockfd1,(sockfd>sockfd1?sockfd:sockfd1)+1);




ret = select( (sockfd>sockfd1?sockfd:sockfd1)+1, &rdfds, NULL, NULL, NULL );
if(ret<0)
{
if(errno == EINTR)
continue;
perror("select:"); 
exit(1);   
}
  //注意,当前的处理改用不加超时的select,cpu占用率更加低,少于%1
else if(ret==0)
{
//printf("\nret==0,timeout\n");
//select在第一次超时之后会将timeo1的值修改为0,所以在下面的判断要做一个usleep,不然在timeout状态时cpu占用高达12%
usleep(1000); //这样cpu处于timeout的时候 占用率会低
continue;
}
else
{
/*if(FD_ISSET(0, &rdfds))
{
memset(snd_buf, 0, sizeof(snd_buf));
read(0, snd_buf, 256);
snd_buf[strlen(snd_buf)] = '\0';
write(sockfd, snd_buf, strlen(snd_buf)+1);

}*/
if(FD_ISSET(sockfd, &rdfds))
{
//printf("go\n");
memset(rcv_buf, 0x00, sizeof(rcv_buf));
if((rcvlen=read(sockfd, rcv_buf, MAXDATASIZE))>0)
{
printf("rcv:[%s],rcvlen=[%d]\n",rcv_buf,rcvlen);
//write(sockfd, snd_buf, strlen(snd_buf)+1);
/*if(write(sockfd1, rcv_buf, sizeof(rcv_buf))<0)
{
perror("write:");
}*/
wrtlen=SeanSend(sockfd1,rcv_buf,rcvlen);
//printf("\nwrtlen=[%d]\n",wrtlen);
}
else if(0==rcvlen)
{
//printf("\nclose wrtlen=[%d]\n",wrtlen);

//printf("\nsockfed close:ip[%s],port[%d]\n",argv[1],atoi(argv[2]));
//close(sockfd);
break;
}
else
{
 printf("sockfed ERROR: errno = %d, strerror = %s \n"  
                                , errno, strerror(errno)); 
}

}

if(FD_ISSET(sockfd1, &rdfds))
{
//printf("go11\n");
memset(rcv_buf, 0x00, sizeof(rcv_buf));
if((rcvlen1=read(sockfd1, rcv_buf, MAXDATASIZE))>0)
{
printf("rcv1:[%s],rcvlen=[%d]\n",rcv_buf,rcvlen1);
/*if(write(sockfd, rcv_buf, rcvlen1)<0)
{
perror("write:"); //缓冲区很快就满了,会出现部分写操作失败。。。
}
*/
wrtlen1=SeanSend(sockfd,rcv_buf,rcvlen1);
//SeanSend(sockfd,"send end",8);

//printf("\nwrtlen1=[%d]\n",wrtlen1);
}
else if(0==rcvlen1)
{
//printf("\nclose wrtlen1=[%d]\n",wrtlen1);
//SeanSend(sockfd1,"nihaoma",8);
//printf("\nsockfed1 close:ip[%s],port[%d]\n",argv[1],atoi(argv[3]));
//close(sockfd1);
break;
}
else
{
printf("sockfed1 ERROR: errno = %d, strerror = %s \n"  
                                , errno, strerror(errno)); 
}

}
//continue;
}
}
//sleep(1);
close(sockfd);//TCP 单向关闭
close(sockfd1);
    return 0;
}

[解决办法]
代码太多,没仔细看,起两个子进程分别用于传输,思路没有错,
但是仅处理两个连接,收---发---收---发,直接用堵塞read和write就可以啦,
没发现有需要用select的地方。

热点排行