免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2292 | 回复: 9
打印 上一主题 下一主题

[C] UDP服务器实现过程中的一点小疑问 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-29 16:48 |只看该作者 |倒序浏览
自己实现的UDP服务器模型存在一点问题。
先上代码:

服务端的:

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


#define MYPORT 4567 //well-known port

#define MAXLINE 4096

static int fd1[2], fd2[2] ;
static char msg[MAXLINE] = {'\0'} ;
int global_sockfd;

struct sockaddr_in     pool[VOLUME];
struct sockaddr_in    global_from_addr;   

//-----------------------------------------------------------------------------

void start_newsrv(struct sockaddr_in newfrom_addr){

        printf("start new srv\n");
        
        static int i = 0;
        static pid_t pid;
        int     sockfd;
        int     addr_len;
        static int port = MYPORT;
        char     newport[5] = {'\0'};
        struct sockaddr_in    newsrv_addr;
        
        addr_len = sizeof(newfrom_addr);
        
        
        
        if( (pid = fork()) < 0 )
            printf("fork error!\n");
        else if (pid > 0){            //create new socket using new port number

        
        
   
        if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {

            perror("socket");
            exit(1);
        }
   
        int opt = 1;

       /*if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
            &opt, sizeof(opt)) != 0){
     
            perror("Server setsockopt failed";
            
        }
        */
        port++;
        
        printf("prepare new port(in srv ):%d\n",port);
   
        bzero((char *) &newsrv_addr, sizeof(newsrv_addr));
        newsrv_addr.sin_family = AF_INET;
        newsrv_addr.sin_port = htons(port); //new port number

        newsrv_addr.sin_addr.s_addr = INADDR_ANY;
   
        if(    bind(sockfd,(struct sockaddr *)&newsrv_addr,
                    sizeof(newsrv_addr)) == -1){
                    
        perror("bind");
        exit(1);
        
        }
        
        sprintf(newport,"%d",port);
        
        printf("new srv ok,send to client new port\n");
        //send client new port info

        if (-1 == sendto(global_sockfd,newport,strlen(newport),0,
                        (struct sockaddr *)&newfrom_addr,
                        sizeof(newfrom_addr)) ){
                        
                perror("sendto");
               
        }
        
        printf("send new port ok\n");
        
        
        do{    //comumunicate with client through new socket

            
        if ( recvfrom(sockfd, msg, MAXLINE, 0,
            (struct sockaddr *)&newfrom_addr, &addr_len) < 0 ){   
        
            perror("recvfrom error");

        }
        

        
        
        int count = 0 ;
        
        printf("receive msg times:%d\n",++count);
            
                if (-1 == sendto(sockfd,msg, MAXLINE, 0,
                    (struct sockaddr *)&newfrom_addr,sizeof(newfrom_addr)) ){
                        
                perror("sendto cli through new udpsrv error");   
               
            }
            
            
    }while(1);
        
        
    }
   
   

}


//-----------------------------------------------------------


int handle_request(int global_sockfd){
   
    int            len;
    int         recvlen;
    int         count = 0;
   
        

again:
    recvlen = recvfrom(global_sockfd, msg, MAXLINE,
                        0,(struct sockaddr *) &global_from_addr, &len);
      

      //部分错误处理以及hash的代码,与问题没有太大关联,略去不计
   
        start_newsrv(global_from_addr);
            
    goto again;
        
    }
   
   

}



void main()

{
    struct sockaddr_in srv_addr;

    memset(pool,'\0',sizeof(pool));
   
    if ((global_sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {

    perror("socket");
    exit(1);
}
       // .......

     

    if(    bind(global_sockfd,(struct sockaddr *)&srv_addr,sizeof(srv_addr)) == -1) {
    perror("bind");
    exit(1);
}   

   printf("udp srv end ok...\n");

    handle_request(global_sockfd);
   

}



服务器模型已经运行成功,唯一有问题的就是那个setsockopt函数的地方,当灰色部分标示的代码被注释掉后,运行结果就是:
只能有一个客户端请求成功,当新的客户端来请求时,服务端的错误处理部分会出现bind error address already in use 的错误。
这里我不解的地方就是,在服务端新建立的socket使用的port和原先的port并不一样啊(有port++操作),并且:
newsrv_addr.sin_addr.s_addr = INADDR_ANY;
那为什么还会提示address 已经被使用的信息呢?



当灰色部分不被注释掉,运行结果是正常的,也可以完成服务器的模型,但有问题,见附件1。
就是当多于一个客户端请求服务端时,在服务端中的port++操作不会成功执行。


大家帮看下问题存在哪里吧,小弟实在是无能为力了,谢谢大家了。

111111.png (67.46 KB, 下载次数: 20)

附件1

附件1

论坛徽章:
0
2 [报告]
发表于 2009-04-29 16:57 |只看该作者
父进程在操作,子进程干什么去了呢?

论坛徽章:
0
3 [报告]
发表于 2009-04-29 17:09 |只看该作者
其实,我贴出的代码只是一部分,子进程还要exec新的程序,包括与后台的数据库联系等等。在这里为了让大家把重点代码看看,就贴出了这里的一部分,不好意思哈

论坛徽章:
0
4 [报告]
发表于 2009-04-29 19:49 |只看该作者
大大们帮忙看看吧?小弟不胜感激

论坛徽章:
0
5 [报告]
发表于 2009-04-30 12:52 |只看该作者
这个问题关注下 

论坛徽章:
0
6 [报告]
发表于 2009-04-30 13:33 |只看该作者
每来一个连接  start_newsrv  启动一次是吗?

static int port = MYPORT;

这句放外面试试
panpass 该用户已被删除
7 [报告]
发表于 2009-05-04 16:55 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
8 [报告]
发表于 2009-05-04 17:11 |只看该作者
看起来很混乱

论坛徽章:
0
9 [报告]
发表于 2009-05-04 20:10 |只看该作者
晕,大哥,能不能保护好你的端口号呀。
port++;
        
        printf("prepare new port(in srv ):%d\n",port);
   
        bzero((char *) &newsrv_addr, sizeof(newsrv_addr));
        newsrv_addr.sin_family = AF_INET;
        newsrv_addr.sin_port = htons(port); //new port number

        newsrv_addr.sin_addr.s_addr = INADDR_ANY;
   
        if(    bind(sockfd,(struct sockaddr *)&newsrv_addr,
                    sizeof(newsrv_addr)) == -1){
                    
        perror("bind");
        exit(1);


        
有2种方式保护你的端口号:
1、 共享内存方式, 在加锁。
2、 用HASH函数重新创建一个port值。

可参见TFTP 代码,中有一个创建临时的端口号,代码如下,但方法是一致的,就是用HASH函数创建:
static int
pick_port_bind(int sockfd, struct sockaddr_in *myaddr)
{
  unsigned int port, firstport;

  firstport = portrange
    ? portrange_from + rand() % (portrange_to-portrange_from+1)
    : 0;

  port = firstport;

  do {
    myaddr->sin_port = htons(port);
   
    if (bind(sockfd, (struct sockaddr *)myaddr, sizeof *myaddr) < 0) {
      /* Some versions of Linux return EINVAL instead of EADDRINUSE */
      if ( !(portrange && (errno == EINVAL || errno == EADDRINUSE)) )
        return -1;

      /* Normally, we shouldn't have to loop, but some situations involving
         aborted transfers make it possible. */
    } else {
      return 0;
    }

    port++;
    if ( port > portrange_to )
      port = portrange_from;
  } while ( port != firstport );

  return -1;
}

论坛徽章:
0
10 [报告]
发表于 2009-05-04 20:45 |只看该作者
static int port = MYPORT; 不解? 什么意思。
为什么不是 int port =MYPORT ?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP