重要的套接字函数

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情
导读:
   7.5 重要的套接字函数
  FreeBSD提供了与套接字相关的不同函数, “打开”一个套接字我们只 需要四个函数。 有时我们只需要两个。
   7.5.1 客户端-服务器差异
  典型情况中,以套接字为基础的数据通信一端是一个 服务器,另一端是一个 客户端
   7.5.1.1 通用元素
   7.5.1.1.1 socket
  这一个函数在客户端和服务器都要使用:socket(2)。它是这样被声明的:
  int socket(int domain, int type, int protocol);
  
  返回值的类型与open的相同,一个整数。 FreeBSD从和文件句柄相同的池中分配它的值。这就是允许套接字被以对文件相同的方式处理的原因。
  参数domain告诉系统你需要使用什么 协议族。有许多种协议族存在,有些是某些厂商专有的,其它的都非常通用。协议族的声明在 sys/socket.h中
  使用PF_INET是对于 UDP, TCP和其它网间协议(IPv4)的情况。
  对于参数type有五个定义好的值,也在 sys/socket.h中。这些值都以 “SOCK_”开头。 其中最通用的是SOCK_STREAM, 它告诉系统你正需要一个 可靠的流传送服务(和PF_INET一起使用时是指 TCP)。
  如果指定SOCK_DGRAM, 你是在请求 无连接报文传送服务(在我们的情形中是UDP)。
  如何你需要处理基层协议 (例如IP),或者甚至是网络接口 (例如,以太网),你就需要指定 SOCK_RAW。
  最后,参数protocol取决于前两个参数,并非总是有意义。在以上情形中,使用取值0。
   未连接的套接字:对于函数socket我们还没有指定我们要连往什么其它(主机)系统。 我们新建的套接字还是 未连接的
  这是有意的:拿电话类比,我们刚把调制解调器接在电话线上。我们既没有告诉调制解调器发起一个呼叫,也不会应答电话振铃。
   7.5.1.1.2 sockaddr
  各种各样的套接字函数需要指定地址,那是一小块内存空间 (用C语言术语是指向一小块内存空间的指针)。在 sys/socket.h中有各种各样如 struct sockaddr的声明。 这个结构是这样被声明的:
  /*
  * 内核用来存储大多数种类地址的结构
  */
  struct sockaddr {
  unsigned char sa_len; /* 总长度 */
  sa_family_t sa_family; /* 地址族 */
  char sa_data[14]; /* 地址值,实际可能更长 */
  };
  #define SOCK_MAXADDRLEN 255 /* 可能的最长的地址长度 */
  
  注意对于sa_data域的定义有些 不确定性。 那只是被定义为14字节的数组, 注释暗示内容可能超过14字节
  这种不确定性是经过深思熟虑的。套接字是个非常强大的接口。多数人可能认为比Internet接口强不到哪里 ──大多数应用现在很可能都用它 ──套接字可被用于几乎 任何种类的进程间通信, Internet(更精确的说是IP)只是其中的一种。
  sys/socket.h提到的各种类型的协议 将被按照 地址族对待,并把它们就列在 sockaddr定义的前面:
  /*
  * 地址族
  */
  #define AF_UNSPEC 0 /* 未指定 */
  #define AF_LOCAL 1 /* 本机 (管道,portal) */
  #define AF_UNIX AF_LOCAL /* 为了向前兼容 */
  #define AF_INET 2 /* 网间协议: UDP, TCP, 等等 */
  #define AF_IMPLINK 3 /* arpanet imp 地址 */
  #define AF_PUP 4 /* pup 协议: 例如BSP */
  #define AF_CHAOS 5 /* MIT CHAOS 协议 */
  #define AF_NS 6 /* 施乐(XEROX) NS 协议 */
  #define AF_ISO 7 /* ISO 协议 */
  #define AF_OSI AF_ISO
  #define AF_ECMA 8 /* 欧洲计算机制造商协会 */
  #define AF_DATAKIT 9 /* datakit 协议 */
  #define AF_CCITT 10 /* CCITT 协议, X.25 等 */
  #define AF_SNA 11 /* IBM SNA */
  #define AF_DECnet 12 /* DECnet */
  #define AF_DLI 13 /* DEC 直接数据链路接口 */
  #define AF_LAT 14 /* LAT */
  #define AF_HYLINK 15 /* NSC Hyperchannel */
  #define AF_APPLETALK 16 /* Apple Talk */
  #define AF_ROUTE 17 /* 内部路由协议 */
  #define AF_LINK 18 /* 协路层接口 */
  #define pseudo_AF_XTP 19 /* eXpress Transfer Protocol (no AF) */
  #define AF_COIP 20 /* 面向连接的IP, 又名 ST II */
  #define AF_CNT 21 /* Computer Network Technology */
  #define pseudo_AF_RTIP 22 /* 用于识别RTIP包 */
  #define AF_IPX 23 /* Novell 网间协议 */
  #define AF_SIP 24 /* Simple 网间协议 */
  #define pseudo_AF_PIP 25 /* 用于识别PIP包 */
  #define AF_ISDN 26 /* 综合业务数字网(Integrated Services Digital Network) */
  #define AF_E164 AF_ISDN /* CCITT E.164 推荐 */
  #define pseudo_AF_KEY 27 /* 内部密钥管理功能 */
  #define AF_INET6 28 /* IPv6 */
  #define AF_NATM 29 /* 本征ATM访问 */
  #define AF_ATM 30 /* ATM */
  #define pseudo_AF_HDRCMPLT 31 /* 由BPF使用,就不必在接口输出例程
  * 中重写头文件了
  */
  #define AF_NETGRAPH 32 /* Netgraph 套接字 */
  #define AF_SLOW 33 /* 802.3ad 慢速协议 */
  #define AF_SCLUSTER 34 /* Sitara 集群协议 */
  #define AF_ARP 35
  #define AF_BLUETOOTH 36 /* 蓝牙套接字 */
  #define AF_MAX 37
  
  用于指定IP的是 AF_INET。这个符号对应着常量 2。
  在sockaddr中的域 sa_family指定 地址族, 从而决定预先只确定下大致字节数的 sa_data的实际大小。
  特别是当 地址族是AF_INET时,我们可以使用 struct sockaddr_in,这可在 netinet/in.h中找到,任何需要 sockaddr的地方都以此作为实际替代。
  /*
  * 套接字地址,Internet风格
  */
  struct sockaddr_in {
  uint8_t sin_len;
  sa_family_t sin_family;
  in_port_t sin_port;
  struct in_addr sin_addr;
  char sin_zero[8];
  };
  
  我们可这样描绘它的结构:
  
  
  三个重要的域是: sin_family,结构体的字节1; sin_port,16位值,在字节2和3; sin_addr,一个32位整数,表示 IP地址,存储在字节4-7。
  现在,让我们尝试填满它。让我们假设我们正在写一个 daytime协议的客户端,这个协议只是简单的规定服务器写出一个代表当前日期和时间文本字符串到端口13。 我们需要使用 TCP/IP,所以我们需要指定在地址族域指定 AF_INET。 AF_INET被定义为 2。让我们使用 IP地址192.43.244.18,这指向 美国联邦政府(time.nist.gov)的服务器。
  
  
  顺便说一下,域sin_addr被声明为类型 struct in_addr,这个类型定义在 netinet/in.h之中:
  /*
  * Internet 地址 (由历史原因而形成的结构)
  */
  struct in_addr {
  in_addr_t s_addr;
  };
  
  而in_addr_t是一个32位整数。
  192.43.244.18只是为了表示32位整数的方便写法,按每个八位字节列出, 以 最高位的字节开始。
  到目前为止,我已经看见了sockaddr。我们的计算机并不将短整数存储为一个16位实体,而是一个2字节序列。同样的,计算机将32位整数存储为4字节序列。
  想象我们这样写程序:
  sa.sin_family = AF_INET;
  sa.sin_port = 13;
  sa.sin_addr.s_addr = (((((192 <<8) | 43) <<8) | 244) <<8) | 18;
  
  结果会是什么样的呢?
  好,那当然是要依赖于其它因素的。在Pentium或其它x86 为基础的计算机上,它会像这样:
  
  
  在另一个不同的系统上,它可能会是:
  
  
  在一台PDP计算机上,它可能又是另一个样子。 不过上面两种情况是今天最常用的了。
   译者注:PDP的字节顺序在英语中称为middle-endian或mixed-endian。例如,原数0x44332211会被PDP存储为0x33441122。 VAX也采用这种字节顺序。
  通常,要书写可移植的代码,程序员假设不存在那些差异。他们回避这种差异(除了他们使用汇编语言写代码的时候)。唉,可你不能在为套接字写代码时那样轻易的回避这种差异。
  为什么?
  因为当与另一台计算机通信时, 你通常不知道对方存储数据时是先存放 最高位字节(MSB)还是 最低位字节(LSB)。
  你可能会有问题, “那么,套接字可以为我把握这种差异吗?”
  它不能。
  这个回答可能先是让你感到惊讶, 请记住通用的套接字接口只明白结构体sockaddr中的域sa_len和sa_family。 你不必担心那里的字节顺序(当然, 在FreeBSD上sa_family只有一个字节, 但是许多其它的 UNIX系统没有 sa_len并使用2字节给 sa_family,而且数据使用何种顺序都取决于计算机(译者注:此处英文原文的用词为“is native to”))。
  其余的数据,也就只剩下sa_data[14]。 依照 地址族,套接字只是将那些数据转发到目的地。
  事实上,我们输入一个端口号, 是为了让其它计算机知道我们需要什么服务。并且,当我们提供服务时, 只有读取了端口号我们才知道其它计算机期望从我们这里获得什么服务。另一方面,套接字只将端口号作为数据转发,完全不去理会(译者注:此处英文原文用词为“interpret”)其中的内容。
  同样的,我们输入IP地址,告诉途经的每台计算机要将我们的数据发送到哪里。 套接字依然只将其按数据转发。
  那就是为什么我们(指 程序员,而不是 套接字)不得不把使用在我们的计算机上的字节顺序和发送给其它计算机时使用的传统字节顺序区分开来。
  我们将把我们的计算机上使用的字节顺序称为 主机字节顺序, 或者就是 主机顺序.
  有一个在IP发送多字节数据的传统: 最高位字节(MSB) 优先。 这,我们将用 网络字节顺序提及, 或者简单的称为 网络顺序
  现在,如果我们在Intel计算机上编译上面的代码, 我们的 主机字节顺序将产生:
  
  
  但是 网络字节顺序要求我们先存储数据的最高位字节(MSB):
  
  
  不幸的是,我们的 主机顺序恰恰与 网络顺序相反。
  我们有几种方法解决这个问题。一种是在我们的代码中 倒置数值:
  sa.sin_family = AF_INET;
  sa.sin_port = 13 <<8;
  sa.sin_addr.s_addr = (((((18 <<8) | 244) <<8) | 43) <<8) | 192;
  
  这将 欺骗我们的编译器把数据按 网络字节顺序存储。在一些情形中,这的确是个有效的办法 (例如,用汇编语言编程)。然而,在多数情形中,这会导致一个问题。
  想象一下,你用C语言写了一个套接字程序。 你知道它将运行在一台Pentium计算机上, 于是你倒着输入你的所有常量,并且把它们强置为 网络字节顺序。 它工作正常。
  然而,有一台,你所信任的旧 Pentium变成一台生了锈的旧 Pentium。你把它更换为一个 主机顺序网络顺序相同的系统。 你需要重新编译你的所有软件。你的所有软件中除了你写的那个程序,都继续工作正常。
  你早已经忘记你将全部常量强置为与 主机顺序相反。你花费宝贵时间拽头发,呼唤你曾经听到过的(有些是你编造的)所有上帝的名字, 用击球棍敲打你的显示器,还上演所有其它的传统仪式 试图找到一个原本好端端的程序突然完成不能工作的原因。
  最终,你找到了原因,发了一通誓言, 开始重写你的代码。
  幸运的是,你不是第一个面对这个问题的人。 其它人已经创建 htons(3)和 htonl(3)C 语言函数分别将 shortand long从 主机字节顺序转换为 网络字节顺序, 并且还有 ntohs(3)和 ntohl(3)C 语言函数进行着另外的转换。
  在 最高位字节(MSB) -最前的系统上,这些函数什么都不做。在 最低位字节(LSB) -最前的系统上它们将值转换为正确的顺序。
  这样一来,无论你的软件在什么系统上编译, 如果你使用这些函数,你的数据最终都将是正确的顺序。
   7.5.1.2 客户端函数
  典型情况中,客户端初始化到服务器的连接。 客户端知道要呼叫哪台服务器:它知道服务器的IP地址,并且知道服务器驻守的 端口。这就好比你拿起电话拨号码 ( 地址),然后,有人应答, 呼叫负责狂欢的人 ( 端口)。
   7.5.1.2.1 connect
  一旦一个客户端已经建立了一个套接字,就需要把它连接到一个远方系统的一个端口上。这使用 connect(2):
  int connect(int s, const struct sockaddr *name, socklen_t namelen);
  
  参数 s是套接字, 那是由函数socket返回的值。 name是一个指向 sockaddr的指针,这个结构体我们已经展开讨论过了。 最后,namelen通知系统 在我们的sockaddr结构体中有多少字节。
  如果 connect成功, 返回 0。否则返回 -1并将错误码存放于 errno之中。
  有许多种connect可能失败的原因。例如,试图发起一个Internet连接时, IP地址可能不存在,或可能停机, 或者就是太忙,或者可能没有在指定端口上有服务器监听。或者直接 拒绝任何特定代码的请求。
   7.5.1.2.2 我们的第一个客户端
  现在我们知道足够多去写一个非常简单的客户端, 一个从192.43.244.18获取当前时间并打印到 stdout的程序。
  /*
  * daytime.c
  *
  * G. Adam Stanislav 编程
  */
  #include
  #include
  #include
  #include
  int main() {
  register int s;
  register int bytes;
  struct sockaddr_in sa;
  char buffer[BUFSIZ+1];
  if ((s = socket(PF_INET, SOCK_STREAM, 0)) <0) {
  perror("socket");
  return 1;
  }
  bzero(&sa, sizeof sa);
  sa.sin_family = AF_INET;
  sa.sin_port = htons(13);
  sa.sin_addr.s_addr = htonl((((((192 <<8) | 43) <<8) | 244) <<8) | 18);
  if (connect(s, (struct sockaddr *)&sa, sizeof sa) <0) {
  perror("connect");
  close(s);
  return 2;
  }
  while ((bytes = read(s, buffer, BUFSIZ)) >0)
  write(1, buffer, bytes);
  close(s);
  return 0;
  }
  
  继续,把它输入到你的编辑器中,保存为 daytime.c,然后编译并运行:
  %cc -O3 -o daytime daytime.c
  %./daytime
  52079 01-06-19 02:29:25 50 0 1 543.9 UTC(NIST) *
  %
  
  在这一情形中,日期是2001年6月19日,时间是 02:29:25 UTC。你的结果会很自然的变化。
   7.5.1.3 服务器函数
  典型的服务器不初始化连接。 相反,服务器等待客户端呼叫并请求服务。服务器不知道客户端什么时候会呼叫, 也不知道有多少客户端会呼叫。服务器就是这样静坐在那儿,耐心等待,一会儿,又一会儿, 它突然发觉自身被从许多客户端来的请求围困,所有的呼叫都同时来到。
  套接字接口提供三个基本的函数处理这种情况。
   7.5.1.3.1 bind
  端口像是电话线分机:在你拨一个号码后, 你拨分机到一个特定的人或部门。
  有65535个 IP端口,但是一台服务器通常只处理从其中一个端口进入的请求。这就像告诉电话室操作员我们处于工作状态并在一个特定分机应答电话。 我们使用 bind(2)告诉套接字我们要服务的端口。
  int bind(int s, const struct sockaddr *addr, socklen_t addrlen);
  
  除了在 addr中指定端口, 服务器还可以包含其自身的 IP地址。不过,也可以就使用符号常量 INADDR_ANY,指示服务于无论哪个 IP上的指定端口上的请求。 这个符号和几个相同的常量,声明在 netinet/in.h之中。
  #define INADDR_ANY (u_int32_t)0x00000000
  
  想象我们正在为 daytime协议在 TCP/IP的基础上写一个服务器。 回想起使用端口13。我们的sockaddr_in结构应当像这样:
  
  
   7.5.1.3.2 listen
  继续我们的办公室电话类比, 在你告诉电话中心操作员你会在哪个分机后,现在你走进你的办公室,确认你自己的电话已插上并且振铃已被打开。还有,你确认呼叫等待功能开启,这样即使你正在与其它人通话, 也可听见电话振铃。
  服务器执守所有经过函数 listen(2)操作的套接字。
  int listen(int s, int backlog);
  
  在这里,变量backlog告诉套接字在忙于处理上一个请求时还可以接受多少个进入的请求。换句话说,这决定了挂起连接的队列的最大大小。
   7.5.1.3.3 accept
  在你听见电话铃响后,你应答呼叫接起电话。 现在你已经建立起一个与你的客户的连接。这个连接保持到你或你的客户挂线。
  服务器通过使用函数 accept(2)接受连接。
  int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
  
  注意,这次 addrlen是一个指针。这是必要的,因为在此情形中套接字要 填上 addr,这是一个 sockaddr_in结构体。
  返回值是一个整数。其实, accept返回一个 新套接字。你将使用这个新套接字与客户通信。
  老套接字会发生什么呢?它继续监听更多的请求 (想起我们传给listen的变量 backlog了吗?),直到我们 close(关闭) 它。
  现在,新套接字仅对通信有意义,是完全接通的。 我们不能再把它传给 listen接受更多的连接。
   7.5.1.3.4 我们的第一个服务器
  我们的第一个服务器会比我们的第一个客户端复杂一些:我们不仅用到了更多的套接字函数, 还需要把程序写成一个守护程序。
  这最好写成:在绑定端口后建立一个 子进程。 主进程随后退出,将控制权交回给 shell(或者任何调用主进程的程序)。
  子进程调用 listen,然后启动一个无休止循环。这个循环接受连接,提供服务, 最后关闭连接的套接字。
  /*
  * daytimed - 端口 13 的服务器
  *
  * G. Adam Stanislav 编程
  * 2001年6月19日
  */
  #include
  #include
  #include
  #include
  #include
  #include
  #define BACKLOG 4
  int main() {
  register int s, c;
  int b;
  struct sockaddr_in sa;
  time_t t;
  struct tm *tm;
  FILE *client;
  if ((s = socket(PF_INET, SOCK_STREAM, 0)) <0) {
  perror("socket");
  return 1;
  }
  bzero(&sa, sizeof sa);
  sa.sin_family = AF_INET;
  sa.sin_port = htons(13);
  if (INADDR_ANY)
  sa.sin_addr.s_addr = htonl(INADDR_ANY);
  if (bind(s, (struct sockaddr *)&sa, sizeof sa) <0) {
  perror("bind");
  return 2;
  }
  switch (fork()) {
  case -1:
  perror("fork");
  return 3;
  break;
  default:
  close(s);
  return 0;
  break;
  case 0:
  break;
  }
  listen(s, BACKLOG);
  for (;;) {
  b = sizeof sa;
  if ((c = accept(s, (struct sockaddr *)&sa, &b)) <0) {
  perror("daytimed accept");
  return 4;
  }
  if ((client = fdopen(c, "w")) == NULL) {
  perror("daytimed fdopen");
  return 5;
  }
  if ((t = time(NULL)) <0) {
  perror("daytimed time");
  return 6;
  }
  tm = gmtime(&t);
  fprintf(client, "%.4i-%.2i-%.2iT%.2i:%.2i:%.2iZ/n",
  tm->tm_year + 1900,
  tm->tm_mon + 1,
  tm->tm_mday,
  tm->tm_hour,
  tm->tm_min,
  tm->tm_sec);
  fclose(client);
  }
  }
  
  我们开始于建立一个套接字。然后我们填好 sockaddr_in类型的结构体 sa。注意, INADDR_ANY的特定使用方法:
  if (INADDR_ANY)
  sa.sin_addr.s_addr = htonl(INADDR_ANY);
  
  这个常量的值是0。由于我们已经使用 bzero于整个结构体, 再把成员设为0将是冗余。 但是如果我们把代码移植到其它一些 INADDR_ANY可能不是0的系统上, 我们就需要把实际值指定给 sa.sin_addr.s_addr。多数现在C语言 编译器已足够智能,会注意到 INADDR_ANY是一个常量。由于它是0,他们将会优化那段代码外的整个条件语句。
  在我们成功调用bind后, 我们已经准备好成为一个 守护进程:我们使用 fork建立一个子进程。 同在父进程和子进程里,变量s都是套接字。 父进程不再需要它,于是调用了close, 然后返回0通知父进程的父进程成功终止。
  此时,子进程继续在后台工作。 它调用listen并设置 backlog 为 4。这里并不需要设置一个很大的值, 因为 daytime不是个总有许多客户请求的协议,并且总可以立即处理每个请求。
  最后,守护进程开始无休止循环,按照如下步骤:
  调用accept。 在这里等待直到一个客户端与之联系。在这里,接收一个新套接字,c, 用来与其特定的客户通信。
  使用 C 语言函数 fdopen把套接字从一个 低级 文件描述符转变成一个 C语言风格的 FILE指针。 这使得后面可以使用 fprintf。
  检查时间,按 ISO 8601格式打印到 “文件” client。 然后使用 fclose关闭文件。这会把套接字一同自动关闭。
  我们可把这些步骤 概括起来,作为模型用于许多其它服务器:
  
  
  这个流程图很好的描述了 顺序服务器, 那是在某一时刻只能服务一个客户的服务器,就像我们的 daytime服务器能做的那样。这只能存在于客户端与服务器没有真正的“对话”的时候:服务器一检测到一个与客户的连接,就送出一些数据并关闭连接。整个操作只花费若干纳秒就完成了。
  这张流程图的好处是,除了在父进程 fork之后和父进程退出前的短暂时间内, 一直只有一个 进程活跃:我们的服务器不占用许多内存和其它系统资源。
  注意我们已经将 初始化守护进程加入到我们的流程图中。我们不需要初始化我们自己的守护进程 (译者注:这里仅指上面的示例程序。一般写程序时都是需要的。), 但这是在程序流程中设置signal处理程序、 打开我们可能需要的文件等操作的好地方。
  几乎流程图中的所有部分都可以用于描述许多不同的服务器。 条目 serve是个例外,我们考虑为一个 “黑盒子”,那是你要为你自己的服务器专门设计的东西, 并且 “接到其余部分上”。
  并非所有协议都那么简单。许多协议收到一个来自客户的请求,回复请求,然后接收下一个来自同一客户的请求。 因此,那些协议不知道将要服务客户多长时间。这些服务器通常为每个客户启动一个新进程 当新进程服务它的客户时,守护进程可以继续监听更多的连接。
  现在,继续,保存上面的源代码为 daytimed.c(用字母d结束守护程序名是个风俗)。在你编译好后,尝试运行:
  %./daytimed
  bind: Permission denied
  %
  
  这里发生了什么?正如你将回想起的, daytime协议使用端口13。 但是所有1024以下的端口保留给超级用户 (否则,任何人都可以启动一个守护进程伪装一个常用端口的服务, 这就导致了一个安全漏洞)。
  再试一次,这次以超级用户的身份:
  #./daytimed
  #
  
  怎么……什么都没有?让我们再试一次:
  #./daytimed
  bind: Address already in use
  #
  
  在一个时刻,每个端口只能被一个程序绑定。我们的第一个尝试真的成功了:启动了守护子进程并安静的返回。守护子进程仍然在运行,并且继续运行到你关闭它,或是它使用的系统调用失败,或是你重启计算机时。
  好,我们知道它正在后台运行着。 但是它正在正常工作吗?我们如何知道它是个正常的 daytime服务器?只需简单的:
  %telnet localhost 13
  Trying ::1...
  telnet: connect to address ::1: Connection refused
  Trying 127.0.0.1...
  Connected to localhost.
  Escape character is '^]'.
  2001-06-19T21:04:42Z
  Connection closed by foreign host.
  %
  
   telnet尝试新协议 IPv6,失败了。又重新尝试 IPv4,而后成功了。守护进程工作正常。
  如果你可以通过 telnet访问另一个 UNIX系统,你可以用测试远程访问服务器。 我们计算机没有静态 IP地址, 所以我这样做:
  %who
  whizkid ttyp0 Jun 19 16:59 (216.127.220.143)
  xxx ttyp1 Jun 19 16:06 (xx.xx.xx.xx)
  %telnet 216.127.220.143 13
  Trying 216.127.220.143...
  Connected to r47.bfm.org.
  Escape character is '^]'.
  2001-06-19T21:31:11Z
  Connection closed by foreign host.
  %
  
  又工作正常了。使用域名还会工作正常吗?
  %telnet r47.bfm.org 13
  Trying 216.127.220.143...
  Connected to r47.bfm.org.
  Escape character is '^]'.
  2001-06-19T21:31:40Z
  Connection closed by foreign host.
  %
  
  顺序说一句, telnet在我们的守护进程关闭套接字之后打印消息 Connection closed by foreign host(连接被外部主机关闭)。这告诉我们,实际上,在我们的代码中使用 fclose(client);的工作情况就像前面说的一样。
  本

本文转自
http://www.cnblogs.com/likaincu/archive/2006/11/14/560123.html
unix网络编程源码编译出错分析 lin@lin-X555LI:~/Downloads/unix-network-programing/unpv13e/lib$ cd ../libfree lin@lin-X555LI:~/Downloads/unix-network-programing/unpv13e/libfree$ make gcc -I../lib -g -O2 -D_REENTRANT -Wall -c -o in_ 阅读详情

相关推荐

socket套接字及相关函数介绍

套接字 套接字是通信端点的一个抽象,套接字描述符类似文件描述符,在i

Waorange的博客 1804

解决“结构 “<unnamed>“ 没有字段 “ac_vol“ C/C++(136)”的错误提示

今天来分享一个自己遇到的错误提示,其实这个错误如果是粗心,可能经常会遇到,哈哈哈如果你的编译器有代码自动补全可能不太会遇到~。好了,我们看一下这个错误,这个错误提示我们:“结构 unnamed 没有字段 “ac_vol" C/C++(136)”,,这些很容易出现在一些没有代码补全的编译器中。因为一不小心打错了就会提示你错误。好了,今天分享就到这里为止~

OMGMac的博客 2718

套接字&套接字地址结构和bind()函数

我们都知道在网络编程中,可以调用socket()函数来获取一个套接字描述符。但是,socket()函数的作用只是返回一个很小的非负整数值吗?并非如此,socket()函数的作用是创建套接字的。 创建套接字的流程如下 socket()函数会触发内核调用sys_socket()函数,然后sys_socket()函数会调用sock_create()函数。此函数 会根据我们在

芳草源的博客 4825

keil5报“没有字段”错误 的解决方式

keil5报错:error: #136: struct "" has no field "PBout"

Miao8miao的博客 5151

tcpserver.c

/* tcpserver.c */#include #include #include #include #include #include #include #include #include int main(){        int sock, connected, bytes_recieved , true = 1;          char send_data [1024]

RichardYSteven的专栏 4262

【Linux基础编程】网络编程——套接字

01、文章目录 文章目录01、文章目录02、套接字介绍03、套接字属性04、socket函数介绍05、套接字地址结构06、总结 02、套接字介绍 Socket套接字由远景研究规划局(Advanced Research Projects Agency, ARPA)资助加里福尼亚大学伯克利分校的一个研究组研发。其目的是将TCP/IP协议相关软件移植到UNIX类系统中。设计者开发了一个接口,以便应用程序能简单地调用该接口通信。这个接口不断完善,最终形成了Socket套接字。Linux系统采用了Socket套接字

Cain Xcy的博客 319

常用套接字函数整理

对基本和高级套接字函数的整理,方便查找。

T2hunz1 1515

函数简介篇——常用套接字函数

常用套接字函数整理 基础套接字函数入门1 套接字函数及其理解 socket()函数用法详解:创建套接字 socket()函数   函数功能:在某协议簇的某协议中创建一个套接字socket)【创建socket数据结构分配存储空间】。 项目 说明 函数原型 extern int socket (int __domain, int __type, int __protocol) __THROW; 头文件 sys/types.h、sys/socket.h 参数说明 1.__domain:协议簇,如

weixin_42645653的博客 4654

socket套接字的读写函数

1

ArtAndLife的博客 373

07 TCP套接字之accept函数

前面介绍listen函数介绍过未完成连接队列和已完成连接队列, 已完成连接队列是跟对端的connect函数保持了连接, 但是这样的连接只是单方向的. 服务端并没有确切想要跟客户端通信, 想要两端能够持久保持连接还能通信, 就需要让服务端调用accept函数 函数原型 #include &amp;lt;sys/socket.h&amp;gt; int accept(int sockfd, struct sockad...

啦啦啦 2420

06 TCP套接字之listen函数

前面介绍了客服端通过调用connect函数就能向服务端请求连接, 但是服务端如果不开启监听服务的话就根本不知道对端想要与之建立连接. 要想知道客户端想要连接就得调用listen函数 函数原型 #include &amp;lt;sys/socket.h&amp;gt; int listen(int sockfd, int backlog); 成功 : 返回0; 失败 : 返回-1. 函数语义 假设由socket函...

啦啦啦 1334

04 TCP套接字之bind的函数

前面介绍了socket函数, 本节介绍bind函数. 该函数是网络通信时服务端必不可少的函数. 函数原型 bind函数把一个本地协议地址赋予一个套接字. #include &amp;lt;sys/socket.h&amp;gt; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 成功 : 返回0. 失败 : 返回-1. ...

啦啦啦 2208

套接字编程常用函数

不同协议的套接字编程(TCP套接字、UDP套接字、原始套接字等)的模型有所差异,但一般会使用到一下常用函数: #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int socket(int domain, int type, int protocol);

Windeal 2977

套接字编程相关函数(2:TCP套接字编程相关函数

  本文摘录自《UNIX网络编程 卷1》。 基本套接字函数      socket函数     为了执行网络I/O,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型。其定义如下: #include <sys/socket.h> int socket(int family, int type, int protocol); // 返回:...

237

套接字socket函数

套接字socket函数 文章目录套接字socket函数学习目标:套接字socket):网络字节序:ip地址转换函数:sockaddr地址结构socket函数:bind函数:accept函数:connect函数:补充: 学习目标: 掌握IP地址转换函数 了解sockaddr地址结构 掌握socket、bind、listen、accept、connet函数套接字socket): 套接字socket): 就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象,一个套接字就是网络上进程通信

KCenti的博客 495

网络编程:套接字socket函数与绑定信息bind函数

套接字socket函数与绑定信息bind函数套接字绑定信息(绑定IP和端口)socket函数bind函数struct sockaddr结构体的组成:struct sockaddr_in结构体的组成:总结:编写socket函数与bind函数 套接字 套接字是网络编程中的一种通信机制,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。 进程间通信的方式: 1、管道(包括无名管道和命名管道); 2、

cckluv的博客 3053

套接字重用setsockopt函数

套接字描述符 (socket descriptor)。这是一个整数,代表先前通过。指向的选项值的大小,以字节为单位。这是非常重要的,必须正确指定。要设置的选项的名称。例如,对于布尔类型的选项 (如。可能会导致错误的结果或程序崩溃。函数的操作直接针对这个套接字。指向要设置的选项值的指针。系统调用创建的套接字

Kongxl64的博客 448

14. TCP套接字之shutdown函数

前面我们虽然解决了困惑我们很久客服端的问题, 但是虽然解决了但是该客服端还有更加大的隐患存在, 接下来我们就来看一下问题所在和问题解决办法. 问题浮现 使用前面IO复用之select 的客服端和服务端都改写成的完整代码做实验. 完整代码 : select_server_client.c 服务端运行: ./a.out 1 8080 127.0.0.1 客服端运行: ./a.out 2 808...

啦啦啦 1199

套接字函数详解

套接字函数详解 创建套接字 文章目录套接字函数详解创建套接字bind: 设定电话号码IPv4 TCP 套接字的例子listen:接上电话线,一切准备就绪accept: 电话铃响起了……connect: 拨打电话著名的 TCP 三次握手: 这一次不用背记为什么tcp建立连接需要三次握手 int socket(int domain, int type, int protocol) domain 就是指 PF_INET、PF_INET6 以及 PF_LOCAL 等,表示什么样的套接字。type 可用的值是:

qq_53111905的博客 734
上一篇: socket编程1
下一篇: socket编程3
yingfox
博客等级 码龄24年 46粉丝 398原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值