免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1911 | 回复: 9

[C] 一个基于tcp的socket代码,友们帮忙指点一下问题 [复制链接]

论坛徽章:
0
发表于 2012-07-29 21:19 |显示全部楼层
下面代码是局域网server同client相互发送数据代码,代码问题是为什么只能互相发送一次数据,再发送,对方就收不到了,求教!
代码如下:
/******* 服务器程序  (server.c) ************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int
main (int argc, char *argv[])
{
  int sockfd, new_fd, pid, nbytes;
  struct sockaddr_in server_addr;
  struct sockaddr_in client_addr;
  int sin_size, portnumber;
  char buffer[1024 * 1024] = { 0 };
  char hello[1024 * 1024] = { 0 };

  if (argc != 2)
    {
      fprintf (stderr, "Usage:%s portnumber\a\n", argv[0]);
      exit (1);
    }

  if ((portnumber = atoi (argv[1])) < 0)
    {
      perror ("atoi() error\n");
      exit (1);
    }

  /* 服务器端开始建立socket描述符 */
  if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket() error\n");
      exit (1);
    }

  /* 服务器端填充 sockaddr结构  */
  bzero (&server_addr, sizeof (struct sockaddr_in));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl (INADDR_ANY);
  server_addr.sin_port = htons (portnumber);

  /* 捆绑sockfd描述符  */
  if (bind
      (sockfd, (struct sockaddr *) (&server_addr),
       sizeof (struct sockaddr)) == -1)
    {
      perror ("bind() error\n");
      exit (1);
    }

  /* 监听sockfd描述符  */
  if (listen (sockfd, 5) == -1)
    {
      perror ("listen() error\n");
      exit (1);
    }

  while (1)
    {

      /* 服务器阻塞,直到客户程序建立连接  */
      sin_size = sizeof (struct sockaddr_in);
      if ((new_fd =
           accept (sockfd, (struct sockaddr *) (&client_addr),
                   &sin_size)) == -1)
        {
          perror ("accept() error\n");
          exit (1);
        }

      fprintf (stderr, "Server get connection from %s\n",
               inet_ntoa (client_addr.sin_addr));
      if ((pid = fork ()) < 0)
        {
          perror ("fork() error\n");
          exit (1);
        }
      else if (pid == 0)
        {
          if ((nbytes = read (new_fd, buffer, sizeof (buffer))) == -1)
            {
              perror ("read() error\n");
              exit (1);
            }
          buffer[nbytes] = '\0';
          fputs (buffer, stdout);
          close (new_fd);
        }
      else
        {
          fgets (hello, sizeof (hello), stdin);
          if (write (new_fd, hello, strlen (hello)) == -1)
            {
              perror ("write() error\n");
              exit (1);
            }

          /* 这个通讯已经结束     */
          close (new_fd);
        }
    }
  close (sockfd);
  exit (0);
}

=================================================================================

/******* 客户端程序  client.c ************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
int
main (int argc, char *argv[])
{
  int sockfd, pid;
  char buffer[1024 * 1024] = { 0 };
  char hello[1024 * 1024] = { 0 };
  struct sockaddr_in server_addr;
  struct hostent *host;
  int portnumber, nbytes;

  if (argc != 3)
    {
      fprintf (stderr, "Usage:%s hostname portnumber\a\n", argv[0]);
      exit (1);
    }

  if ((host = gethostbyname (argv[1])) == NULL)
    {
      perror ("gethostbyname() error\n");
      exit (1);
    }

  if ((portnumber = atoi (argv[2])) < 0)
    {
      perror ("atoi() error\n");
      exit (1);
    }

  /* 客户程序开始建立 sockfd描述符  */
  if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket() error\n");
      exit (1);
    }

  /* 客户程序填充服务端的资料       */
  bzero (&server_addr, sizeof (server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons (portnumber);
  server_addr.sin_addr = *((struct in_addr *) host->h_addr);

  /* 客户程序发起连接请求         */
  if (connect
      (sockfd, (struct sockaddr *) (&server_addr),
       sizeof (struct sockaddr)) < 0)
    {
      perror ("connect() error\n");
      exit (1);
    }
  /* 连接成功了           */
  if ((pid = fork ()) < 0)
    {
      perror ("fork() error\n");
    }
  else if (pid > 0)
    {

      while (1)
        {
          nbytes = read (sockfd, buffer, 1024);
          buffer[nbytes] = '\0';
          fputs (buffer, stdout);
        }
    }
  else
    {
      while (1)
        {
          fgets (hello, sizeof (hello), stdin);
          if (write (sockfd, hello, strlen (hello)) < 0)
            {
              perror ("write() error\n");
              exit (1);
            }

        }
    }
  /* 结束通讯     */
  close (sockfd);
  exit (0);
}

论坛徽章:
0
发表于 2012-07-30 01:31 来自手机 |显示全部楼层
server端接收数据之后,你关闭了socket,建立的连接就终止了,然后while循环继续,阻塞在accept处。client端write函数应该报错了吧。

论坛徽章:
208
巨蟹座
日期:2013-09-02 09:16:36卯兔
日期:2013-09-02 20:53:59酉鸡
日期:2013-09-05 21:21:45戌狗
日期:2013-10-15 20:51:17寅虎
日期:2013-10-18 21:13:16白羊座
日期:2013-10-23 21:15:19午马
日期:2013-10-25 21:22:48技术图书徽章
日期:2013-11-01 09:11:32双鱼座
日期:2013-11-01 20:29:44丑牛
日期:2013-11-01 20:40:00卯兔
日期:2013-11-11 09:21:32酉鸡
日期:2013-12-04 19:56:39
发表于 2012-07-30 08:52 |显示全部楼层
server的child/father各自在干吗?child close了,father还在等输入
father应该只是在等 ,child要干现在child/father的干活

论坛徽章:
0
发表于 2012-07-30 21:44 |显示全部楼层
client端没报错,就是一直死在那,数据发不出去,收不到,不知道是客户端,还是服务端的问题回复 2# 小兔吉


   

论坛徽章:
0
发表于 2012-07-30 21:47 |显示全部楼层
不太明白,send,reciver都在子进程内完成吗?回复 3# 流氓无产者


   

论坛徽章:
0
发表于 2012-07-30 23:30 |显示全部楼层
accept函数从监听队列中取出连接请求。server收到client信息后new_fd关闭,server与client断开连接。同时,new_fd关闭给该describer解锁,即可以修改new_fd指向的对象。而程序中并没有再次指定,所以它还是指向client_addr。由于之前的连接断开,server中子进程发送数据的方式其实是udp。因而客户端能够收到。而此后客户端再也没有connect服务器,因而服务器的监听队列中一直没有数据,即被阻塞在accpet处。

论坛徽章:
0
发表于 2012-07-31 00:53 |显示全部楼层
你的serve端与client端通信的处理不对称。

server端代码中,accept是处于while循环中的。
server端accept取出一个就绪的client连接,创建子进程读数据然后close socket,父进程则负责写数据然后close socket,因此进行一次读写数据之后,这个已连接的socket就无效了。进入下一次循环重新accept。

client端代码中,connect仅有一次,未处于循环中,也就是client这个进程与server只能建立一次连接。
建立连接后,由于server端进行一次读写之后关闭了已连接的socket,那么client端write函数实际上写入一个已关闭的socket句柄中,虽然没有报错。至于read函数,server端将socket关闭之后,应该会返回0,至于下一次read的行为,我也不清楚。
因此client端父子进程中的后续的read、write都与server端无关了。

不清楚你要写成那个样子。如果仅仅是一个client连接server的话,那server端也没必要把accept放到while中,只需要像client端那样将父子进程中read、write函数放到循环中处理就行了。你要处理多个client连接的话,那就把read、write处理都放到子进程中,父进程只负责循环accpet。

回复 4# 嵌入式教父


   

论坛徽章:
0
发表于 2012-07-31 12:52 |显示全部楼层
恩,这下明白了,非常感谢!讲的很透彻,让我对socket编程又有了更进一步的理解回复 7# 小兔吉


   

论坛徽章:
0
发表于 2012-07-31 12:54 |显示全部楼层
哦,明白了,这么个流程啊,谢谢,又学到不少 回复 6# xxwade


   

论坛徽章:
0
发表于 2012-08-01 09:39 |显示全部楼层
回复 6# xxwade


   正解, 每次调用服务端accept后,客户机都要调用connect去连接的。所以只要在服务端把accept移到while之前就应该可以了吧。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

DTCC2020中国数据库技术大会

【架构革新 高效可控】2020年12月21日-23日第十一届中国数据库技术大会将在北京隆重召开。

大会设置2大主会场,20+技术专场,将邀请超百位行业专家,重点围绕数据架构、AI与大数据、传统企业数据库实践和国产开源数据库等内容展开分享和探讨,为广大数据领域从业人士提供一场年度盛会和交流平台。

http://dtcc.it168.com


大会官网>>
  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP