多进程的简单网络聊天程序-源代码。

简介:
这个是偶在unix下编写的多进程网络通讯程序,不能算是个聊天程序,因为它只实现了单向信息传送功能,使用了最常见的fork产生多进程避免了阻塞。偶的练笔作,bug太多,不过还好编译运行成功。里面偶花了点时间用中文注释了下,供以后自己参考下。有朋友愿意探讨的可互相联系。废话少说,源代码供上,批注:服务端偶中文注释了,客户端就算了,大同小异。只是少了listen和accept,多了个connect而已。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//这是个用fork多进程的网络服务端程序,使用了标准的编写模式流程,我用中文详细标注下解释,供个人与大众参考,有bug地方,愿大
//家供探讨,参考steven的UNIX环境高级编程和UNIX网络编程。
//MSN: [email]wu23qing45ying@163.com[/email] E-mail: [email]carywu@yahoo.cn[/email]
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////
//头文件
//////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include < string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>

//////////////////////////////////////////////////
//全局定义。
//////////////////////////////////////////////////
#define LISTENQ 5
#define BUFFER_SIZE 1024

int child_process( int fd);

//////////////////////////////////////////////////
// 主函数
//////////////////////////////////////////////////
int main ( int argc,  char **argv)
{
     int global_connect_number = 0;  //用于检测连接服务端的客户端个数。在父进程中获得数量。
     //XXX: step 1: check the argc
     if (argc < 3)  //用于检测程序后的参数个数,没有IP和port就会出错退出。
    {
  fprintf(stderr,  "Usage: %s<ipaddress><port>\n", strerror(errno));
  exit(1);
    }
     //XXX: step 2: create a socket
     int fd;  //定义文件描述符。

     if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)  //socket程序,成功返回文件描述符。你不知道什么是文件描述符?看UNIX网络编程去哦。
    {
  fprintf(stderr,  "socket failed:%s\n", strerror(errno));  //错误处理,获得errno。
  exit(1);  //确实退出不是好办法,不过这只是学习程序,原谅下。
    }

     //XXX: step 3: create a bind
     struct sockaddr_in server_address;  //定义bind使用的IP 地址结构。为什么用sockaddr_in而不是socketaddr? 
    memset(&server_address, 0,  sizeof(server_address));  //初始化这个IP地址结构,用0填充。

    server_address.sin_family = PF_INET;  //IPV4 address,
    server_address.sin_port = htons(atoi(argv[2])); //因为字节序问题,需要用htons和atoi转换下端口。
    server_address.sin_addr.s_addr = inet_addr(argv[1]); //同理。

     if (bind(fd, ( struct sockaddr *)&server_address,  sizeof(server_address)) < 0)  //bind程序,注意类型转换成sockaddr
    {
  fprintf(stderr,  "bind failed:%s\n", strerror(errno));  //出错处理。
  exit(1); // 不推荐这么使用。
    }
      
     //XXX: step 4: create a listen
     if (listen(fd, LISTENQ) < 0) //listen函数,监听此文件描述符对应的socket绑定IP和port。
    {
  fprintf(stderr, "listen failed:%s\n", strerror(errno));
  exit(1);
    }
     else
    {
  fprintf(stdout,  "socket is listening the port:%s:%d\n", argv[1],atoi(argv[2])); // 显示成功。
    }
     //XXX: step 5: create a accept
     int new_fd;
    socklen_t address_len;  //定义地址大小,socklen_t类型。
     struct sockaddr_in remote_address;  //定义远程客户端的ip地址
    address_len =  sizeof(remote_address);  //为什么这么用?后面可以看到。

     for(;;)  //for循环,
    {    
     if ((new_fd = accept(fd, ( struct sockaddr *)&remote_address,&address_len)) < 0)  //accept语句,接收远端地址。
    {
  fprintf(stderr,  "accept failed():%s\n", strerror(errno));
  exit(1);
    }
     else
    {
  fprintf(stdout,  "accept a new TCP connect %d from %s:%d\n", new_fd, inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port));  //成功显示的信息,注意字节序问题。

    }    
     //XXX: step 6: create a fork
    pid_t pid;

     if ((pid = fork()) < 0)  //用fork新建立子进程,用子进程处理读和写的问题。
    {
  fprintf(stderr,  "fork() failed:%s\n", strerror(errno));
  exit(1);
    }
     else  if (pid == 0)  //子进程,
    {
   //CHILD PROCESS
  child_process(new_fd); // 处理函数,下面有,也可以不写成函数,但哪个好?不要偶说了。
  exit(0);
    }
     else  //父进程。
    {
   //PARENT PROCESS
  fprintf(stdout,  "%d have connection of %d\n", getpid(), global_connect_number++); //看到了吧,显示连接数量。

    }
    }

    close(fd);  //记得关闭描述符哦。
    close(new_fd);
     return 0;
}


int child_process( int fd)  //子程序的读写处理函数。
{
    ssize_t written;  //写程序返回的写词的个数。
     char *request =  "Hello, welcome to my char program!\n"; //算是给客户端的返回信息了。
again:
     if ((written = write (fd, request, strlen(request))) < 0) // 写程序,也可以用send()代替.
    
    {
   if (errno == EINTR)  //碰到信号中断,执行again工作。
  {
      fprintf(stderr,  "write was closed by signal\n");
       goto again;
  }
   else
  {
      fprintf(stderr,  "write failed:%s\n", strerror(errno));  //出错信息。
      exit(1);
  }
    }
     else
    {
  fprintf(stdout,  "write successd!\n");  //成功信息,这比较简单,能写多点功能更好。我懒。
    }
    
     char buffer[BUFFER_SIZE];  //从监控端口读到的信息存放点。
    ssize_t n;  //读到的数。
    
     for(;;)  //又一个for循环,
    {

     if ((n = read (fd, buffer, BUFFER_SIZE)) < 0)  //读程序,从fd文件描述符中读数据,存到buffer中。
    {
  fprintf(stderr,  "recv() failed:%s\n", strerror(errno));
  exit(1);
    }
     else  if (n == 0)  //读完了的处理。
    {
  fprintf(stderr,  "connection was closed by peer\n");
  close(fd);
   break;
    }
     else

    {
  buffer[n] = '\0'; //有这个才不会出现下次读信息时出现数据不匹配的问题。
   struct sockaddr_in peer_address;
  socklen_t peer_len;
  peer_len =  sizeof(peer_address);

   if (getpeername(fd, ( struct sockaddr *)&peer_address, &peer_len) < 0)  //获得ip结构信息。
  {
      fprintf(stderr,  "getpeername() failed:%s\n", strerror(errno));
      exit(1);
  }
   else
  {
      fprintf(stdout,  "On socket %d(%s:%d):%s\n", fd,inet_ntoa(peer_address.sin_addr),ntohs(peer_address.sin_port), buffer);  //读成功时的输出,显示远程IP地址和端口。
  }


    }
    }
    
    close(fd); //关闭文件描述符。
    free(buffer); //清空缓存。避免出现内存泄漏。
     return 0;
}

// 客户端程序。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include < string.h>
#include <errno.h>

#define BUFFER_SIZE 1024

int
main ( int argc,  char **argv)
{
   //XXX: step 1: check the argc
   if (argc < 3)
    {
      fprintf (stderr,  "Usage: %s<ipaddress><port>\n", strerror (errno));
      exit (1);
    }

   //XXX: step 2: create a socket
   int fd;

   if ((fd = socket (PF_INET, SOCK_STREAM, 0)) < 0)
    {
      fprintf (stderr,  "socket failed():%s\n", strerror (errno));
      exit (1);
    }

  fprintf (stdout,  "successd create a socket %d\n", fd);

   //XXX: step 3: create a bind (optinal)
   struct sockaddr_in remote_address;
  memset (&remote_address, 0,  sizeof (remote_address));

  remote_address.sin_family = PF_INET;
  remote_address.sin_port = htons (atoi (argv[2]));
  remote_address.sin_addr.s_addr = inet_addr (argv[1]);


   //XXX: step 4: create a connect
   if (connect
      (fd, ( struct sockaddr *) &remote_address,  sizeof (remote_address)) < 0)
    {
      fprintf (stderr,  "connect failed:%s\n", strerror (errno));
      exit (1);
    }

  fprintf (stdout,  "connect (%s:%d) successd\n", argv[1], atoi (argv[2]));

   char buffer[BUFFER_SIZE];
  ssize_t n;
  ssize_t written;

   for (;;)
    {
       if ((n = read (fd, buffer, BUFFER_SIZE)) < 0)
    {
   fprintf (stderr,  "read failed:%s\n", strerror (errno));
   exit (1);

    }
       else  if (n == 0)
    {
    break;
    }
       else
    {
   buffer[n] = '\0';
   fprintf (stdout,  "%s\n", buffer);
   fflush (stdout);
    }


       while (fgets (buffer, BUFFER_SIZE, stdin) != NULL)
    {
    again:
    if ((written = write (fd, buffer, strlen(buffer))) < 0)
     {
        if (errno == EINTR)
  {
    fprintf (stderr,  "write was closed by signal\n");
     goto again;
  }
        else
  {
    fprintf (stderr,  "write failed:%s\n", strerror (errno));
    exit (1);
  }
     }
    else
     {
       fprintf (stdout,  "write %d bytes to remote address:(%s:%d)\n",
         written, argv[1], atoi (argv[2]));
     }

    //XXX: step 7: create a read
    }
    }
  close (fd);
   return 0;
}







 本文转自 wuqingying 51CTO博客,原文链接:http://blog.51cto.com/carywu/89241 ,如需转载请自行联系原作者
相关文章
用MASM32按Time Protocol(RFC868)协议编写网络对时程序中的一些有用的函数代码
用MASM32按Time Protocol(RFC868)协议编写网络对时程序中的一些有用的函数代码
|
3月前
|
网络协议 Linux
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
550 2
|
3月前
|
Linux Python
linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间
linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间
64 2
|
27天前
|
安全 API C#
C# 如何让程序后台进程不被Windows任务管理器强制结束
C# 如何让程序后台进程不被Windows任务管理器强制结束
54 0
|
28天前
|
安全 网络协议 IDE
使用Python编写网络扫描程序
使用Python编写网络扫描程序
35 0
|
2月前
|
Python
惊!Python进程间通信IPC,让你的程序秒变社交达人,信息畅通无阻
【9月更文挑战第13天】在编程的世界中,进程间通信(IPC)如同一场精彩的社交舞会,每个进程通过优雅的IPC机制交换信息,协同工作。本文将带你探索Python中的IPC奥秘,了解它是如何让程序实现无缝信息交流的。IPC如同隐形桥梁,连接各进程,使其跨越边界自由沟通。Python提供了多种IPC机制,如管道、队列、共享内存及套接字,适用于不同场景。通过一个简单的队列示例,我们将展示如何使用`multiprocessing.Queue`实现进程间通信,使程序如同社交达人般高效互动。掌握IPC,让你的程序在编程舞台上大放异彩。
21 3
|
3月前
|
存储 网络协议 安全
|
3月前
|
数据采集 监控 API
如何监控一个程序的运行情况,然后视情况将进程杀死并重启
这篇文章介绍了如何使用Python的psutil和subprocess库监控程序运行情况,并在程序异常时自动重启,包括多进程通信和使用日志文件进行断点重续的方法。
|
3月前
|
算法 安全 网络安全
探索操作系统核心:进程调度的奥秘网络安全的盾牌与剑——漏洞防御与加密技术
【8月更文挑战第30天】在数字世界的每一次点击和命令背后,都隐藏着一个不为人知的英雄——进程调度器。它默默无闻地在后台工作,确保我们的命令得以流畅执行。本文将揭开这位幕后英雄的面纱,带你了解进程调度的原理、重要性以及它是如何在操作系统中发挥作用的。无论你是编程新手还是资深开发者,理解进程调度都能帮你更好地掌握计算机的运作原理。准备好深入操作系统的核心,一探究竟了吗?让我们开始吧!
|
3月前
|
Python
惊!Python进程间通信IPC,让你的程序秒变社交达人,信息畅通无阻
【8月更文挑战第1天】在编程世界中,进程间通信(IPC)犹如一场社交舞会,各进程通过IPC机制优雅地交换信息,共同完成复杂任务。IPC就像隐形桥梁,连接并行运行的进程,使它们能跨越边界自由沟通。Python提供了多种IPC机制,如管道、队列、共享内存和套接字等,适应不同需求。例如,使用`multiprocessing.Queue`实现进程间通信,生产者向队列添加数据,消费者取出并处理数据,两者虽独立却能有效协作。IPC打破了进程界限,使得程序能像社交达人般自由交流,构建出高效、灵活的应用。掌握IPC,让程序信息畅通无阻。
25 1