LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 2011|回复: 11

[求助]是否可以用2个listen()来监听两个端口?

[复制链接]
发表于 2006-3-27 12:04:03 | 显示全部楼层 |阅读模式
是否可以用2个listen()来监听两个端口?

比如监听一个本机IP如192.168.0.1在端口3490是否有读的请求;
同时监听一个本机IP如192.168.0.2在端口9034是否有写的请求。

然后通过select()来处理。

刚开始学socket编程,谢谢各位DX
[PHP]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
//#include <fcntl.h>

int
main (void)
{
  int s1, s2;
  int ss1, ss2;
  int n;
  int rv;
  char buf[256];
  struct sockaddr_in recv_addr;
  struct sockaddr_in recv_remote_addr;
  struct sockaddr_in send_addr;
  struct sockaddr_in send_remote_addr;
  socklen_t sin_size;
  int yes = 1;
  fd_set readfds;
  fd_set writefds;
  struct timeval tv;

  if ((s1 = socket (PF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket s1");
      exit (1);
    }
  //fcntl(s1, F_SETFL, O_NONBLOCK);
  if ((s2 = socket (PF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket s2");
      exit (1);
    }
   //fcntl(s2, F_SETFL, O_NONBLOCK);

  if (setsockopt (s1, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == -1)
    {
      perror ("setsockopt s1");
      exit (1);
    }
  if (setsockopt (s2, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == -1)
    {
      perror ("setsockopt s2");
      exit (1);
    }

  recv_addr.sin_family = AF_INET;
  recv_addr.sin_port = htons (3490);
  //recv_addr.sin_addr.s_addr = inet_addr ("127.0.0.1");
  inet_aton("127.0.0.1", &(recv_addr.sin_addr));
  memset(&(recv_addr.sin_zero), '\0', 8);

  send_addr.sin_family = AF_INET;
  send_addr.sin_port = htons (9034);
  //send_addr.sin_addr.s_addr = inet_addr ("127.0.0.1");
  inet_aton("127.0.0.1", &(send_addr.sin_addr));
  memset(&(send_addr.sin_zero), '\0', 8);

  if (bind (s1, (struct sockaddr *) &recv_addr, sizeof (struct sockaddr)) ==
      -1)
    {
      perror ("bind s1");
      exit (1);
    }
  if (bind (s2, (struct sockaddr *) &send_addr, sizeof (struct sockaddr)) ==
      -1)
    {
      perror ("bind s2");
      exit (1);
    }

  //clear the set ahead of time
  FD_ZERO (&readfds);
  FD_ZERO (&writefds);

  //listen (s1, 10);
  //listen (s2, 10);

  //add our descriptors to the set
  //FD_SET (s1, &readfds);
  //FD_SET (s2, &writefds);

  //since we got s2 second, it's the "greater",
  //so we use that for the n param in select()
  //n = s2 + 1;
  //tv.tv_sec = 2;
  //tv.tv_usec = 1000000;

  //监听s1,s2看s1是否有读请求,s2是否有写请求,然后分别响应请求
  for (;;)
    {
      listen (s1, 10);
      listen (s2, 10);

      FD_SET (s1, &readfds);
      FD_SET (s2, &writefds);

      n = s2 + 1;
      tv.tv_sec = 2;
      tv.tv_usec = 1000000;
      rv = select (n, &readfds, &writefds, NULL, &tv);
      if (rv == -1)
        {
          perror ("select");        //error occurred in select()
        }
      else if (rv == 0)
        {
          printf ("Timeout occurred! No data after 3 seconds.\n");
        }
      else
        {
          if (FD_ISSET (s1, &readfds))
            {
              if ((ss1 =
                   accept (s1, (struct sockaddr *) &recv_remote_addr,
                           &sin_size)) == -1)
                {
                  perror ("accept ss1");
                }
              if (recv (ss1, buf, sizeof (buf), 0) == -1)
                {
                  perror ("recv ss1");
                }
              else
                {
                  printf ("We got: %s\n", buf);
                }
              close (ss1);
            }
          if (FD_ISSET (s2, &writefds))
            {
              if ((ss2 =
                   accept (s2, (struct sockaddr *) &send_remote_addr,
                           &sin_size)) == -1)
                {
                  perror ("accept ss2");
                }
                if (send (ss2, "Hello world!\n", 14, 0)==-1)
              //if (send (ss2, buf, sizeof (buf), 0) == -1)
                {
                  perror ("send ss2");
                }
              else
                {
                  printf ("We send: %s\n", buf);
                }
              close (ss2);
            }
        }
    }
  return 0;
}

[/PHP]
 楼主| 发表于 2006-3-27 12:07:59 | 显示全部楼层
然后做了个简单测试的send程序,如下:
[PHP]
/*
** send.c -- a stream send
*/

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

int
main (void)
{
  int s;
  int numbytes;
  struct sockaddr_in server_addr;

  if ((s = socket (PF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket");
      exit (1);
    }

  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons (3490);
  inet_aton ("127.0.0.1", &(server_addr.sin_addr));
  memset (&(server_addr.sin_zero), '\0', 8);

  if (connect (s, (struct sockaddr *) &server_addr, sizeof (struct sockaddr))
      == -1)
    {
      perror ("connect");
      exit (1);
    }
  if ((numbytes = send (s, "Hello world!\n", 14, 0)) == -1)
    {
      perror ("send");
      exit (1);
    }
  printf ("We send: Hello world!\n");
  close (s);
  return 0;
}
[/PHP]

另一个简单的recv测试程序如下:
[PHP]
/*
** send.c -- a stream recv
*/

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

int
main (void)
{
  int s;
  int numbytes;
  char buf[256];
  struct sockaddr_in server_addr;

  if ((s = socket (PF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket");
      exit (1);
    }

  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons (9034);
  inet_aton ("127.0.0.1", &(server_addr.sin_addr));
  memset (&(server_addr.sin_zero), '\0', 8);

  if (connect (s, (struct sockaddr *) &server_addr, sizeof (struct sockaddr))
      == -1)
    {
      perror ("connect");
      exit (1);
    }
  if ((numbytes = recv (s, buf, sizeof(buf), 0)) == -1)
    {
      perror ("recv");
      exit (1);
    }
  printf ("We got: %s\n", buf);
  close (s);
  return 0;
}

[/PHP]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-3-27 12:13:35 | 显示全部楼层
测试结果:
send程序一切正常;
recv程序,在gdb跟踪后发现可以connect成功,然后就timeout.

不知何故,各位DX帮帮忙看看,谢谢!

参考的资料是来自http://beej.us/guide/bgnet/的《Beej's Guide to Network Programming》
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-3-27 16:13:36 | 显示全部楼层
可能问得太幼稚了,
问题解决:
只监听有写请求端口,另一端读请求是要求读取写请求端写入数据。
因此不监听,一旦写请求端有数据写入,自动往另一端发送。

PS,
如果读请求端不是读该端写入数据的话,这样还是解决不了问题。
望DX指教!
回复 支持 反对

使用道具 举报

发表于 2006-3-27 19:32:31 | 显示全部楼层
select 函数里面的读写的含义你没搞清楚,select 应该是这样的:
  1. fd_set fds[2] = {readfds,writefds};
  2. rv = select (n, fds , NULL, NULL, &tv);
复制代码
因为你是在listen的时候select的,不是在accept之后select的。
回复 支持 反对

使用道具 举报

发表于 2006-3-28 16:29:07 | 显示全部楼层
Post by rickxbx
select 函数里面的读写的含义你没搞清楚,select 应该是这样的:

  1. fd_set fds[2] = {readfds,writefds};
  2. rv = select (n, fds , NULL, NULL, &tv);
复制代码

因为你是在listen的时候select的,不是在accept之后select的。

rickxbx的说法基本正确,这个程序的错误也基本上就处在这里,但是还没有完全……
要修改这个程序,最最简单的办法就是把server的代码里面所有的writefds改成readfds,这样应该就能满足搂主的要求了。至少看上去是。
select可以用来监听两个端口。正常情况下,accept会阻塞,select就是用来看一些socket是否就绪,对就绪的socket来读数据就不会阻塞了,把一个处于listen状态的socket放在readfds里面,当有新的连接请求的时候select会返回,这时候调用accept就会立即返回。用这种办法可以同时listen多个socket。
另外,在一个已经连接的socket上面accept可以看socket里面是否有数据可以读或者写,所以建议把ss1也放到readfds里面,否则如果你的send程序只connect不send,你的server就动不了了……
回复 支持 反对

使用道具 举报

发表于 2006-3-28 17:20:03 | 显示全部楼层
hehe, I am so careless
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-3-29 08:35:56 | 显示全部楼层
多谢版主和aliff兄指点!!
按照这句话“在一个已经连接的socket上面accept可以看socket里面是否有数据可以读或者写”修改程序后实现了同时监听两个端口的要求!
修改后的程序如下:
修改三处,已在修改处标注。
留做以后遗忘备查:)
[PHP]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
//#include <fcntl.h>

int
main (void)
{
  int s1, s2;
  int ss1, ss2;
  int n;
  int rv;
  char buf[256];
  struct sockaddr_in recv_addr;
  struct sockaddr_in recv_remote_addr;
  struct sockaddr_in send_addr;
  struct sockaddr_in send_remote_addr;
  socklen_t sin_size;
  int yes = 1;
  fd_set readfds;
  fd_set writefds;
  struct timeval tv;

  if ((s1 = socket (PF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket s1");
      exit (1);
    }
  //fcntl(s1, F_SETFL, O_NONBLOCK);
  if ((s2 = socket (PF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket s2");
      exit (1);
    }
  //fcntl(s2, F_SETFL, O_NONBLOCK);

  if (setsockopt (s1, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == -1)
    {
      perror ("setsockopt s1");
      exit (1);
    }
  if (setsockopt (s2, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == -1)
    {
      perror ("setsockopt s2");
      exit (1);
    }

  recv_addr.sin_family = AF_INET;
  recv_addr.sin_port = htons (3490);
  //recv_addr.sin_addr.s_addr = inet_addr ("127.0.0.1");
  inet_aton ("127.0.0.1", &(recv_addr.sin_addr));
  memset (&(recv_addr.sin_zero), '\0', 8);

  send_addr.sin_family = AF_INET;
  send_addr.sin_port = htons (9034);
  //send_addr.sin_addr.s_addr = inet_addr ("127.0.0.1");
  inet_aton ("127.0.0.1", &(send_addr.sin_addr));
  memset (&(send_addr.sin_zero), '\0', 8);

  if (bind (s1, (struct sockaddr *) &recv_addr, sizeof (struct sockaddr)) ==
      -1)
    {
      perror ("bind s1");
      exit (1);
    }
  if (bind (s2, (struct sockaddr *) &send_addr, sizeof (struct sockaddr)) ==
      -1)
    {
      perror ("bind s2");
      exit (1);
    }

  //clear the set ahead of time
  FD_ZERO (&readfds);
  FD_ZERO (&writefds);

  //listen (s1, 10);
  //listen (s2, 10);

  //add our descriptors to the set
  //FD_SET (s1, &readfds);
  //FD_SET (s2, &writefds);

  //since we got s2 second, it's the "greater",
  //so we use that for the n param in select()
  //n = s2 + 1;
  //tv.tv_sec = 2;
  //tv.tv_usec = 1000000;

  //监听s1,s2看s1是否有读请求,s2是否有写请求,然后分别响应请求
  for (;;)
    {
      listen (s1, 10);
      listen (s2, 10);

      FD_SET (s1, &readfds);
      FD_SET (s2, &readfds);  //writefds-->readfds

      n = s2 + 1;
      tv.tv_sec = 2;
      tv.tv_usec = 1000000;
      rv = select (n, &readfds, NULL, NULL, &tv);  //&writefds-->>NULL
      if (rv == -1)
        {
          perror ("select");        //error occurred in select()
        }
      else if (rv == 0)
        {
          printf ("Timeout occurred! No data after 3 seconds.\n");
        }
      else
        {
          if (FD_ISSET (s1, &readfds))
            {
              if ((ss1 =
                   accept (s1, (struct sockaddr *) &recv_remote_addr,
                           &sin_size)) == -1)
                {
                  perror ("accept ss1");
                }
              if (recv (ss1, buf, sizeof (buf), 0) == -1)
                {
                  perror ("recv ss1");
                }
              else
                {
                  printf ("We got: %s\n", buf);
                }
              close (ss1);
            }
          if (FD_ISSET (s2, &readfds))  //writefds-->readfds
            {
              if ((ss2 =
                   accept (s2, (struct sockaddr *) &send_remote_addr,
                           &sin_size)) == -1)
                {
                  perror ("accept ss2");
                }
              if (send (ss2, "Hello world!\n", 14, 0) == -1)
                //if (send (ss2, buf, sizeof (buf), 0) == -1)
                {
                  perror ("send ss2");
                }
              else
                {
                  printf ("We send: %s\n", buf);
                }
              close (ss2);
            }
        }
    }
  return 0;
}

[/PHP]
回复 支持 反对

使用道具 举报

发表于 2006-3-29 11:01:59 | 显示全部楼层
你辜负了aliff兄的一番好意,他的说话重点在
另外,在一个已经连接的socket上面accept可以看socket里面是否有数据可以读或者写,所以建议把ss1也放到readfds里面,否则如果你的send程序只connect不send,你的server就动不了了……
回复 支持 反对

使用道具 举报

发表于 2011-7-20 14:19:56 | 显示全部楼层
  1.   
  2. for (;;)
  3.     {
  4.       listen (s1, 10);
  5.       listen (s2, 10);
复制代码


listen是否应该放到for循环的上面去呢?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表