Linux中BSD套接口开发的基础介绍_unix linux

    这是关于如何用各种可以得到的接口为Linux开发网络程序的系列文章的第一篇。就像大多数Unix-based的操作系统一样,Linux支持将TCP/IP作为本地的网络传输协议。在这个系列中,我们假定你已经比较熟悉Linux上的C编程和Linux的一些系统知识诸如signals,forking等等。

  这篇文章是关于如何用BSD套接口创建网络程序的基础介绍 。在下一篇中,我们会解决涉及到建立(网络)deamon进程的问题。而且今后的文章我们还会涉及到使用远程过程调用(RPC),以及用CORBA/distributed objects进行开发。

  一、TCP/IP的基础介绍

  TCP/IP协议族允许两个运行在同一台电脑或者由网络连接在一起的两台电脑上的程序进行通讯。这个协议族是专门为了在不可靠的网络上进行通讯设计的。TCP/IP允许两个基本的操作模式——面向连接的可靠的传输(指TCP)和无连接的(connectionless)不可靠的传输(UDP)。

  TCP提供带有对上层协议透明的中继功能的,顺序的,可靠的,双向的(bi-directional),以连接为基础的字节传输流。TCP将你的信息分割成数据报(不大于64kb)并保证所有的数据报无误的按照顺序都到达目的地。由于以连接为基础,所以一个虚拟连接必须在一个网络实体(network entity)和另一个之间进行通信前建立。UDP相反则提供一个(非常快的)无连接的不可靠消息传输(消息的大小是一个确定的最大长度)。

  为了使程序间可以相互通信,不论他们是在同一个机器(通过loopback接口)还是不同主机,每一个程序都必须有独立的地址。

  TCP/IP地址由两部分组成——用来辨别机器的IP地址和用来辨别在那台机器上的特定程序的端口地址。

  地址可以是点分(dotted-quad)符号形式的(如,127.0.0.1)或者是主机名形式的(如,www.csdn.net)。系统可以使用/etc/hosts或DNS域名服务(如果可以获得的话)进行主机名到点分符号地址(也就是IP地址)的转换。

  端口从1号开始编号。1和IPP0RT_RESERVED(在/usr/include/netinet/in.h中定义,通常为1024)之间的段口号保留给系统使用(也就是说,你必须以root的身份建立一个网络服务来绑定这部分的端口)。

  最简单的网络程序大都用的客户-服务器模型。一个服务进程等待一个客户进程连接他。当连接建立时,服务器代表客户执行特定的任务,通常这这以后连接就中断了。

 二、使用BSD套接口界面

  最通行的TCP/IP编程方法就是使用BSD套接口界面编程。通过它,网络端点(network endpoints)(IP地址和端口地址)以套接口(sockets)的形式出现。

  这套套接口IPC(interprocess communication,进程间通讯)设施(从4.2BSD开始引入)的设计是为了能让网络程序的设计能够独立于不同的底层通信设施。

  1、建立一个服务器程序

  要使用BSD界面建立一个服务器程序,你必须通过以下步骤:

  (1)通过函数socket()建立一个套接口
  (2)通过函数bind()绑定一个地址(IP地址和端口地址)。这一步确定了服务器的位置,使客户端知道如何访问。
  (3)通过函数listem()监听(listen)端口的新的连接请求。
  (4)通过函数accept()接受新的连接。

  通常,维护代表了客户的请求可能需要花费相当长的一段时间。在处理一个请求时,接收和处理新的请求也应该是高效的。达到这种目的的最通常的做法是让服务器通过fork()函数拷贝一份自己的进程来接受新的连接。

  以下的例子显示了服务器是如何用C实现的:

/*
 * Simple "Hello, World!" server
 * Ivan Griffin 

 */

/* Hellwolf Misty translated */

#include  /* */
#include  /* exit() */
#include  /* memset(), memcpy() */
#include  /* uname() */
#include
#include  /* socket(), bind(),
               listen(), accept() */
#include
#include
#include
#include  /* fork(), write(), close() */

/*
 * constants
 */
const char MESSAGE[] = "Hello, World!\n";
const int BACK_LOG = 5;

/*

*程序需要一个命令行参数:需要绑定的端口号
 */
int main(int argc, char *argv[])
{
  int serverSocket = 0,
    on = 0,
    port = 0,
    status = 0,
    childPid = 0;
  struct hostent *hostPtr = NULL;
  char  hostname[80] = "";
  struct sockaddr_in serverName = { 0 };

  if (2 != argc)
  {
    fprintf(stderr, "Usage: %s \n",
  argv[0]);
    exit(1);
  }
  port = atoi(argv[1]);
/ *
  *socket()系统调用,带有三个参数:
  *    1、参数domain指明通信域,如PF_UNIX(unix域),PF_INET(IPv4),
  *      PF_INET6(IPv6)等
  *    2、type指明通信类型,最常用的如SOCK_STREAM(面向连接可靠方式, 
  *      比如TCP)、SOCK_DGRAM(非面向连接的非可靠方式,比如UDP)等。
  *    3、参数protocol指定需要使用的协议。虽然可以对同一个协议
  *      家族(protocol family)(或者说通信域(domain))指定不同的协议
  *      参数,但是通常只有一个。对于TCP参数可指定为IPPROTO_TCP,对于
  *      UDP可以用IPPROTO_UDP。你不必显式制定这个参数,使用0则根据前
  *      两个参数使用默认的协议。  
  */ 
  serverSocket = socket(PF_INET, SOCK_STREAM,
  IPPROTO_TCP);
  if (-1 == serverSocket)
  {
    perror("socket()");
    exit(1);
  }
 
  /*
   * 一旦套接口被建立,它的运作机制可以通过套接口选项(socket option)进行修改。   
   */

  /*
   * SO_REUSEADDR选项的设置将套接口设置成重新使用旧的地址(IP地址加端口号)而不等待
   * 注意:在Linux系统中,如果一个socket绑定了某个端口,该socket正常关闭或程序退出后,
   * 在一段时间内该端口依然保持被绑定的状态,其他程序(或者重新启动  的原程序)无法绑定该端口。
   *
   * 下面的调用中:SOL_SOCKET代表对SOCKET层进行操作
   */
  on = 1;

  status = setsockopt(serverSocket, SOL_SOCKET,
  SO_REUSEADDR,
    (const char *) &on, sizeof(on));

  if (-1 == status)
  {
    perror("setsockopt(...,SO_REUSEADDR,...)");
  }

  /* 当连接中断时,需要延迟关闭(linger)以保证所有数据都
   * 被传输,所以需要打开SO_LINGER这个选项
   * linger的结构在/usr/include/linux/socket.h中定义:
   *  struct linger
   *  {
   *   int l_onoff;  /* Linger active */
   *   int l_linger;  /* How long to linger */
   *  };

  *  如果l_onoff为0,则延迟关闭特性就被取消。如果非零,则允许套接口延迟关闭。
   *  l_linger字段则指明延迟关闭的时间
   */

  {
    struct linger linger = { 0 };

    linger.l_onoff = 1;
    linger.l_linger = 30;
    status = setsockopt(serverSocket,
  SOL_SOCKET, SO_LINGER,
  (const char *) &linger,
  sizeof(linger));

    if (-1 == status)
    {
      perror("setsockopt(...,SO_LINGER,...)");
    }
  }

  /*
   * find out who I am
   */

  status = gethostname(hostname,
  sizeof(hostname));
  if (-1 == status)
  {
    perror("gethostname()");
    exit(1);
  }

  hostPtr = gethostbyname(hostname);
  if (NULL == hostPtr)
  {
    perror("gethostbyname()");
    exit(1);
  }

  (void) memset(&serverName, 0,
  sizeof(serverName));
  (void) memcpy(&serverName.sin_addr,
  hostPtr->h_addr,
  hostPtr->h_length);
   /*
    *h_addr是h_addr_list[0]的同义词,
    *  h_addr_list是一组地址的数组
    *长度为4(byte)代表一个IP地址的长度
    */
/*
 * 为了使服务器绑定本机所有的IP地址,
 * 上面一行代码需要用下面的代码代替
 * serverName.sin_addr.s_addr=htonl(INADDR_ANY);
 */

  serverName.sin_family = AF_INET;
  /* htons:h(host byteorder,主机字节序)
   *     to n(network byteorder,网络字节序
   *     s(short类型)
   */
  serverName.sin_port = htons(port);
/* 在一个地址(本例中的serverSocket)被建立后
 * 它就应该被绑定到我们获得的套接口。
 */
  status = bind(serverSocket,
 (struct sockaddr *) &serverName,
    sizeof(serverName));
  if (-1 == status)
  {
    perror("bind()");
    exit(1);
  }
/* 现在套接口就可以被用来监听新的连接。
 * BACK_LOG指定了未决连接监听队列(listen queue for pending connections)
 * 的最大长度。当一个新的连接到达,而队列已满的话,客户就会得到连接拒绝错误。

 * (这就是dos拒绝服务攻击的基础)。
 */
  status = listen(serverSocket, BACK_LOG);
  if (-1 == status)
  {
    perror("listen()");
    exit(1);
  }
/* 从这里开始,套接口就开始准备接受请求,并为他们服务。
 * 本例子是用for循环来达到这个目的。一旦连接被接受(accpepted),
 * 服务器可以通过指针获得客户的地址以便进行一些诸如记录客户登陆之类的
 * 任务。
  for (;;)
  {
    struct sockaddr_in clientName = { 0 };
    int slaveSocket, clientLength =
  sizeof(clientName);

    (void) memset(&clientName, 0,
  sizeof(clientName));

    slaveSocket = accept(serverSocket,
  (struct sockaddr *) &clientName,
  &clientLength);
    if (-1 == slaveSocket)
    {
      perror("accept()");
      exit(1);
    }

    childPid = fork();

    switch (childPid)
    {
    case -1: /* ERROR */
      perror("fork()");
      exit(1);

    case 0: /* child process */

      close(serverSocket);

      if (-1 == getpeername(slaveSocket,
  (struct sockaddr *) &clientName,
  &clientLength))
      {
        perror("getpeername()");
      }
      else
      {
      printf("Connection request from %s\n",
          inet_ntoa(clientName.sin_addr));
      }

      /*
       * Server application specific code
       * goes here, e.g. perform some
       * action, respond to client etc.
       */
      write(slaveSocket, MESSAGE,
  strlen(MESSAGE));
        /* 也可以使用带缓存的ANSI函数fprint,
        * 只要你记得必要时用fflush刷新缓存
        */
      close(slaveSocket);
      exit(0);

    default: /* parent process */
      close(slaveSocket);/* 这是一个非常好的习惯
                * 父进程关闭子进程的套接口描述符
                * 正如上面的子进程关闭父进程的套接口描述符。
                */               
    }
  }

  return 0;
}

 

 

时间: 2024-10-27 01:40:26

Linux中BSD套接口开发的基础介绍_unix linux的相关文章

谈谈linux中flock、lockf和fcntl的区别_unix linux

首先flock和fcntl是系统调用,而lockf是库函数.lockf实际上是fcntl的封装,所以lockf和fcntl的底层实现是一样的,对文件加锁的效果也是一样的.后面分析不同点时大多数情况是将fcntl和lockf放在一起的.下面首先看每个函数的使用,从使用的方式和效果来看各个函数的区别.  1. flock 函数原型 int flock(int fd, int operation);  // Apply or remove an advisory lock on the open fi

Linux中文件的五个查找命令总结_unix linux

Linux的查找命令有5个,分别如下:     which:在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果:     type:用于区分某个命令到底是由shell自带的,还是由shell外部的独立二进制文件提供的.如果一个命令是外部命令,那么使用-p参数,会显示该命令的路径,相当于which命令.type命令其实不能算查找命令:     whereis:只能用于程序名的搜索,而且只搜索二进制文件(参数-b).man说明文件(参数-m)和源代码文件(参数-s):    

在Linux中利用yum安装JDK的实现步骤_unix linux

一.卸载centos自带的jdk 1.查看当前的jdk版本,并卸载 root@localhost opt]# rpm -qa|grep java java-1.6.0-openjdk-1.6.0.37-1.13.9.4.el5_11 tzdata-java-2015g-1.el5 //卸载 [root@localhost opt]# rpm -e --allmatches --nodeps java-1.6.0-openjdk-1.6.0.37-1.13.9.4.el5_11 [root@loc

Linux下的Grep命令使用方法详细介绍_unix linux

1. grep简介  grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来.Unix的grep家族包括grep. egrep和fgrep.egrep和fgrep的命令只跟grep有很小不同.egrep是grep的扩展,支持更多的re元字符, fgrep就是 fixed grep或fast grep,它们把所有的

《Linux C编程从入门到精通》一第2章 在Linux中编写C语言代码2.1 Linux中C语言程序开发流程和工具介绍

第2章 在Linux中编写C语言代码 Linux C编程从入门到精通 Linux作为一个操作系统,一项重要的功能就是要支持用户编程.传统的UNIX下的程序开发语言是C语言,C语言是一种平台适应性强.易于移植的语言.Linux是用C语言写成的.反过来,Linux又为C语言提供了很好的支持,C语言编译工具gcc.调试工具gdb属于最早开发出来的一批自由软件.因此Linux与C语言形成了完美的结合,为用户提供了一个强大的编程环境,本章将介绍在Linux中编写C语言程序的流程和具体方法. 2.1 Lin

Kali Linux中的十大WiFi攻击工具介绍

本文讲的是Kali Linux中的十大WiFi攻击工具介绍,在这十大WiFi攻击黑客工具中,我们将讨论一个非常受欢迎的主题:无线网络攻击以及如何防止黑客入侵.无线网络通常是网络的一个弱点,因为WiFi信号可以随处可见,任何人都可以使用.还有很多路由器包含漏洞,可以利用正确的设备和软件(如Kali Linux附带的工具)轻松利用漏洞.许多路由器制造商和ISP仍然默认在其路由器上启用了WPS,这使得无线安全和渗透测试变得更加重要.通过以下十大无线网络黑客工具,你可以测试我们自己的无线网络以获取潜在的

Linux 适合你吗?(一)_unix linux

    第一部分:Linux 之你问我答     我能在我的电脑上安装 Linux 吗?    当然!但是要使它工作正常你需要做一些准备--甚至于替换掉某些硬件.围绕着 Linux 的一个为时已久的问题就是硬件支持问题.你可能无法在每一台电脑上都安装 Linux 并期望它工作正常.例如,你可能会遇到图形显示卡在 Linux 下无法正常工作的问题,因为一些厂商并不提供为 Linux 图形界面(XFree86)设计的驱动程序,而这种情况在目前得到了很大的改观:最新发布的XFree86提供了对 NVi

Linux Socket学习--套接口的类型和协议

  我们首先来说一下PF_INET和AF_INET,虽然标准提倡在指定demain参数的时候,优先使用PF_INET,但是大量已经编写的c代码遵循旧的协议.目前情况是AF_UNIX=PF_UNIX,AF_INET=PF_INET.但是将来是不是这样不好说啊.           不同于socketpari函数的demain参数只能指定为PF_LOCAL,socket函数可以用于生成所支持的任何协议族的套接口,语法如下: #include <sys/types.h> #include <s

Linux学习基础教程_unix linux

 Linux学习基础 1.什么是Linux?  准确的说,是指Linux的kernel(系统的核心程序),其内核版权属于Linus Torvalds,在GPL(GNU General Public License)版权协议下发行, 任何人都可以自由的复制(copy), 修改(change), 套装分发(distribute),销售,但是不可以在分发时加入任何限制, 而且所有原码必须是公开的,所以任何人都可以无偿取得所有执行文件和原代码.  对于Linux用户和系统管理员来说,Linux是指包含L