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

//
InBlock.gif //这是个用fork多进程的网络服务端程序,使用了标准的编写模式流程,我用中文详细标注下解释,供个人与大众参考,有bug地方,愿大
InBlock.gif //家供探讨,参考steven的UNIX环境高级编程和UNIX网络编程。
InBlock.gif //MSN: [email]wu23qing45ying@163.com[/email] E-mail: [email]carywu@yahoo.cn[/email]
///
InBlock.gif
//
InBlock.gif //头文件
//
InBlock.gif#include <stdio.h>
InBlock.gif#include <stdlib.h>
InBlock.gif#include <unistd.h>
InBlock.gif#include < string.h>
InBlock.gif#include <sys/types.h>
InBlock.gif#include <sys/socket.h>
InBlock.gif#include <arpa/inet.h>
InBlock.gif#include <errno.h>
InBlock.gif
//
InBlock.gif //全局定义。
//
InBlock.gif#define LISTENQ 5
InBlock.gif#define BUFFER_SIZE 1024
InBlock.gif
int child_process( int fd);
InBlock.gif
//
InBlock.gif // 主函数
//
InBlock.gif int main ( int argc, char **argv)
InBlock.gif{
InBlock.gif     int global_connect_number = 0; //用于检测连接服务端的客户端个数。在父进程中获得数量。
InBlock.gif     //XXX: step 1: check the argc
InBlock.gif     if (argc < 3) //用于检测程序后的参数个数,没有IP和port就会出错退出。
InBlock.gif    {
InBlock.gif  fprintf(stderr, "Usage: %s<ipaddress><port>\n", strerror(errno));
InBlock.gif  exit(1);
InBlock.gif    }
InBlock.gif     //XXX: step 2: create a socket
InBlock.gif     int fd; //定义文件描述符。
InBlock.gif
     if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) //socket程序,成功返回文件描述符。你不知道什么是文件描述符?看UNIX网络编程去哦。
InBlock.gif    {
InBlock.gif  fprintf(stderr, "socket failed:%s\n", strerror(errno)); //错误处理,获得errno。
InBlock.gif  exit(1); //确实退出不是好办法,不过这只是学习程序,原谅下。
InBlock.gif    }
InBlock.gif
     //XXX: step 3: create a bind
InBlock.gif     struct sockaddr_in server_address; //定义bind使用的IP 地址结构。为什么用sockaddr_in而不是socketaddr?
InBlock.gif    memset(&server_address, 0, sizeof(server_address)); //初始化这个IP地址结构,用0填充。
InBlock.gif
    server_address.sin_family = PF_INET; //IPV4 address,
InBlock.gif    server_address.sin_port = htons(atoi(argv[2])); //因为字节序问题,需要用htons和atoi转换下端口。
InBlock.gif    server_address.sin_addr.s_addr = inet_addr(argv[1]); //同理。
InBlock.gif
     if (bind(fd, ( struct sockaddr *)&server_address, sizeof(server_address)) < 0) //bind程序,注意类型转换成sockaddr
InBlock.gif    {
InBlock.gif  fprintf(stderr, "bind failed:%s\n", strerror(errno)); //出错处理。
InBlock.gif  exit(1); // 不推荐这么使用。
InBlock.gif    }
InBlock.gif      
InBlock.gif     //XXX: step 4: create a listen
InBlock.gif     if (listen(fd, LISTENQ) < 0) //listen函数,监听此文件描述符对应的socket绑定IP和port。
InBlock.gif    {
InBlock.gif  fprintf(stderr, "listen failed:%s\n", strerror(errno));
InBlock.gif  exit(1);
InBlock.gif    }
InBlock.gif     else
InBlock.gif    {
InBlock.gif  fprintf(stdout, "socket is listening the port:%s:%d\n", argv[1],atoi(argv[2])); // 显示成功。
InBlock.gif    }
InBlock.gif     //XXX: step 5: create a accept
InBlock.gif     int new_fd;
InBlock.gif    socklen_t address_len; //定义地址大小,socklen_t类型。
InBlock.gif     struct sockaddr_in remote_address; //定义远程客户端的ip地址
InBlock.gif    address_len = sizeof(remote_address); //为什么这么用?后面可以看到。
InBlock.gif
     for(;;) //for循环,
InBlock.gif    {    
InBlock.gif     if ((new_fd = accept(fd, ( struct sockaddr *)&remote_address,&address_len)) < 0) //accept语句,接收远端地址。
InBlock.gif    {
InBlock.gif  fprintf(stderr, "accept failed():%s\n", strerror(errno));
InBlock.gif  exit(1);
InBlock.gif    }
InBlock.gif     else
InBlock.gif    {
InBlock.gif  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)); //成功显示的信息,注意字节序问题。
InBlock.gif
    }    
InBlock.gif     //XXX: step 6: create a fork
InBlock.gif    pid_t pid;
InBlock.gif
     if ((pid = fork()) < 0) //用fork新建立子进程,用子进程处理读和写的问题。
InBlock.gif    {
InBlock.gif  fprintf(stderr, "fork() failed:%s\n", strerror(errno));
InBlock.gif  exit(1);
InBlock.gif    }
InBlock.gif     else if (pid == 0) //子进程,
InBlock.gif    {
InBlock.gif   //CHILD PROCESS
InBlock.gif  child_process(new_fd); // 处理函数,下面有,也可以不写成函数,但哪个好?不要偶说了。
InBlock.gif  exit(0);
InBlock.gif    }
InBlock.gif     else //父进程。
InBlock.gif    {
InBlock.gif   //PARENT PROCESS
InBlock.gif  fprintf(stdout, "%d have connection of %d\n", getpid(), global_connect_number++); //看到了吧,显示连接数量。
InBlock.gif
    }
InBlock.gif    }
InBlock.gif
    close(fd); //记得关闭描述符哦。
InBlock.gif    close(new_fd);
InBlock.gif     return 0;
InBlock.gif}
InBlock.gif

InBlock.gif int child_process( int fd) //子程序的读写处理函数。
InBlock.gif{
InBlock.gif    ssize_t written; //写程序返回的写词的个数。
InBlock.gif     char *request = "Hello, welcome to my char program!\n"; //算是给客户端的返回信息了。
InBlock.gifagain:
InBlock.gif     if ((written = write (fd, request, strlen(request))) < 0) // 写程序,也可以用send()代替.
InBlock.gif    
InBlock.gif    {
InBlock.gif   if (errno == EINTR) //碰到信号中断,执行again工作。
InBlock.gif  {
InBlock.gif      fprintf(stderr, "write was closed by signal\n");
InBlock.gif       goto again;
InBlock.gif  }
InBlock.gif   else
InBlock.gif  {
InBlock.gif      fprintf(stderr, "write failed:%s\n", strerror(errno)); //出错信息。
InBlock.gif      exit(1);
InBlock.gif  }
InBlock.gif    }
InBlock.gif     else
InBlock.gif    {
InBlock.gif  fprintf(stdout, "write successd!\n"); //成功信息,这比较简单,能写多点功能更好。我懒。
InBlock.gif    }
InBlock.gif    
InBlock.gif     char buffer[BUFFER_SIZE]; //从监控端口读到的信息存放点。
InBlock.gif    ssize_t n; //读到的数。
InBlock.gif    
InBlock.gif     for(;;) //又一个for循环,
InBlock.gif    {
InBlock.gif
     if ((n = read (fd, buffer, BUFFER_SIZE)) < 0) //读程序,从fd文件描述符中读数据,存到buffer中。
InBlock.gif    {
InBlock.gif  fprintf(stderr, "recv() failed:%s\n", strerror(errno));
InBlock.gif  exit(1);
InBlock.gif    }
InBlock.gif     else if (n == 0) //读完了的处理。
InBlock.gif    {
InBlock.gif  fprintf(stderr, "connection was closed by peer\n");
InBlock.gif  close(fd);
InBlock.gif   break;
InBlock.gif    }
InBlock.gif     else
InBlock.gif
    {
InBlock.gif  buffer[n] = '\0'; //有这个才不会出现下次读信息时出现数据不匹配的问题。
InBlock.gif   struct sockaddr_in peer_address;
InBlock.gif  socklen_t peer_len;
InBlock.gif  peer_len = sizeof(peer_address);
InBlock.gif
   if (getpeername(fd, ( struct sockaddr *)&peer_address, &peer_len) < 0) //获得ip结构信息。
InBlock.gif  {
InBlock.gif      fprintf(stderr, "getpeername() failed:%s\n", strerror(errno));
InBlock.gif      exit(1);
InBlock.gif  }
InBlock.gif   else
InBlock.gif  {
InBlock.gif      fprintf(stdout, "On socket %d(%s:%d):%s\n", fd,inet_ntoa(peer_address.sin_addr),ntohs(peer_address.sin_port), buffer); //读成功时的输出,显示远程IP地址和端口。
InBlock.gif  }
InBlock.gif

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

InBlock.gif// 客户端程序。
#include <stdio.h>
InBlock.gif#include <stdlib.h>
InBlock.gif#include <unistd.h>
InBlock.gif#include <sys/types.h>
InBlock.gif#include <sys/socket.h>
InBlock.gif#include <arpa/inet.h>
InBlock.gif#include < string.h>
InBlock.gif#include <errno.h>
InBlock.gif
#define BUFFER_SIZE 1024
InBlock.gif
int
InBlock.gifmain ( int argc, char **argv)
InBlock.gif{
InBlock.gif   //XXX: step 1: check the argc
InBlock.gif   if (argc < 3)
InBlock.gif    {
InBlock.gif      fprintf (stderr, "Usage: %s<ipaddress><port>\n", strerror (errno));
InBlock.gif      exit (1);
InBlock.gif    }
InBlock.gif
   //XXX: step 2: create a socket
InBlock.gif   int fd;
InBlock.gif
   if ((fd = socket (PF_INET, SOCK_STREAM, 0)) < 0)
InBlock.gif    {
InBlock.gif      fprintf (stderr, "socket failed():%s\n", strerror (errno));
InBlock.gif      exit (1);
InBlock.gif    }
InBlock.gif
  fprintf (stdout, "successd create a socket %d\n", fd);
InBlock.gif
   //XXX: step 3: create a bind (optinal)
InBlock.gif   struct sockaddr_in remote_address;
InBlock.gif  memset (&remote_address, 0, sizeof (remote_address));
InBlock.gif
  remote_address.sin_family = PF_INET;
InBlock.gif  remote_address.sin_port = htons (atoi (argv[2]));
InBlock.gif  remote_address.sin_addr.s_addr = inet_addr (argv[1]);
InBlock.gif

InBlock.gif   //XXX: step 4: create a connect
InBlock.gif   if (connect
InBlock.gif      (fd, ( struct sockaddr *) &remote_address, sizeof (remote_address)) < 0)
InBlock.gif    {
InBlock.gif      fprintf (stderr, "connect failed:%s\n", strerror (errno));
InBlock.gif      exit (1);
InBlock.gif    }
InBlock.gif
  fprintf (stdout, "connect (%s:%d) successd\n", argv[1], atoi (argv[2]));
InBlock.gif
   char buffer[BUFFER_SIZE];
InBlock.gif  ssize_t n;
InBlock.gif  ssize_t written;
InBlock.gif
   for (;;)
InBlock.gif    {
InBlock.gif       if ((n = read (fd, buffer, BUFFER_SIZE)) < 0)
InBlock.gif    {
InBlock.gif   fprintf (stderr, "read failed:%s\n", strerror (errno));
InBlock.gif   exit (1);
InBlock.gif
    }
InBlock.gif       else if (n == 0)
InBlock.gif    {
InBlock.gif   break;
InBlock.gif    }
InBlock.gif       else
InBlock.gif    {
InBlock.gif   buffer[n] = '\0';
InBlock.gif   fprintf (stdout, "%s\n", buffer);
InBlock.gif   fflush (stdout);
InBlock.gif    }
InBlock.gif

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