??xml version="1.0" encoding="utf-8" standalone="yes"?>一本一本久久A久久综合精品 ,久久这里都是精品,久久国产免费直播http://www.shnenglu.com/lmlf001/category/1601.html三?zhn)明镜垂`韵,?ji)撩清泉z尘? zh-cnTue, 20 May 2008 01:07:16 GMTTue, 20 May 2008 01:07:16 GMT60epoll实现的net_echoE序http://www.shnenglu.com/lmlf001/archive/2007/09/08/31854.html芥之?/dc:creator>芥之?/author>Sat, 08 Sep 2007 12:49:00 GMThttp://www.shnenglu.com/lmlf001/archive/2007/09/08/31854.htmlhttp://www.shnenglu.com/lmlf001/comments/31854.htmlhttp://www.shnenglu.com/lmlf001/archive/2007/09/08/31854.html#Feedback0http://www.shnenglu.com/lmlf001/comments/commentRss/31854.htmlhttp://www.shnenglu.com/lmlf001/services/trackbacks/31854.html阅读全文

]]>
Linux|络~程--8. 套接字选项QZZQ?/title><link>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6372.html</link><dc:creator>芥之?/dc:creator><author>芥之?/author><pubDate>Thu, 27 Apr 2006 05:12:00 GMT</pubDate><guid>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6372.html</guid><wfw:comment>http://www.shnenglu.com/lmlf001/comments/6372.html</wfw:comment><comments>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6372.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/lmlf001/comments/commentRss/6372.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/lmlf001/services/trackbacks/6372.html</trackback:ping><description><![CDATA[ <p align="center"> <font size="4">Linux|络~程--8. 套接字选项</font> <br /> <br />http://linuxc.51.net 作?hoyt<br /></p> <br />有时候我们要控制套接字的行ؓ(f)(如修改缓冲区的大?,q个时候我们就要控制套接字的选项? <br /><br /><br />8.1 getsockopt和setsockopt <br /><br />int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen) <br />int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t *optlen) <br /><br />level指定控制套接字的层次.可以取三U? 1)SOL_SOCKET:通用套接字选项. 2)IPPROTO_IP:IP选项. 3)IPPROTO_TCP:TCP选项. <br />optname指定控制的方?选项的名U?,我们下面详细解释 <br /><br />optval获得或者是讄套接字选项.Ҏ(gu)选项名称的数据类型进行{? <br /><br /><br />选项名称 说明 数据cd <br />======================================================================== <br /> SOL_SOCKET <br />------------------------------------------------------------------------ <br />SO_BROADCAST 允许发送广播数? int <br />SO_DEBUG 允许调试 int <br />SO_DONTROUTE 不查找\? int <br />SO_ERROR 获得套接字错? int <br />SO_KEEPALIVE 保持q接 int <br />SO_LINGER 延迟关闭q接 struct linger <br />SO_OOBINLINE 带外数据攑օ正常数据? int <br />SO_RCVBUF 接收~冲区大? int <br />SO_SNDBUF 发送缓冲区大小 int <br />SO_RCVLOWAT 接收~冲Z? int <br />SO_SNDLOWAT 发送缓冲区下限 int <br />SO_RCVTIMEO 接收时 struct timeval <br />SO_SNDTIMEO 发送超? struct timeval <br />SO_REUSERADDR 允许重用本地地址和端? int <br />SO_TYPE 获得套接字类? int <br />SO_BSDCOMPAT 与BSDpȝ兼容 int <br />========================================================================== <br /> IPPROTO_IP <br />-------------------------------------------------------------------------- <br />IP_HDRINCL 在数据包中包含I(xin)P首部 int <br />IP_OPTINOS IP首部选项 int <br />IP_TOS 服务cd <br />IP_TTL 生存旉 int <br />========================================================================== <br /> IPPRO_TCP <br />-------------------------------------------------------------------------- <br />TCP_MAXSEG TCP最大数据段的大? int <br />TCP_NODELAY 不用Nagle法 int <br />========================================================================= <br /><br />关于q些选项的详l情况请查看 Linux Programmer's Manual <br /><br />8.2 ioctl <br />ioctl可以控制所有的文g描述W的情况,q里介绍一下控制套接字的选项. <br /><br /> int ioctl(int fd,int req,...) <br /><br />========================================================================== <br /> ioctl的控刉项 <br />-------------------------------------------------------------------------- <br />SIOCATMARK 是否到达带外标记 int <br />FIOASYNC 异步输入/输出标志 int <br />FIONREAD ~冲区可ȝ字节? int <br />========================================================================== <br /><br />详细的选项L(fng) man ioctl_list 查看. <img src ="http://www.shnenglu.com/lmlf001/aggbug/6372.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/lmlf001/" target="_blank">芥之?/a> 2006-04-27 13:12 <a href="http://www.shnenglu.com/lmlf001/archive/2006/04/27/6372.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SOCKET常用函数介(ZZQ?/title><link>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6370.html</link><dc:creator>芥之?/dc:creator><author>芥之?/author><pubDate>Thu, 27 Apr 2006 04:11:00 GMT</pubDate><guid>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6370.html</guid><wfw:comment>http://www.shnenglu.com/lmlf001/comments/6370.html</wfw:comment><comments>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6370.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/lmlf001/comments/commentRss/6370.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/lmlf001/services/trackbacks/6370.html</trackback:ping><description><![CDATA[什么是 socketQ?<br />你始l听Ch们谈论着 "socket"Q而你不知道他的确切含义。那么,现在我告诉你Q?他是使用 Unix 文g描述W?(fiel descriptor) 和其他程序通讯的方式?<br />什么? <br />Ok --你也许听C?Unix 高手 (hacker) q样_(d)(x)“呀QUnix 中所有的东西是文gQ”那个家伙也许正在说C个事实:(x)Unix E序在执行Q何Ş式的 I/O 的时候,E序是在L者写一个文件描q符。一个文件描q符只是一个和打开的文件相兌的整数。但?注意后面的话)Q这个文件可能是一个网l连接, FIFOQ管道,l端Q磁盘上的文?或者什么其他的东西。Unix 中所有的东西是文Ӟ因此Q你惛_ Internet 上别 的程序通讯的时候,你将要通过文g描述W。最好相信刚才的话?<br />现在你脑中或许冒出q样的念_(d)(x)“那么我从哪里得到网l通讯的文件描q符呢, 聪明 人?”无论如何,我要回答q个问题Q你利用pȝ调用 socket()。他q回套接口描 q符 (socket descriptor)Q然后你再通过他来调用 send() ?recv()?<br />“但?..”,你可能现在叫hQ“如果他是个文g描述 W,那么Z么不用一般的调用 read() ?write() 来通过套接口通讯Q”简单的{案是:(x)“你可以使用 一般的函数Q”。详l的{案是:(x)“你可以Q但是?send() ?recv() 让你更好的控制数据传输。?<br />有这样一个事实:(x)在我们的 世界上,有很多种套接口。有 DARPA Internet 地址 (Internet 套接?Q本地节点的路径?(Unix 套接?QCCITT X.25 地址 (你可以完全忽?X.25 套接??也许在你?Unix 机器上还有其他的。我们在q里只讲W一U:(x)Internet 套接口?<br />----------------------------------------------------------------------------<br />Internet 套接口的两种cd <br />什 么意思?有两U?Internet 套接口?是的。不Q我在撒谎。其实还有很多,但是我可不想 吓着你。我们这里只讲两U?Except for this sentence, where I'm going to tell you that "Raw Sockets" are also very powerful and you should look them up. <br />好了Q好了。那两种cd是什么呢Q一U是 "Stream Sockets"Q另外一U是 "Datagram Sockets"。我们以后谈C们的时候也?x)用?"SOCK_STREAM" ?"SOCK_DGRAM"。数据报套接口有时也叫“无q接套接口?如果你确实要q接的时候用 connect()? <br />式套接口是可靠的双向通讯的数据流。如果你向套接口安顺序输出?Q?”,那么他们 安序?Q?”到辑֏一辏V他们也是无错误的传递的Q有自己的错误控制?<br />? 谁在使用式套接口?你可能听说过 telnetQ不是吗Q他׃用流式套接口。你需要你所输入的字W按序到达Q不?吗?同样QW(xu)WW 览器用的 HTTP 协议也用他们。实际上Q当你通过端口80 telnet C?WWW 站点Q然后输?“GET pagename? 的时候,你也可以得到 HTML 的内宏V?<br />Z么流式套接口可以辑ֈ高质量的数据传输Q他使用了“传输控制协?(The Transmission Control Protocol)”,也叫 “TCP?(请参?RFC-793 获得详细资料?TCP 控制你的数据 按顺序到辑ƈ且没有错误。你也许听到 “TCP?是因为听到过 “TCP/IP”。这里的 IP 是指 “Internet 协议?请参?RFC-791.) IP 只是处理 Internet 路由而已?<br />那么数据报套接口呢?Z么他叫无q接呢?Z么他是不可靠的呢Q恩Q有q样的事实:(x)如果你发送一个数据报Q他可能到达Q他可能ơ序颠倒了。如果他到达Q那么在q个包的内部是无错误的?<br />数据报也使用 IP 作\由,但是他不选择 TCP。他使用“用h据报协议 (User Datagram Protocol)”,也叫 “UDP?(请参?RFC-768.) <br />Z么他们是无连接的呢?主要原因是因Zq不象流式套接口那样l持一个连接?你只要徏立一个包Q在目标信息中构造一?IP _(d)然后发出厅R不需要连接。应用程序有Q?tftp, bootp {等?<br />“够 了!”你也许?x)想Q“如果数据丢׃q些E序如何正常工作Q”我的朋友,每个E序?UDP 上有自己的协议。例如,tftp 协议每发Z个包Q收到者发回一个包来说“我收到了!?(一个“命令正应{”也叫“ACK? ?。如果在一定时间内(例如5U?Q发送方没有收到应答Q?他将重新发送,直到得到 ACK。这一点在实现 SOCK_DGRAM 应用E序的时候非帔R要?<br />----------------------------------------------------------------------------<br />|络理论 <br />既然我刚才提C协议层,那么现在是讨论网l究竟如何工作和演示 SOCK_DGRAM 的工作。当?dng)你也可以跌q一D,如果你认?已经熟?zhn)的话?<br />? 友们Q现在是学习(fn) 数据装 (Data Encapsulation) 的时候了Q?q非帔R帔R要。It's so important that you might just learn about it if you take the networks course here at Chico State <img src="http://www.osprg.org/uploads/smil3dbd4e398ff7b.gif" alt="" />. 主要的内Ҏ(gu)Q一个包Q先是被W一个协?在这里是 TFTP )包装(“封装?Q?然后Q整个数?包括 TFTP ?被另外一个协?在这里是 UDP )装Q然后下 一? IP )Q一直重复下去,直到g(物理)? Ethernet )?<br />当另外一台机器接收到包,g先剥?Ethernet _(d)内核剥去 IP ?UDP _(d)TFTP E序再剥?TFTP _(d)最后得到数据?<br />? 在我们终于讲到臭名远播的 |络分层模型 (Layered Network Model)。这U网l模型在描述|络pȝ上相对其他模型有很多优点。例如,你可以写一个套接口 E序而不用关心数据的物理传输(串行口,以太|,q接单元接口 (AUI) q是其他介质? 因ؓ(f)底层的程序ؓ(f)你处理他们。实际的|络g和拓扑对于程序员来说是透明的?<br />不说其他废话了,我现在列出整个层ơ模型。如果你要参加网l考试Q可一定要CQ?<br />应用?(Application) <br />表示?(Presentation) <br />?x)话?(Session) <br />传输?(Transport) <br />|络?(Network) <br />数据链\?(Data Link) <br />物理?(Physical) <br />物理层是g(串口Q以太网{等)。应用层是和g层相隔最q的--他是用户和网l?交互的地斏V?<br />q个模型如此通用Q如果你惻I你可以把他作Z车指南。把他应用到 UnixQ结果是: <br />应用?(Application Layer) (telnet, ftp, {等) <br />传输?(Host-to-Host Transport Layer) (TCP, UDP) <br />Internet ?(Internet Layer) (IP 和\? <br />|络讉K?(Network Access Layer) (|络层,数据链\层和物理? <br />现在Q你可能看到q些层次如何协调来封装原始的数据了?<br />? 看徏立一个简单的数据包有多少工作Q哎呀Q你不得不使用 "cat" 来完?他们Q简直是W话。对于流式套接口你要作的?send() 发送数据。对于数据报 式套接口你按照你选择的方式封装数据然后用 sendto()。内核将Z建立传输 层和 Internet 层,g完成|络讉K层。这是CU技?<br />现在l束我们的网l理论速成班。哦Q忘记告诉你关于路由的事情了。但是我不准备谈他?如果你真的想知道Q那么参?IP RFC。如果你从来不曾了解他,也没?关系Q你q活着不是吗?<br />----------------------------------------------------------------------------<br />structs <br />l于到达q里了,l于谈到~程了。在q章Q我谈到被套接口用到的各种数据cd。因?他们中的一些太重要了?<br />首先是简单的一个:(x)socket descriptor。他是下面的cdQ?<br /> int <br />仅仅是一个常见的 int?<br />? 现在P事情变得不可思议了。请跟我一起忍受苦恼吧。注意这L(fng)事实Q?有两U字节排列顺序:(x)重要的字节在前面(有时? "octet")Q或者不重要的字节在前面?前一U叫“网l字节顺?(Network Byte Order)”。有些机器在内部是按照这个顺序储存数据,而另外一些则不然。当我说某数据必L?NBO 序Q那么你要调用函??? htons() )来将他从本机字节序 (Host Byte Order) 转换q来。如果我 没有提到 NBOQ?那么p他是本机字节序吧? <br />我的W一个结?TM)--struct sockaddr. q个数据l构 多类型的套接口储存套接口地址信息Q?<br />struct sockaddr { <br /> unsigned short sa_family; /* address family, AF_xxx */ <br /> char sa_data[14]; /* 14 bytes of protocol address */ <br />}; <br />sa_family 能够是各U各L(fng)事情Q但是在q篇文章中是 "AF_INET"?sa_data 为套接口储存目标地址和端口信息。看上去很笨拙,不是吗?<br />Z对付 struct sockaddrQ程序员创造了一个ƈ列的l构Q?struct sockaddr_in ("in" 代表 "Internet".) <br />struct sockaddr_in { <br /> short int sin_family; /* Address family */ <br /> unsigned short int sin_port; /* Port number */ <br /> struct in_addr sin_addr; /* Internet address */ <br /> unsigned char sin_zero[8]; /* Same size as struct sockaddr */ <br /><br />}; <br />q? 个数据结构让可以L处理套接口地址的基本元素。注?sin_zero (?被加入到q个l构Qƈ且长度和 struct sockaddr 一? 应该使用函数 bzero() ?memset() 来全部置零?Also, and this is the important bit, a pointer to a struct sockaddr_in can be cast to a pointer to a struct sockaddr and vice-versa. q样的话 即 socket() 惌的是 struct sockaddr *Q?你仍然可以?struct sockaddr_inQand cast it at the last minute! 同时Q注? sin_family ?struct sockaddr 中的 sa_family 一致ƈ能够讄?"AF_INET"。最后, sin_port ?sin_addr 必须是网l字节顺?(Network Byte Order)Q?<br />你也怼(x)反对道:(x)"但是Q怎么让整个数据结?struct in_addr sin_addr 按照|络字节序?" 要知道这个问题的{案Q我们就要仔l的看一 看这个数据结构:(x) struct in_addr, 有这样一个联?(unions)Q?<br />/* Internet address (a structure for historical reasons) */ <br /> struct in_addr { <br /> unsigned long s_addr; <br /> }; <br />? 曄是个最坏的联合Q但是现在那些日子过M。如果你声明 "ina" ?数据l构 struct sockaddr_in 的实例,那么 "ina.sin_addr.s_addr" 储?字节?IP 地址(|络字节序)。如果你不幸?pȝ使用的还是恐怖的联合 struct in_addr Q你q是可以攑ֿ4?节的 IP 地址是和上面我说的一?q是因ؓ(f) #define? <br />----------------------------------------------------------------------------<br />Convert the Natives! <br />我们现在到达下个章节。我们曾l讲了很多网l到本机字节序Q现在是采取行动的时MQ?<br />? 能够转换两种cdQ?short (两个字节)?long (四个字节)。这?函数对于变量cd unsigned 也适用。假设你惛_ short 从本机字节顺?转换为网l字节顺序。用 "h" 表示 "本机 (host)"Q接着?"to"Q然后用 "n" 表示 "|络 (network)"Q最后用 "s" 表示 "short"Q?h-to-n-s, 或?htons() ("Host to Network Short")?<br />太简单了... <br />如果不是太傻的话Q你一定想Cl合 "n"Q?h"Q?s"Q和 "l"。但是这里没?stolh() ("Short to Long Host") 函数Q但是这里有Q?<br />htons()--"Host to Network Short" <br />htonl()--"Host to Network Long" <br />ntohs()--"Network to Host Short" <br />ntohl()--"Network to Host Long" <br />? 在,你可能想你已l知道他们了。你也可能想Q?如果我改?char 的顺序会(x) 怎么样呢? 我的 68000 机器已经使用了网l字节顺序,我没有必要去调用 htonl() 转换 IP 地址? 你可能是对的Q但是当你移植你的程序到别的机器上的时候,你的E序?p|。可UL性!q里?Unix 世界Q记住:(x)在你数据放到网l上的时候,信他们是网l字 节顺序?<br />最后一点:(x)Z么在数据l构 struct sockaddr_in 中, sin_addr ?sin_port 需要{换ؓ(f)|络字节序Q?sin_family 不需要呢? {案是:(x)sin_addr ? sin_port 分别装在包?IP ?UDP 层。因此,他们必须要是|络字节序?但是 sin_family 域只是被内核 (kernel) 使用来决定在数据l构中包含什么类型的地址Q所以他应该是本机字节顺序。也?sin_family 没有 ? 送到|络上,他们可以是本机字节顺序?<br />----------------------------------------------------------------------------<br />IP 地址和如何处理他?<br />现在我们很幸q,因ؓ(f)我们有很多的函数来方便地操作 IP 地址。没有必要用手工计算 他们Q也没有必要?<< 操作W来操作 long?<br />? 先,假设你用 struct sockaddr_in inaQ你惛_ IP 地址 "132.241.5.10" 储存到其中。你要用的函数是 inet_addr()Q{?numbers-and-dots 格式?IP 地址?unsigned long。这个工作可以这h做:(x) <br /> ina.sin_addr.s_addr = inet_addr("132.241.5.10"); <br />注意Qinet_addr() q回的地址已经是按照网l字节顺序的Q你没有必要再去调用 htonl()?<br />? 面的代码可不是很健壮 (robust)Q因为没有错误检查。inet_addr() 在发生错?的时候返?1。记得二q制数吗? ?IP 地址?255.255.255.255 的时候返回的?(unsigned)-1Q这是个q播地址Q记住正的使用错误(g)查?<br />好了Q你? 在可以{换字W串形式?IP 地址?long 了。那么你有一个数据结?struct in_addrQ该如何按照 numbers-and-dots 格式打印? 在这?时候,也许你要用函?inet_ntoa() ("ntoa" 意思是 "network to ascii")Q?<br /> printf("%s",inet_ntoa(ina.sin_addr)); <br />他将打印 IP 地址。注意的是:(x)函数 inet_ntoa() 的参数是 struct in_addrQ而不? long。同时要注意的是他返回的是一个指向字W的指针??inet_ntoa 内部的指针静态地储存字符数组Q因此每ơ你调用 inet_ntoa() 的时候他覆盖以前的内容。例如:(x) <br /> char *a1, *a2; <br /> . <br /> . <br /> a1 = inet_ntoa(ina1.sin_addr); /* this is 198.92.129.1 */ <br /> a2 = inet_ntoa(ina2.sin_addr); /* this is 132.241.5.10 */ <br /> printf("address 1: %s\n",a1); <br /> printf("address 2: %s\n",a2); <br />q行l果是:(x) <br /> address 1: 132.241.5.10 <br /> address 2: 132.241.5.10 <br />如果你想保存地址Q那么用 strcpy() 保存到自q字符数组中?<br />q就是这章的内容了。以后,我们学?fn){?"whitehouse.gov" 形式的字W串到正??IP 地址(L(fng)后面?DNS 一章? <br />----------------------------------------------------------------------------<br />socket()--得到文g描述W! <br />我猜我不?x)再扯远?-我必讲 socket() q个pȝ调用了。这里是详细的定义:(x) <br /> #include <sys/types.h> <br /> #include <sys/socket.h> <br /> int socket(int domain, int type, int protocol); <br />? 是他们的参数怎么? 首先Qdomain 应该讄?"AF_INET"Q就象上面的 数据l构 struct sockaddr_in 中一栗然后,参数 type 告诉内核?SOCK_STREAM cdq是 SOCK_DGRAM cd。最后,?protocol 讄? "0"?注意Q有很多U?domain、typeQ?我不可能一一列出了,L(fng) socket() ?man page。当?dng)q有一?更好"的方?d?protocol。请?getprotobyname() ?man page? <br />socket() 只是q回你以后在pȝ调用U可能用到的 socket 描述W,或者在错误 的时候返?1。全局变量 errno 中储存错误倹{?请参?perror() ?man page? <br />----------------------------------------------------------------------------<br />bind()--我在哪个端口? <br />一 旦你得到套接口,你可能要套接口和机器上的一定的端口兌h?如果你想?listen() 来侦听一定端口的数据Q这是必要一?-MUD l常告诉你说用命?"telnet x.y.z 6969".)如果你只想用 connect()Q那么这个步骤没有必要。但是无论如何,L(fng)l读下去?<br />q里是系l调?bind() 的大略:(x) <br /> #include <sys/types.h> <br /> #include <sys/socket.h> <br /> int bind(int sockfd, struct sockaddr *my_addr, int addrlen); <br />sockfd 是调?socket q回的文件描q符。my_addr 是指?数据l构 struct sockaddr 的指针,他保存你的地址(即端口和 IP 地址) 信息。addrlen 讄?sizeof(struct sockaddr)?<br />单得很不是吗? 再看看例子:(x) <br /> #include <string.h> <br /> #include <sys/types.h> <br /> #include <sys/socket.h> <br /> #define MYPORT 3490 <br /> main() <br /> { <br /> int sockfd; <br /> struct sockaddr_in my_addr; <br /> sockfd = socket(AF_INET, SOCK_STREAM, 0); /* do some error checking! */ <br /> my_addr.sin_family = AF_INET; /* host byte order */ <br /> my_addr.sin_port = htons(MYPORT); /* short, network byte order */ <br /> my_addr.sin_addr.s_addr = inet_addr("132.241.5.10"); <br /> bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */ <br /> /* don't forget your error checking for bind(): */ <br /> bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); <br /> . <br /> . <br /> . <br />q里也有要注意的几g事情。my_addr.sin_port 是网l字节顺序,my_addr.sin_addr.s_addr 也是的。另外要注意到的事情是因pȝ的不同, 包含的头文g也不相同,h阅自q man page?<br />?bind() 主题中最后要说的话是Q在处理自己?IP 地址?或端口的时候,有些工作 是可以自动处理的?<br /> my_addr.sin_port = 0; /* choose an unused port at random */ <br /> my_addr.sin_addr.s_addr = INADDR_ANY; /* use my IP address */ <br />通过?赋给 my_addr.sin_portQ你告诉 bind() 自己选择合适的端口。同P ?y_addr.sin_addr.s_addr 讄?INADDR_ANYQ你告诉他自动填?他所q行的机器的 IP 地址?<br />如果你一向小心}慎,那么你可能注意到我没有将 INADDR_ANY 转换为网l字节顺序!q是因ؓ(f)我知道内部的东西QINADDR_ANY 实际上就?0Q即使你 改变字节的顺序,0依然?。但是完主义者说安全W一Q那么看下面的代码:(x) <br /> my_addr.sin_port = htons(0); /* choose an unused port at random */ <br /> my_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* use my IP address */ <br />你可能不怿Q上面的代码可以随便移植?<br />bind() 在错误的时候依然是q回-1Qƈ且设|全局变量 errno?<br />在你调用 bind() 的时候,你要心的另一件事情是Q不要采用小?024的端口号。所有小?024的端口号?被系l保留!你可以选择?024?5535(如果他们没有被别的程序用的??<br />? 要注意的另外一件小事是Q有时候你Ҏ(gu)不需要调用他。如果你使用 connect() 来和q程机器通讯Q你不要兛_你的本地端口?p你在使用 telnet 的时?Q你只要 单的调用 connect() 够可,他会(x)(g)查套接口是否l定Q如果没有,他会(x)自己l定 一个没有用的本地端口?<br />----------------------------------------------------------------------------<br />connect()--HelloQ?<br />? 在我们假设你是个 telnet E序。你的用户命令你(p?sh)?jing) TRON 中一?得到套接?的文件描q符。你听从命o(h)调用? socket()。下一步,你的用户告诉你通过端口23(??telnet 端口)q接?132.241.5.10"。你该怎么做呢? <br />q运的是Q你正在疯狂地阅?connect()--如何q接到远E主一章。你可不惌 你的用户失望?<br />connect() pȝ调用是这L(fng)Q?<br /> #include <sys/types.h> <br /> #include <sys/socket.h> <br /> int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); <br />sockfd 是系l调?socket() q回的套接口文g描述W。serv_addr 是保存着目的地端口和 IP 地址的数据结?struct sockaddr。addrlen 讄?sizeof(struct sockaddr)?<br />让我们来看个例子Q?<br /> #include <string.h> <br /> #include <sys/types.h> <br /> #include <sys/socket.h> <br /> #define DEST_IP "132.241.5.10" <br /> #define DEST_PORT 23 <br /> main() <br /> { <br /> int sockfd; <br /> struct sockaddr_in dest_addr; /* will hold the destination addr */ <br /> sockfd = socket(AF_INET, SOCK_STREAM, 0); /* do some error checking! */ <br /> dest_addr.sin_family = AF_INET; /* host byte order */ <br /> dest_addr.sin_port = htons(DEST_PORT); /* short, network byte order */ <br /> dest_addr.sin_addr.s_addr = inet_addr(DEST_IP); <br /> bzero(&(dest_addr.sin_zero), 8); /* zero the rest of the struct */ <br /> /* don't forget to error check the connect()! */ <br /> connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)); <br /> . <br /> . <br /> . <br />再一ơ,你应该检?connect() 的返回?-他在错误的时候返?1Qƈ 讄全局变量 errno?<br />同时Q你可能看到Q我没有调用 bind()。另外,我也没有本地的端口受我只关?我在q接。内核将为我选择一个合适的端口P而我们所q接的地方也自动地获得这些信息?<br />----------------------------------------------------------------------------<br />listen()--Will somebody please call me? <br />Ok, time for a change of pace. What if you don't want to connect to a remote host. Say, just for kicks, that you want to wait for incoming connections and handle them in some way. 处理q程分两步:(x)首先Q你?-listen()Q然后,你接?-accept() (L(fng) 下面的内??<br />除了要一点解释外Q系l调?listen 相当单?<br /> int listen(int sockfd, int backlog); <br />sockfd 是调?socket() q回的套接口文g描述W。backlog ?在进入队列中允许的连接数目。是什么意思呢? q入的连接是在队列中一直等待直C接受 (accept() L(fng)下面的文?的连接。他们的数目限制于队列的允许。大多数pȝ的允许数目是20Q你也可以设|ؓ(f)5?0?<br />和别的函CP在发生错误的时候返?1Qƈ讄全局变量 errno?<br />你可能想象到了,在你调用 listen() 前你或者要调用 bind() 或者让 内核随便选择一个端口。如果你想侦听进入的q接Q那么系l调用的序可能是这L(fng)Q?<br /> socket(); <br /> bind(); <br /> listen(); <br /> /* accept() goes here */ <br />因ؓ(f)他相当的明了Q我在q里不给Z子了??accept() 那一章的代码更?完全?真正ȝ(ch)的部分在 accept()?<br />----------------------------------------------------------------------------<br />accept()--"Thank you for calling port 3490." <br />? 备好了,pȝ调用 accept() ?x)有点古怪的地方的!你可以想象发生这L(fng)事情Q?有h从很q的地方通过一个你在侦?(listen()) 的端口连?(connect()) C的机器。他的连接将加入到等待接?(accept()) 的队列中。你调用 accept() 告诉他你有空闲的q接。他返回一个新的套接口文g描述W! 原来的一个还在侦听你的那个端口,新的最后在准备发?(send()) 和接?( recv()) 数据。这是q个q程Q?<br />函数是这样定义的Q?<br /> #include <sys/socket.h> <br /> int accept(int sockfd, void *addr, int *addrlen); <br />sockfd 相当单,是和 listen() 中一L(fng)套接口描q符。addr 是个指向局部的数据l构 struct sockaddr_in 的指针。This is where the information about the incoming connection will go (and you can determine which host is calling you from which port). 在他的地址传递给 accept 之前Qaddrlen 是个局部的整Ş变量Q设|ؓ(f) sizeof(struct sockaddr_in)。accept ?不会(x)多余的字节l?addr。如果你攑օ的少些,那么?addrlen 的g反映 出来?<br />同样Q在错误时返?1q设|全局变量 errno?<br />现在是你应该熟?zhn)的代码片Dc(din)?<br /> #include <string.h> <br /> #include <sys/types.h> <br /> #include <sys/socket.h> <br /> #define MYPORT 3490 /* the port users will be connecting to */ <br /> #define BACKLOG 10 /* how many pending connections queue will hold */ <br /> main() <br /> { <br /> int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */ <br /> struct sockaddr_in my_addr; /* my address information */ <br /> struct sockaddr_in their_addr; /* connector's address information */ <br /> int sin_size; <br /> sockfd = socket(AF_INET, SOCK_STREAM, 0); /* do some error checking! */ <br /> my_addr.sin_family = AF_INET; /* host byte order */ <br /> my_addr.sin_port = htons(MYPORT); /* short, network byte order */ <br /> my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ <br /> bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */ <br /> /* don't forget your error checking for these calls: */ <br /> bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); <br /> listen(sockfd, BACKLOG); <br /> sin_size = sizeof(struct sockaddr_in); <br /> new_fd = accept(sockfd, &their_addr, &sin_size); <br /> . <br /> . <br /> . <br />注意Q在pȝ调用 send() ?recv() 中你应该使用新的文g描述W?如果你只惌一个连接进来,那么你可以?close() d闭原来的文g描述 W?sockfd 来避免同一个端口更多的q接?<br />----------------------------------------------------------------------------<br />send() and recv()--Talk to me, baby! <br />q两个函数用于流式套接口和数据报套接口的通讯。如果你喜欢使用无连接的数据?套接口,你应该看一看下面关?sendto() ?recvfrom() 的章节?<br />send() 是这L(fng)Q?<br /> int send(int sockfd, const void *msg, int len, int flags); <br />sockfd 是你惛_送数据的套接口描q符(或者是调用 socket() 或者是 accept() q回的?msg 是指向你惛_送的数据的指针。len ? 数据的长度。把 flags 讄?0 可以了?详细的资料请?send() ?man page)?<br />q里是一些可能的例子Q?<br /> char *msg = "Beej was here!"; <br /> int len, bytes_sent; <br /> . <br /> . <br /> len = strlen(msg); <br /> bytes_sent = send(sockfd, msg, len, 0); <br /> . <br />. <br /> . <br />send() q回实际发送的数据的字节数--他可能小于你要求发送的数目Q也即你告诉他要发送一堆数据可是他不能处理成功。他只是发送他可能发送的数据Q然? 希望你以后能够发送其他的数据。记住,如果 send() q回的数据和 len ? 匚wQ你应该发送其他的数据。但是这里也有个好消息:(x)如果你要发送的包很?于大约 1K)Q他可能处理让数据一ơ发送完。最后,在错误的时候返?1Qƈ讄 errno?<br />recv() 函数很相|(x) <br /> int recv(int sockfd, void *buf, int len, unsigned int flags); <br />sockfd 是要ȝ套接口描q符。buf 是要ȝ信息的缓册Ӏlen ?~冲的最大长度。flags 也可以设|ؓ(f)0?请参考r(ji)ecv() ?man page? <br />recv() q回实际d~冲的数据的字节数。或者在错误的时候返?1Q同时设|?errno?<br />很简单,不是? 你现在可以在式套接口上发送数据和接收数据了。你现在?Unix |络E序员了Q?<br />----------------------------------------------------------------------------<br />sendto() ?recvfrom()--Talk to me, DGRAM-style <br />"q很不错?Q我听到你说Q?但是你还没有讲无q接数据报套接口呢?没问题,现在我们开?q个内容?<br />既然数据报套接口不是q接到远E主机的Q那么在我们发送一个包之前需要什么信息呢? 不错Q是目标地址Q看下面的:(x) <br /> int sendto(int sockfd, const void *msg, int len, unsigned int flags, <br /> const struct sockaddr *to, int tolen); <br />? 已经看到了,除了另外的两个信息外Q其余的和函?send() 是一L(fng)?to 是个指向数据l构 struct sockaddr 的指针,他包含了目的地的 IP 地址和断口信息。tolen 可以单地讄?sizeof(struct sockaddr)?<br />和函?send() cMQsendto() q回实际发送的字节?他也可能于?惌发送的字节敎ͼ)Q或者在错误的时候返?-1?<br />怼的还有函?recv() ?recvfrom()。recvfrom() 的定义是 q样的:(x) <br /> int recvfrom(int sockfd, void *buf, int len, unsigned int flags <br /> struct sockaddr *from, int *fromlen); <br />? 一ơ,除了一点多余的参数外,q个函数?recv() 也是一L(fng)。from ?一个指向局部数据结?struct sockaddr 的指针,他的内容是源机器 ?IP 地址和端口信息。fromlen 是个 int 型的局部指针,他的初始??sizeof(struct sockaddr)。函数调用后Qfromlen 保存着 实际储存?from 中的地址的长度?<br />recvfrom() q回收到的字节长度,或者在发生错误后返?-1?<br />CQ如果你是用 connect() q接一个数据报套接口,你可以简单的调用 send() ?recv() 来满你的要求。这个时候依然是数据报套接口Q依然?UDPQ系l?自动的加上了目标和源的信息?<br />----------------------------------------------------------------------------<br />close() ?shutdown()--Get outta my face! <br />你已l整天都在发?(send()) 和接?(recv()) 数据了,现在你准?关闭你的套接口描q符了。这很简单,你可以用一般的 Unix 文g描述W的 close() ?敎ͼ(x) <br /> close(sockfd); <br />他将防止套接口上更多的数据的d。Q何在另一端读写套接口的企N返回错误信息?<br />如果你想在如何关闭套接口上有多一点的控制Q你可以使用函数 shutdown()。他能够?你将一定方向的通讯或者双向的通讯(p close() 一?关闭Q你可以使用Q?<br /> int shutdown(int sockfd, int how); <br />sockfd 是你惌关闭的套接口文g描述复。how 的值是下面的其中之一Q?<br />0 - Further receives are disallowed <br />1 - Further sends are disallowed <br />2 - Further sends and receives are disallowed (?close() 一?<br />shutdown() 成功时返?0Q失败时q回 -1(同时讄 errno? <br />如果在无q接的数据报套接口中使用 shutdown()Q那么只不过是让 send() ?recv() 不能使用(记得你在数据报套接口中用了 connect 后是可以 使用他们的吗?) <br />----------------------------------------------------------------------------<br />getpeername()--Who are you? <br />q个函数太简单了?<br />他太单了Q以x都不惛_列一章。但是我q是q样做了?<br />函数 getpeername() 告诉你在q接的流式套接口上谁在另外一辏V函数是q样的:(x) <br /> #include <sys/socket.h> <br /> int getpeername(int sockfd, struct sockaddr *addr, int *addrlen); <br />sockfd 是连接的式套接口的描述W。addr 是一个指向结?struct sockaddr (或者是 struct sockaddr_in) 的指针,他保存着 q接的另一边的信息。addrlen 是一?int 型的指针Q他初始化ؓ(f) sizeof(struct sockaddr)?<br />函数在错误的时候返?-1Q设|相应的 errno?<br />一 旦你获得他们的地址Q你可以使用 inet_ntoa() 或?gethostbyaddr() 来打印或者获得更多的信息。但是你不能得到他的帐号?如果他运行着愚蠢的守护进E,q是 可能的,但是他的讨论已经出了本文的范围Q请参? RFC-1413 以获得更多的信息? <br />------------------------------------------------------------------------------- <br />gethostname()--Who am I? <br />甚至?getpeername() q简单的函数?gethostname()。他q回你程?所q行的机器的L名字。然后你可以使用 gethostbyname() 以获得你的机器的 IP 地址?<br />下面是定义:(x) <br /> #include <unistd.h> <br /> int gethostname(char *hostname, size_t size); <br />参数很简单:(x)hostname 是一个字W数l指针,他将在函数返回时保存 L名。size ?hostname 数组的字节长度?<br />函数调用成功时返?0Q失败时q回 -1Qƈ讄 errno?<br />-------------------------------------------------------------------------------- <br />DNS--You say "whitehouse.gov", I say "198.137.240.100" <br />? 果你不知?DNS 的意思,那么我告诉你Q他代表"域名服务 (Domain Name Service)"。他主要的功能是Q你l他一个容易记忆的某站点的地址Q他l你 IP 地址(然后你就可以 使用 bind(), connect(), sendto() 或者其他函数?当一个h 输入Q?<br /> $ telnet whitehouse.gov <br />telnet 能知道他连?(connect()) ?"198.137.240.100"?<br />但是q是如何工作的呢? 你可以调用函?gethostbyname()Q?<br /> #include <netdb.h> <br /> struct hostent *gethostbyname(const char *name); <br />很明白的是,他返回一个指?struct hostent 的指针。这个数据结构是 q样的:(x) <br /> struct hostent { <br /> char *h_name; <br /> char **h_aliases; <br /> int h_addrtype; <br /> int h_length; <br /> char **h_addr_list; <br /> }; <br /> #define h_addr h_addr_list[0] <br />q里是这个数据结构的详细资料Q?struct hostent: <br />h_name - Official name of the host. <br />h_aliases - A NULL-terminated array of alternate names for the host. <br />h_addrtype - The type of address being returned; usually AF_INET. <br />h_length - The length of the address in bytes. <br />h_addr_list - A zero-terminated array of network addresses for the host. Host addresses are in Network Byte Order. <br />h_addr - The first address in h_addr_list. <br />gethostbyname() 成功时返回一个指?struct hostent ?指针Q或者是个空 (NULL) 指针?但是和以前不同,errno 不设|,h_errno 讄错误信息。请看下面的 herror()? <br />但是如何使用? q个函数可不象他看上去那么难用?<br />q里是个例子Q?<br /> #include <stdio.h> <br /> #include <stdlib.h> <br /> #include <errno.h> <br /> #include <netdb.h> <br /> #include <sys/types.h> <br /> #include <netinet/in.h> <br /> int main(int argc, char *argv[]) <br /> { <br /> struct hostent *h; <br /> if (argc != 2) { /* error check the command line */ <br /> fprintf(stderr,"usage: getip address\n"); <br /> exit(1); <br /> } <br /> if ((h=gethostbyname(argv[1])) == NULL) { /* get the host info */ <br /> herror("gethostbyname"); <br /> exit(1); <br /> } <br /> printf("Host name : %s\n", h->h_name); <br /> printf("IP Address : %s\n",inet_ntoa(*((struct in_addr *)h->h_addr))); <br /> return 0; <br /> } <br />在?gethostbyname() 的时候,你不能用 perror() 打印错误信息(??errno 没有使用)Q你应该调用 herror()?<br />相当单,你只是传递一个保存机器名的自负串(例如 "whitehouse.gov") l?gethostbyname()Q然后从q回的数据结?struct hostent ?攉信息?<br />唯一让hqh的是打印 IP 地址信息。h->h_addr 是一?char *Q?但是 inet_ntoa() 需要的?struct in_addr。因此,?转换 h->h_addr ?struct in_addr *Q然后得到数据?<br />-------------------------------------------------------------------------------- <br />Client-Server Background <br />q? 里是个客?-服务器的世界。在|络上的所有东襉K是在处理客户q程和服务器q程的交谈?举个 telnet 的例子。当你用 telnet (客户)通过 23 L(fng)口登陆到LQ主Zq行 的一个程?一般叫 telnetdQ服务器)ȀzR他处理q个q接Q显C登陆界面,{等?<br />Figure 2. The Client-Server Relationship. <br />?2 说明了客户和服务器之间的信息交换?<br />? 意,?-服务器之间可以用SOCK_STREAM、SOCK_DGRAM 或者其?只要他们采用相同?。一些很好的客户--服务器的例子? telnet/telnetd?ftp/ftpd ?bootp/bootpd。每ơ你使用 ftp ?时候,在远端都有一?ftpd Z服务?<br />一般,在服务端只有一个服务器Q他采用 fork() 来处理多个客L(fng)q接。基本的 E序是:(x)服务器等待一个连接,接受 (accept()) q接Q然?fork() 一?子进E处理他。这是下一章我们的例子中会(x)讲到的?<br />-------------------------------------------------------------------------------- <br />单的服务?<br />q个服务器所做的全部工作是在留式q接上发送字W串 "Hello, World!\n"。你?试q个E序的话Q可以在一台机器上q行该程序,然后在另外一机器上登陆:(x) <br /> $ telnet remotehostname 3490 <br />remotehostname 是该E序q行的机器的名字?<br />服务器代码:(x) <br /> #include <stdio.h> <br /> #include <stdlib.h> <br /> #include <errno.h> <br /> #include <string.h> <br /> #include <sys/types.h> <br /> #include <netinet/in.h> <br /> #include <sys/socket.h> <br /> #include <sys/wait.h> <br /> #define MYPORT 3490 /* the port users will be connecting to */ <br /> #define BACKLOG 10 /* how many pending connections queue will hold */ <br /> main() <br /> { <br /> int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */ <br /> struct sockaddr_in my_addr; /* my address information */ <br /> struct sockaddr_in their_addr; /* connector's address information */ <br /> int sin_size; <br /> if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { <br /> perror("socket"); <br /> exit(1); <br /> } <br /> my_addr.sin_family = AF_INET; /* host byte order */ <br /> my_addr.sin_port = htons(MYPORT); /* short, network byte order */ <br /> my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ <br /> bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */ <br /> if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \ <br /> == -1) { <br /> perror("bind"); <br /> exit(1); <br /> } <br /> if (listen(sockfd, BACKLOG) == -1) { <br /> perror("listen"); <br /> exit(1); <br /> } <br /> while(1) { /* main accept() loop */ <br /> sin_size = sizeof(struct sockaddr_in); <br /> if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, \ <br /> &sin_size)) == -1) { <br /> perror("accept"); <br /> continue; <br /> } <br /> printf("server: got connection from %s\n", \ <br /> inet_ntoa(their_addr.sin_addr)); <br /> if (!fork()) { /* this is the child process */ <br /> if (send(new_fd, "Hello, world!\n", 14, 0) == -1) <br /> perror("send"); <br /> close(new_fd); <br /> exit(0); <br /> } <br /> close(new_fd); /* parent doesn't need this */ <br /> while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */ <br /> } <br /> } <br />如果你很挑剔的话Q一定不满意我所有的代码都在一个很大的 main() 函数 中。如果你不喜Ƣ,可以划分得更l点?<br />你也可以用我们下一章中的程序得到服务器端发送的字符丌Ӏ?<br />-------------------------------------------------------------------------------- <br />单的客户E序 <br />q个E序比服务器q简单。这个程序的所有工作是通过 3490 端口q接到命令行中制定的LQ?然后得到服务器的字符丌Ӏ?<br />客户代码: <br /> #include <stdio.h> <br /> #include <stdlib.h> <br /> #include <errno.h> <br /> #include <string.h> <br /> #include <netdb.h> <br /> #include <sys/types.h> <br /> #include <netinet/in.h> <br /> #include <sys/socket.h> <br /> #define PORT 3490 /* the port client will be connecting to */ <br /> #define MAXDATASIZE 100 /* max number of bytes we can get at once */ <br /> int main(int argc, char *argv[]) <br /> { <br /> int sockfd, numbytes; <br /> char buf[MAXDATASIZE]; <br /> struct hostent *he; <br /> struct sockaddr_in their_addr; /* connector's address information */ <br /> if (argc != 2) { <br /> fprintf(stderr,"usage: client hostname\n"); <br /> exit(1); <br /> } <br /> if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */ <br /> herror("gethostbyname"); <br /> exit(1); <br /> } <br /> if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { <br /> perror("socket"); <br /><br /> exit(1); <br /><br /> } <br /><br /> their_addr.sin_family = AF_INET; /* host byte order */ <br /><br /> their_addr.sin_port = htons(PORT); /* short, network byte order */ <br /><br /> their_addr.sin_addr = *((struct in_addr *)he->h_addr); <br /><br /> bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */ <br /><br /> if (connect(sockfd, (struct sockaddr *)&their_addr, \ <br /> sizeof(struct sockaddr)) == -1) { <br /> perror("connect"); <br /> exit(1); <br /> } <br /> if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) { <br /> perror("recv"); <br /> exit(1); <br /> } <br /> buf[numbytes] = '\0'; <br /> printf("Received: %s",buf); <br /> close(sockfd); <br /> return 0; <br /> } <br />注意Q如果你在运行服务器之前q行客户E序Qconnect() 返?"Connection refused" 信息?<br />-------------------------------------------------------------------------------- <br />数据?Sockets <br />我不惌更多了,所以我l出代码 talker.c ?listener.c?<br />listener 在机器上{待在端?4590 来的数据包。talker 发送数据包C定的 机器Q他包含用户在命令行输入的东ѝ?<br />q里是 listener.cQ?<br /> #include <stdio.h> <br /> #include <stdlib.h> <br /> #include <errno.h> <br /> #include <string.h> <br /> #include <sys/types.h> <br /> #include <netinet/in.h> <br /> #include <sys/socket.h> <br /> #include <sys/wait.h> <br /> #define MYPORT 4950 /* the port users will be sending to */ <br /> #define MAXBUFLEN 100 <br /> main() <br /> { <br /> int sockfd; <br /> struct sockaddr_in my_addr; /* my address information */ <br /> struct sockaddr_in their_addr; /* connector's address information */ <br /> int addr_len, numbytes; <br /> char buf[MAXBUFLEN]; <br /> if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { <br /> perror("socket"); <br /> exit(1); <br /> } <br /> my_addr.sin_family = AF_INET; /* host byte order */ <br /> my_addr.sin_port = htons(MYPORT); /* short, network byte order */ <br /> my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ <br /> bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */ <br /> if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \ <br /> == -1) { <br /> perror("bind"); <br /> exit(1); <br /> } <br /> addr_len = sizeof(struct sockaddr); <br /> if ((numbytes=recvfrom(sockfd, buf, MAXBUFLEN, 0, \ <br /> (struct sockaddr *)&their_addr, &addr_len)) == -1) { <br /> perror("recvfrom"); <br /> exit(1); <br /> } <br /> printf("got packet from %s\n",inet_ntoa(their_addr.sin_addr)); <br /> printf("packet is %d bytes long\n",numbytes); <br /> buf[numbytes] = '\0'; <br /> printf("packet contains \"%s\"\n",buf); <br /> close(sockfd); <br /> } <br />注意在我们的调用 socket()Q我们最后用了 SOCK_DGRAM。同Ӟ没有 必要M?listen() 或?accept()。我们在使用无连接的数据报套接口Q?<br />下面?talker.cQ?<br /> #include <stdio.h> <br /> #include <stdlib.h> <br /> #include <errno.h> <br /> #include <string.h> <br /> #include <sys/types.h> <br /> #include <netinet/in.h> <br /> #include <netdb.h> <br /> #include <sys/socket.h> <br /> #include <sys/wait.h> <br /> #define MYPORT 4950 /* the port users will be sending to */ <br /> int main(int argc, char *argv[]) <br /> { <br /> int sockfd; <br /> struct sockaddr_in their_addr; /* connector's address information */ <br /> struct hostent *he; <br /> int numbytes; <br /> if (argc != 3) { <br /> fprintf(stderr,"usage: talker hostname message\n"); <br /> exit(1); <br /> } <br /> if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */ <br /> herror("gethostbyname"); <br /> exit(1); <br /> } <br /> if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { <br /> perror("socket"); <br /> exit(1); <br /> } <br /> their_addr.sin_family = AF_INET; /* host byte order */ <br /> their_addr.sin_port = htons(MYPORT); /* short, network byte order */ <br /> their_addr.sin_addr = *((struct in_addr *)he->h_addr); <br /> bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */ <br /> if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0, \ <br /> (struct sockaddr *)&their_addr, sizeof(struct sockaddr))) == -1) { <br /> perror("sendto"); <br /> exit(1); <br /> } <br /> printf("sent %d bytes to %s\n",numbytes,inet_ntoa(their_addr.sin_addr)); <br /> close(sockfd); <br /> return 0; <br /> } <br />q就是所有的了。在一台机器上q行 listenerQ然后在另外一台机器上q行 talker。观察他们的通讯Q?<br />Except for one more tiny detail that I've mentioned many times in the past: connected datagram sockets. I need to talk about this here, since we're in the datagram section of the document. Let's say that talker calls connect() and specifies the listener's address. From that point on, talker may only sent to and receive from the address specified by connect(). For this reason, you don't have to use sendto() and recvfrom(); you can simply use send() and recv(). <br />-------------------------------------------------------------------------------- <br />d <br />? 塞,你也许早听说了?d"?"sleep" 的科技行话。你可能注意到前面运行的 listener E序Q他在那里不停地q行Q等待数据包的到来。实际在q行的是 他调?recvfrom()Q然后没有数据,因此 recvfrom() ?d (block)" 直到数据的到来?<br />很多函数都利用阻塞。accept() dQ所有的 recv*() 函数d。他们之所以能q样做是因ؓ(f)他们被允许这样做。当你第一ơ调?socket() 建立套接口描q符的时候,内核将他设|ؓ(f)d。如果你不想套接口阻塞,你就要调用函?fcntl()Q?<br /> #include <unistd.h> <br /> #include <fcntl.h> <br /> . <br /> . <br /> sockfd = socket(AF_INET, SOCK_STREAM, 0); <br /> fcntl(sockfd, F_SETFL, O_NONBLOCK); <br /> . <br /> . <br />通过讄套接口ؓ(f)非阻塞,你能够有效地"询问"套接口以获得信息。如果你试着 从一个非d的套接口M息ƈ且没有Q何数据,他不?x)变成阻?-他将q回 -1 q??errno 讄?EWOULDBLOCK?<br />但是一般说来,q种轮询不是个好L。如果你让你的程序在忙等状态查询套接口的数据, 你将费大量?CPU 旉。更好的解决之道是用下一章讲?select() L?是否有数据要读进来?<br />-------------------------------------------------------------------------------- <br />select()--多\同步 I/O <br />虽然q个函数有点奇怪,但是他很有用。假设这L(fng)情况Q你是个服务器,你一边在不停?从连接上L据,一边在侦听q接上的信息?<br />? 问题Q你可能?x)说Q不是一?accept() 和两?recv() ? q么Ҏ(gu) 吗,朋友? 如果你在调用 accept() 的时候阻塞呢? 你怎么能够同时接受 recv() 数据? "用非d的套接口啊!" 不行Q你不想耗尽所有的 CPUQ不是吗? 那么Q该如何是好? <br />select() 让你可以同时监视多个套接口。如果你想知道的话,那么他就?x)告诉你哪个套接口准备读Q哪个又 准备好了写,哪个套接口又发生了例?(exception)?<br />闲话说Q下面是 select()Q?<br /> #include <sys/time.h> <br /> #include <sys/types.h> <br /> #include <unistd.h> <br /> int select(int numfds, fd_set *readfds, fd_set *writefds, <br /> fd_set *exceptfds, struct timeval *timeout); <br />q? 个函数监视一pd文g描述W,特别?readfds、writefds ?exceptfds。如果你想知道你是否能够从标准输入和套接口描q符 sockfd ?入数据,你只要将文g描述W?0 ?sockfd 加入到集?readfds 中?参数 numfds 应该{于最高的文g描述W的值加1。在q个例子中,你应该设|该??sockfd+1。因Z一定大于标准输入的文g描述W?(0)?<br />当函?select() q回的时候,readfds 的g改ؓ(f)反映你选择的哪个文?描述W可以读。你可以用下面讲到的?FD_ISSET() 来测试?<br />在我们l下M前,让我来讲讲如何对q些集合q行操作。每个集合类型都?fd_set?下面有一些宏来对q个cdq行操作Q?<br />FD_ZERO(fd_set *set) - clears a file descriptor set <br />FD_SET(int fd, fd_set *set) - adds fd to the set <br />FD_CLR(int fd, fd_set *set) - removes fd from the set <br />FD_ISSET(int fd, fd_set *set) - tests to see if fd is in the set <br />最 后,是有点古怪的数据l构 struct timeval。有时你可不xq等待别人发送数据过来。也总么事情都没有发生的时候你也想每隔96U在l端 上打印字W串 "Still Going..."。这个数据结构允怽讑֮一个时_(d)如果旉CQ??select() q没有找C个准备好的文件描q符Q他返回让你l处理?<br />数据l构 struct timeval 是这L(fng)Q?<br /> struct timeval { <br /> int tv_sec; /* seconds */ <br /> int tv_usec; /* microseconds */ <br /> }; <br />? 要将 tv_sec 讄Z要等待的U数Q将 tv_usec 讄Z要等待的微秒数就可以了。是的,是微U而不是毫U?,000微秒{于1豪秒Q?,000毫秒{于1U。也是_(d)1U等?,000,000? U。ؓ(f)什么用W号 "usec" ? 字母 "u" 很象希腊字母 MuQ??Mu 表示 "? 的意思。当?dng)函数q回的时?timeout 可能是剩余的 旉Q之所以是可能Q是因ؓ(f)他依赖于你的 Unix 操作pȝ?<br />哈!我们现在有一个微U的定时器Q不要计了Q标准的 Unix pȝ的时间片?00毫秒Q所?无论你如何设|你的数据结?struct timevalQ你都要{待那么长的 旉?<br />q? 有一些有的事情Q如果你讄数据l构 struct timeval 中的 数据?0Qselect() 立卌Ӟq样可以有效地轮询集合中的 所有的文g描述W。如果你参?timeout 赋gؓ(f) NULLQ那么将永远不会(x)发生时Q即一直等到第一个文件描q符qA。最后,如果你不是很兛_{待多长旉Q那?把他赋?NULL 吧?<br />下面的代码演CZ在标准输入上{待 2.5 U:(x) <br /> #include <sys/time.h> <br /> #include <sys/types.h> <br /> #include <unistd.h> <br /> #define STDIN 0 /* file descriptor for standard input */ <br /> main() <br /> { <br /> struct timeval tv; <br /> fd_set readfds; <br /> tv.tv_sec = 2; <br /> tv.tv_usec = 500000; <br /> FD_ZERO(&readfds); <br /> FD_SET(STDIN, &readfds); <br /> /* don't care about writefds and exceptfds: */ <br /> select(STDIN+1, &readfds, NULL, NULL, &tv); <br /> if (FD_ISSET(STDIN, &readfds)) <br /> printf("A key was pressed!\n"); <br /> else <br /> printf("Timed out.\n"); <br /> } <br />如果你是在一?line buffered l端上,那么你敲的键应该是回?(RETURN)Q否则无论如?他都?x)超时?<br />现在Q你可能回认是在数据报套接口上{待数据的方?-你是对的Q他可能是?有些 Unix pȝ可以按这U方式,而另外一些则不能。你在尝试以前可能要先看看本pȝ ?man page 了?<br />最后一件关?select() 的事情:(x)如果你有一个正在侦?(listen()) 的套接口Q你可以通过该套接口的文g描述W加入到 readfds 集合中来?是否有新的连接?<br />q就是我关于函数 select() 要讲的所有的东西?img src ="http://www.shnenglu.com/lmlf001/aggbug/6370.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/lmlf001/" target="_blank">芥之?/a> 2006-04-27 12:11 <a href="http://www.shnenglu.com/lmlf001/archive/2006/04/27/6370.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux|络~程-- 服务器模型(ZZQ?/title><link>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6369.html</link><dc:creator>芥之?/dc:creator><author>芥之?/author><pubDate>Thu, 27 Apr 2006 03:58:00 GMT</pubDate><guid>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6369.html</guid><wfw:comment>http://www.shnenglu.com/lmlf001/comments/6369.html</wfw:comment><comments>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6369.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/lmlf001/comments/commentRss/6369.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/lmlf001/services/trackbacks/6369.html</trackback:ping><description><![CDATA[ <p align="left"> <font size="4">原文地址Qhttp://study.pay500.com/1/s12212.htm<br /></font> </p> <p align="center"> <font size="4">Linux|络~程--9. 服务器模?/font> <br /> <br /> </p> <br /> 学习(fn)q《Y件工E》吧.软g工程可是每一个程序员"必修"的课E啊.如果你没有学?fn)? 你去看一? 在这一章里?我们一h从Y件工E的角度学习(fn)|络~程的思想.在我们写E序之前, 我们都应该从软g工程的角度规划好我们的Y?q样我们开发Y件的效率才会(x)? 在网l程序里?一般的来说都是许多客户机对应一个服务器.Z处理客户机的h, Ҏ(gu)务端的程序就提出了特D的要求.我们学习(fn)一下目前最常用的服务器模型. <br /><br />循环服务?循环服务器在同一个时d可以响应一个客L(fng)的请? <br /><br />q发服务?q发服务器在同一个时d以响应多个客L(fng)的请? <br /><br /><br />9.1 循环服务?UDP服务? <br />UDP循环服务器的实现非常?UDP服务器每ơ从套接字上d一个客L(fng)的请?处理, 然后结果返回给客户? <br /><br />可以用下面的法来实? <br /><br /> socket(...); <br /> bind(...); <br /> while(1) <br /> { <br /> recvfrom(...); <br /> process(...); <br /> sendto(...); <br /> } <br /><br />因ؓ(f)UDP是非面向q接?没有一个客L(fng)可以老是占住服务? 只要处理q程不是d@? 服务器对于每一个客h的请求L能够满. <br />9.2 循环服务?TCP服务? <br />TCP循环服务器的实现也不?TCP服务器接受一个客L(fng)的连?然后处理,完成了这个客L(fng)所有请求后,断开q接. <br /><br />法如下: <br /><br /> socket(...); <br /> bind(...); <br /> listen(...); <br /> while(1) <br /> { <br /> accept(...); <br /> while(1) <br /> { <br /> read(...); <br /> process(...); <br /> write(...); <br /> } <br /> close(...); <br /> } <br /><br />TCP循环服务器一ơ只能处理一个客L(fng)的请?只有在这个客L(fng)所有请求都满? 服务器才可以l箋后面的请?q样如果有一个客L(fng)占住服务器不放时,其它的客h都不能工作了.因此,TCP服务器一般很用循环服务器模型的. <br /><br />9.3 q发服务?TCP服务? <br />Z弥补循环TCP服务器的~陷,Z又想Zq发服务器的模型. q发服务器的思想是每一个客h的请求ƈ不由服务器直接处?而是服务器创Z?子进E来处理. <br /><br />法如下: <br /><br /> socket(...); <br /> bind(...); <br /> listen(...); <br /> while(1) <br /> { <br /> accept(...); <br /> if(fork(..)==0) <br /> { <br /> while(1) <br /> { <br /> read(...); <br /> process(...); <br /> write(...); <br /> } <br /> close(...); <br /> exit(...); <br /> } <br /> close(...); <br /> } <br /><br />TCPq发服务器可以解决TCP循环服务器客h独占服务器的情况. 不过也同时带来了一个不的问题.Z响应客户机的h,服务器要创徏子进E来处理. 而创建子q程是一U非常消耗资源的操作. <br /><br />9.4 q发服务?多\复用I/O <br />Z解决创徏子进E带来的pȝ资源消?Z又想Z多\复用I/O模型. <br /><br />首先介绍一个函数select <br /><br /> int select(int nfds,fd_set *readfds,fd_set *writefds, <br /> fd_set *except fds,struct timeval *timeout) <br /> void FD_SET(int fd,fd_set *fdset) <br /> void FD_CLR(int fd,fd_set *fdset) <br /> void FD_ZERO(fd_set *fdset) <br /> int FD_ISSET(int fd,fd_set *fdset) <br /><br />一般的来说当我们在向文件读写时,q程有可能在d出阻?直到一定的条g满. 比如我们从一个套接字L据时,可能~冲区里面没有数据可?通信的对方还没有 发送数据过?,q个时候我们的读调用就?x)等?d)直到有数据可?如果我们?希望d,我们的一个选择是用selectpȝ调用. 只要我们讄好select的各个参?那么当文件可以读写的时候select?通知"我们 说可以读写了. readfds所有要ȝ文g文g描述W的集合 <br />writefds所有要的写文g文g描述W的集合 <br /><br />exceptfds其他的服要向我们通知的文件描q符 <br /><br />timeout时讄. <br /><br />nfds所有我们监控的文g描述W中最大的那一个加1 <br /><br />在我们调用select时进E会(x)一直阻塞直C下的一U情况发? 1)有文件可以读.2)有文件可以写.3)时所讄的时间到. <br /><br />Z讄文g描述W我们要使用几个? FD_SETfd加入到fdset <br /><br />FD_CLRfd从fdset里面清除 <br /><br />FD_ZERO从fdset中清除所有的文g描述W? <br /><br />FD_ISSET判断fd是否在fdset集合? <br /><br />使用select的一个例? <br /><br />int use_select(int *readfd,int n) <br />{ <br /> fd_set my_readfd; <br /> int maxfd; <br /> int i; <br /><br /> maxfd=readfd[0]; <br /> for(i=1;i<br /> if(readfd[i]>maxfd) maxfd=readfd[i]; <br /> while(1) <br /> { <br /> /* 所有的文g描述W加? */ <br /> FD_ZERO(&my_readfd); <br /> for(i=0;i<br /> FD_SET(readfd[i],*my_readfd); <br /> /* q程d */ <br /> select(maxfd+1,& my_readfd,NULL,NULL,NULL); <br /> /* 有东西可以读? */ <br /> for(i=0;i<br /> if(FD_ISSET(readfd[i],&my_readfd)) <br /> { <br /> /* 原来是我可以M */ <br /> we_read(readfd[i]); <br /> } <br /> } <br />} <br /><br />使用select后我们的服务器程序就变成? <br /><br /><br /> 初始?socket,bind,listen); <br /><br /> while(1) <br /> { <br /> 讄监听d文g描述W?FD_*); <br /><br /> 调用select; <br /><br /> 如果是們֐套接字就l?说明一个新的连接请求徏? <br /> { <br /> 建立q接(accept); <br /> 加入到监听文件描q符中去; <br /> } <br /> 否则说明是一个已l连接过的描q符 <br /> { <br /> q行操作(read或者write); <br /> } <br /><br /> } <br /><br />多\复用I/O可以解决资源限制的问?着模型实际上是UDP循环模型用在了TCP上面. q也带来了一些问?如由于服务器依次处理客户的请?所以可能会(x)D有的客户 ?x)等待很? <br /><br />9.5 q发服务?UDP服务? <br />Z把ƈ发的概念用于UDP得Cq发UDP服务器模? q发UDP服务器模型其实是单的.和ƈ发的TCP服务器模型一h创徏一个子q程来处理的 法和ƈ发的TCP模型一? <br /><br />除非服务器在处理客户端的h所用的旉比较长以?Z实际上很用q种模型. <br /><br /><br />9.6 一个ƈ发TCP服务器实? <br /><br />#include <br />#include <br />#include <br />#include <br />#include <br />#define MY_PORT 8888 <br /><br />int main(int argc ,char **argv) <br />{ <br /> int listen_fd,accept_fd; <br /> struct sockaddr_in client_addr; <br /> int n; <br /><br /> if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0) <br /> { <br /> printf("Socket Error:%s\n\a",strerror(errno)); <br /> exit(1); <br /> } <br /><br /> bzero(&client_addr,sizeof(struct sockaddr_in)); <br /> client_addr.sin_family=AF_INET; <br /> client_addr.sin_port=htons(MY_PORT); <br /> client_addr.sin_addr.s_addr=htonl(INADDR_ANY); <br /> n=1; <br /> /* 如果服务器终止后,服务器可以第二次快速启动而不用等待一D|? */ <br /> setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(int)); <br /> if(bind(listen_fd,(struct sockaddr *)&client_addr,sizeof(client_addr))<0) <br /> { <br /> printf("Bind Error:%s\n\a",strerror(errno)); <br /> exit(1); <br /> } <br /> listen(listen_fd,5); <br /> while(1) <br /> { <br /> accept_fd=accept(listen_fd,NULL,NULL); <br /> if((accept_fd<0)&&(errno==EINTR)) <br /> continue; <br /> else if(accept_fd<0) <br /> { <br /> printf("Accept Error:%s\n\a",strerror(errno)); <br /> continue; <br /> } <br /> if((n=fork())==0) <br /> { <br /> /* 子进E处理客L(fng)的连?*/ <br /> char buffer[1024]; <br /><br /> close(listen_fd); <br /> n=read(accept_fd,buffer,1024); <br /> write(accept_fd,buffer,n); <br /> close(accept_fd); <br /> exit(0); <br /> } <br /> else if(n<0) <br /> printf("Fork Error:%s\n\a",strerror(errno)); <br /> close(accept_fd); <br /> } <br />} <br /><br />你可以用我们前面写客L(fng)E序来调试着E序,或者是用来telnet调试 <img src ="http://www.shnenglu.com/lmlf001/aggbug/6369.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/lmlf001/" target="_blank">芥之?/a> 2006-04-27 11:58 <a href="http://www.shnenglu.com/lmlf001/archive/2006/04/27/6369.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个简单聊天室的两U实?(fcntl ?select)(ZZQ?/title><link>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6368.html</link><dc:creator>芥之?/dc:creator><author>芥之?/author><pubDate>Thu, 27 Apr 2006 03:51:00 GMT</pubDate><guid>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6368.html</guid><wfw:comment>http://www.shnenglu.com/lmlf001/comments/6368.html</wfw:comment><comments>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6368.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/lmlf001/comments/commentRss/6368.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/lmlf001/services/trackbacks/6368.html</trackback:ping><description><![CDATA[ <div id="noagbxq" class="blogbody"> <h2 class="title">一个简单聊天室的两U实?(fcntl ?select)(?</h2> </div> <br /> <font size="2"> 在互联网相当普及(qing)的今天,在互联网上聊天对很多“网虫”来说已l是家常侉K了。聊天室E序可以说是|上最单的多点通信E序。聊天室的实现方法有很多Q但 都是利用所谓的“多用户I间”来对信息进行交换,h典型的多路I/O的架构。一个简单的聊天? 从程序员的观Ҏ(gu)看就是在多个I/O端点之间实现多对多的通信。其架构如图一所C。这L(fng)实现在用L(fng)眼里是聊天室内M一个h输入一D字W之?其他 用户都可以得到这一句话。这U“多用户I间”的架构在其他多炚w信E序中应用的非常q泛Q其核心?yu)是多\I/O通信。多路I/O通信又被UCؓ(f)I/O多\? 用(I/O MultiplexingQ一般被使用在以下的场合Q?<br /> 客户E序需要同时处理交互式的输入和同服务器之间的网l连接时需要处理I/O多\复用问题Q?<br /> 客户端需要同时对多个|络q接作出反应Q这U情况很见Q; <br /> TCP服务器需要同时处理处于监听状态和多个q接状态的socketQ?<br /> 服务器需要处理多个网l协议的socket; <br /> 服务器需要同时处理不同的|络服务和协议?<br /> 聊天室所需要面对的情况正是W一和第三两U情c(din)我们将通过在TCP/IP协议之上建立一个功能简单的聊天室让大家更加了解多\I/O以及(qing)它的实现Ҏ(gu)?<br /> 我们要讨论的聊天室功能非常简? 感兴的朋友可以其功能扩展, 发展成一个功能比较完整的聊天? 如加上用戯? 用户늧, U密信息, semote {功? <br /> 首先它是一?client/server l构的程? 首先启动 server, 然后用户使用 client q行q接. client/server l构的优Ҏ(gu)速度? ~点是当 server q行更新? client 也必需更新. <br />   <br /> |络初始?<br />   <br /> 首先是初始化 server, 使server q入监听状? (Zzv?以下引用的程序与实际E序略有出入, 下同) <br /> sockfd = socket( AF_INET,SOCK_STREAM, 0); <br /> // 首先建立一?socket, 族ؓ(f) AF_INET, cd?SOCK_STREAM. <br /> // AF_INET = ARPA Internet protocols 即?TCP/IP 协议?<br /> // SOCK_STREAM cd提供了顺序的, 可靠? Z字节的全双工连? <br /> // ׃该协议族中只有一个协? 因此W三个参Cؓ(f) 0 <br />   <br /> bind( sockfd, ( struct sockaddr *)&serv_addr, sizeof( serv_addr)); <br /> // 再将q个 socket 与某个地址q行l定. <br /> // serv_addr 包括 sin_family = AF_INET 协议族同 socket <br /> // sin_addr.s_addr = htonl( INADDR_ANY) server 所接受的所有其?<br /> // 地址h建立的连? <br /> // sin_port = htons( SERV_TCP_PORT) server 所监听的端?<br /> // 在本E序? server ?IP和监听的端口都存攑֜ config 文g? <br /> listen( sockfd, MAX_CLIENT); <br /> // 地址l定之后, server q入监听状? <br /> // MAX_CLIENT 是可以同时徏立连接的 client L. <br /> server q入 listen 状态后, {待 client 建立q接?<br /> Client端要建立q接首先也需要初始化q接Q?<br /> sockfd = socket( AF_INET,SOCK_STREAM,0)); <br /> // 同样? client 也先建立一?socket, 其参C server 相同. <br /> connect( sockfd, ( struct sockaddr *)&serv_addr, sizeof( serv_addr)); <br /> // client 使用 connect 建立一个连? <br /> // serv_addr 中的变量分别讄? <br /> // sin_family = AF_INET 协议族同 socket <br /> // sin_addr.s_addr = inet_addr( SERV_HOST_ADDR) 地址?server <br /> // 所在的计算机的地址. <br /> // sin_port = htons( SERV_TCP_PORT) 端口?server 监听的端? <br /> ?client 建立新连接的h被送到Server端时, server 使用 accept 来接受该q接: <br /> accept( sockfd, (struct sockaddr*)&cli_addr, &cli_len); <br /> // 在函数返回时, cli_addr 中保留的是该q接Ҏ(gu)的信?<br /> // 包括Ҏ(gu)?IP 地址和对方用的端口. <br /> // accept q回一个新的文件描q符. <br /> ?server q入 listen 状态之? ׃已有多个用户在线Q所以程序需要同时对q些用户q行操作Qƈ在它们之间实C息交换。这在实CUCؓ(f)I/O多\复用技术。多路复用一般有以下几种Ҏ(gu)Q?<br /> 非阻塞通信Ҏ(gu)Q将文g道通过fcntl()设ؓ(f)非阻塞通信方式Q每隔一端时间对他们实行一ơ轮询,以判断是否可以进行读写操作。这U方式的~点是费用太高,大部分资源浪费在轮询上?<br /> 子进E方法:(x)应用多个子进E,每一个对一个单工阻塞方式通信。所有子q程通过IPC和父q程q行通信。父q程掌管所有信息。这U方式的~点是实现复杂,而且׃IPC在各个操作系l^Cq不完全一_(d)?x)导致可UL性降低?<br /> 信号驱动QSIGIOQ的异步I/OҎ(gu)Q首先,异步I/O是基于信h制的Qƈ不可靠。其ơ单一的信号不以提供更多的信息来源。还是需要辅助以其他的手D,实现上有很高的难度?<br /> select ()Ҏ(gu)Q在BSD中提供了一U可以对多\I/Oq行d式查询的Ҏ(gu)——select()。它提供同时对多个I/O描述W进行阻塞式查询的方法,利用 它,我们可以很方便的实现多\复用。根据统一UNIX规范的协?POSIX也采用了q种Ҏ(gu)Q因此,我们可以在大多数操作pȝ中用selectҎ(gu)? <br /> 使用专门的I/O多\复用器:(x)在“UNIX? SYSTEM V Programmer's Guide: STREAMS”一书中详细的说明了构造和使用多\复用器的Ҏ(gu)。这里就不再详述了?<br />   <br /> 我们下面分别讨论多\I/O的两U实现方? <br /> 1. 非阻塞通信Ҏ(gu) <br />? 一个文件描q符指定的文件或讑֤, 有两U工作方? d与非d。所谓阻塞方式的意思是? 当试囑֯该文件描q符q行d? 如果当时没有东西可读,或者暂时不可写, E序p入等待状? 直到有东西可L者可写ؓ(f)止。而对于非d状? 如果没有东西可读, 或者不可写, d函数马上q回, 而不?x)等待。缺省情况下, 文g描述W处于阻塞状态。在实现聊天室时, server 需要轮查询与各client 建立? socket, 一旦可d该 socket 中的字符d来ƈ向所有其他client 发送。ƈ? server q要随时查看是否有新? client 试图建立q接,q样, 如果 server 在Q何一个地斚w塞了, 其他 client 发送的内容׃(x)受到影响,得不到服务器的及(qing)时响应。新 client 试图建立q接也会(x)受到影响。所以我们在q里不能使用~省的阻塞的文g工作方式Q而需要将文g的工作方式变成非d方式。在UNIX下,函数fcntl() 可以用来改变文gI/O操作的工作方式,函数描述如下Q?<br /> fcntl( sockfd, F_SETFL, O_NONBLOCK); <br /> // sockfd 是要改变状态的文g描述W? <br /> // F_SETFL 表明要改变文件描q符的状?<br /> // O_NONBLOCK 表示文件描q符变ؓ(f)非阻塞的. <br /> Z节省幅我们使用自然语言描述聊天?server : <br /> while ( 1) { <br /> if 有新q接 then 建立q记录该新连? <br /> for ( 所有的有效q接) <br /> begin <br /> if 该连接中有字W可?then <br /> begin <br /> d字符Ԍ <br /> for ( 所有其他的有效q接) <br /> begin <br /> 该字符串发送给该连? <br /> end; <br /> end; <br /> end; <br /> end. <br /> ׃判断是否有新q接, 是否可读都是非阻塞的, 因此每次判断,不管有还是没? 都会(x)马上q回. q样,M一?client ?server 发送字W或者试囑־立新q接, 都不?x)对其?client 的活动造成影响?<br /> ?client 而言, 建立q接之后, 只需要处理两个文件描q符, 一个是建立了连接的 socket 描述W? 另一个是标准输入. ? server 一? 如果使用d方式的话, 很容易因为其中一个暂时没有输入而媄(jing)响另外一个的d.. 因此它们都变成非阻塞的, 然后client q行如下动作: <br /> while ( 不想退? <br /> begin <br /> if ( ?server 的连接有字符可读) <br /> begin <br /> 从该q接d, q输出到标准输出上去. <br /> End; <br /> if ( 标准输入可读) <br /> Begin <br /> 从标准输入读? q输出到?server 的连接中? <br /> End; <br /> End. <br /> 上面的读写分别调用这样两个函? <br /> read( userfd[i], line, MAX_LINE); <br /> // userfd[i] 是指W?i ?client q接的文件描q符. <br /> // line 是指d的字W存攄位置. <br /> // MAX_LINE 是一ơ最多读出的字符? <br /> // q回值是实际d的字W数. <br /> write( userfd[j], line, strlen( line)); <br /> // userfd[j] 是第 j ?client 的文件描q符. <br /> // line 是要发送的字符? <br /> // strlen( line) 是要发送的字符串长? <br /> 分析上面的程序可以知? 不管?server q是 client, 它们都不停的轮流查询各个文g描述W? 一旦可ddq进行处? q样的程? 不停的在执行, 只要有CPU 资源, ׃?x)放q。因此对pȝ资源的消耗非常大。server 或?client 单独执行? CPU 资源?98% 左右都被其占用。极大的消耗了pȝ资源?<br /> select Ҏ(gu) <br /> 因此Q虽然我们不希望在某一个用h有反应时d其他的用P但我们却应该在没有Q何用h反应的情况之下停止程序的q行Q让出抢占的pȝ资源Q进入阻塞状态。有没有q种Ҏ(gu)呢?现在的UNIXpȝ中都提供了selectҎ(gu)Q具体实现方式如下:(x) <br /> select Ҏ(gu)? 所有文件描q符都是d? 使用 select 判断一l文件描q符中是否有一个可??, 如果没有阻? 直到有一个的时候就被唤? 我们先看比较单的 client 的实? <br /> ׃ client 只需要处理两个文件描q符, 因此, 需要判断是否有可读写的文g描述W只需要加入两? <br /> FD_ZERO( sockset); <br /> // ?sockset 清空 <br /> FD_SET( sockfd, sockset); <br /> // ?sockfd 加入?sockset 集合?<br /> FD_SET( 0, sockset); <br /> // ?0 (标准输入) 加入?sockset 集合?<br /> 然后 client 的处理如? <br /> while ( 不想退? { <br /> select( sockfd+1, &sockset, NULL, NULL, NULL); <br /> // 此时该函数将d直到标准输入或?sockfd 中有一个可Mؓ(f)?<br /> // W一个参数是 0 ?sockfd 中的最大值加一 <br /> // W二个参数是 读集, 也就?sockset <br /> // W三, 四个参数是写集和异常? 在本E序中都为空 <br /> // W五个参数是时旉, 卛_指定旉内仍没有可读, 则出?<br /> // q返? 当这个参Cؓ(f)NULL ? 时旉被设|ؓ(f)无限? <br /> // ?select 因ؓ(f)可读q回? sockset 中包含的只是可读?<br /> // 那些文g描述W? <br /> if ( FD_ISSET( sockfd, &sockset)) { <br /> // FD_ISSET q个宏判?sockfd 是否属于可读的文件描q符 <br /> ?sockfd 中读? 输出到标准输Z? <br /> } <br /> if ( FD_ISSET( 0, &sockset)) { <br /> // FD_ISSET q个宏判?sockfd 是否属于可读的文件描q符 <br /> 从标准输入读? 输出?sockfd 中去. <br /> } <br /> 重新讄 sockset. (卛_ sockset 清空, q将 sockfd ?0 加入) <br /> } <br /> 下面?server 的情? <br /> 讄 sockset 如下: <br /> FD_ZERO( sockset); <br /> FD_SET( sockfd, sockset); <br /> for ( 所有有效连? <br /> FD_SET( userfd[i], sockset); <br /> } <br /> maxfd = 最大的文g描述W号 + 1; <br /> server 处理如下: <br /> while ( 1) { <br /> select( maxfd, &sockset, NULL, NULL, NULL); <br /> if ( FD_ISSET( sockfd, &sockset)) { <br /> // 有新q接 <br /> 建立新连? q将该连接描q符加入?sockset 中去? <br /> } <br /> for ( 所有有效连? { <br /> if ( FD_ISSET ( userfd[i], &sockset)) { <br /> // 该连接中有字W可?<br /> 从该q接中读入字W? q发送到其他有效q接中去. <br /> } <br /> } <br /> 重新讄 sockset; <br /> } <br /> 性能比较 <br /> ׃采用 select 机制, 因此当没有字W可L, E序处于d状?最程度的占用CPU 资源, 在同一台机器上执行一?server 和若q个client ? pȝ负蝲只有 0.1 左右, 而采用原来的非阻塞通信Ҏ(gu), 只运行一?server, pȝ负蝲可以达?1.5 左右. 因此我们推荐使用 select. </font> <img src ="http://www.shnenglu.com/lmlf001/aggbug/6368.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/lmlf001/" target="_blank">芥之?/a> 2006-04-27 11:51 <a href="http://www.shnenglu.com/lmlf001/archive/2006/04/27/6368.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux环境下的Socket~程QZZQ?/title><link>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6367.html</link><dc:creator>芥之?/dc:creator><author>芥之?/author><pubDate>Thu, 27 Apr 2006 03:46:00 GMT</pubDate><guid>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6367.html</guid><wfw:comment>http://www.shnenglu.com/lmlf001/comments/6367.html</wfw:comment><comments>http://www.shnenglu.com/lmlf001/archive/2006/04/27/6367.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/lmlf001/comments/commentRss/6367.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/lmlf001/services/trackbacks/6367.html</trackback:ping><description><![CDATA[ <span id="unrbldg" class="title">Linux环境下的Socket~程</span> <br /> <br /> <br /> <!----> <br /> <table style="table-layout: fixed;" align="center" bgcolor="#ffffff" cellpadding="5" cellspacing="0"> <tbody> <tr> <td valign="top"> <span id="ikiucyj" class="pg" id="xydwtext"> 什么是Socket <br />  Socket接口是TCP/IP|络的APIQSocket接口定义了许多函数或例程Q程序员可以用它们来开发TCP/IP|络上的应用E序。要学Internet上的TCP/IP|络~程Q必ȝ解Socket接口?<br />   Socket接口设计者最先是接口放在Unix操作pȝ里面的。如果了解Unixpȝ的输入和输出的话Q就很容易了解Socket了。网l的 Socket数据传输是一U特D的I/OQSocket也是一U文件描q符。Socket也具有一个类g打开文g的函数调用Socket()Q该函数q? 回一个整型的Socket描述W,随后的连接徏立、数据传输等操作都是通过该Socket实现的。常用的Socketcd有两U:(x)式Socket QSOCK_STREAMQ和数据报式SocketQSOCK_DGRAMQ。流式是一U面向连接的SocketQ针对于面向q接的TCP服务应用Q数? 报式Socket是一U无q接的SocketQ对应于无连接的UDP服务应用?<br /><br />Socket建立 <br />  Z建立SocketQ程序可以调用Socket函数Q该函数q回一个类g文g描述W的句柄。socket函数原型为:(x) <br />  int socket(int domain, int type, int protocol); <br />   domain指明所使用的协议族Q通常为PF_INETQ表CZ联网协议族(TCP/IP协议族)Qtype参数指定socket的类型:(x) SOCK_STREAM 或SOCK_DGRAMQSocket接口q定义了原始SocketQSOCK_RAWQ,允许E序使用低层协议Qprotocol通常赋?0"? Socket()调用q回一个整型socket描述W,你可以在后面的调用用它?<br />  Socket描述W是一个指向内部数据结构的指针Q它指向描述W表入口。调用Socket函数Ӟsocket执行体将建立一个SocketQ实际上"建立一个Socket"意味着Z个Socket数据l构分配存储I间。Socket执行体ؓ(f)你管理描q符表?<br />  两个|络E序之间的一个网l连接包括五U信息:(x)通信协议、本地协议地址、本C机端口、远端主机地址和远端协议端口。Socket数据l构中包含这五种信息?<br /><br />Socket配置 <br />   通过socket调用q回一个socket描述W后Q在使用socketq行|络传输以前Q必配|该socket。面向连接的socket客户端通过 调用Connect函数在socket数据l构中保存本地和q端信息。无q接socket的客L(fng)和服务端以及(qing)面向q接socket的服务端通过调用 bind函数来配|本C息?<br />Bind函数socket与本Z的一个端口相兌Q随后你可以在该端口监听服务请求。Bind函数原型为:(x) <br />  int bind(int sockfd,struct sockaddr *my_addr, int addrlen); <br />  Sockfd是调用socket函数q回的socket描述W?my_addr是一个指向包含有本机IP地址?qing)端口号{信息的sockaddrcd的指针;addrlen常被讄为sizeof(struct sockaddr)?<br />  struct sockaddrl构cd是用来保存socket信息的:(x) <br />  struct sockaddr { <br />   unsigned short sa_family; /* 地址族, AF_xxx */ <br />char sa_data[14]; /* 14 字节的协议地址 */ <br />}; <br />  sa_family一般ؓ(f)AF_INETQ代表InternetQTCP/IPQ地址族;sa_data则包含该socket的IP地址和端口号?<br />  另外q有一U结构类型:(x) <br />  struct sockaddr_in { <br />   short int sin_family; /* 地址?*/ <br />   unsigned short int sin_port; /* 端口?*/ <br />   struct in_addr sin_addr; /* IP地址 */ <br />   unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */ <br />  }; <br />   q个l构更方便用。sin_zero用来sockaddr_inl构填充Cstruct sockaddr同样的长度,可以用bzero()或memset()函数其|ؓ(f)零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互{换,q意味着如果一个函数所需参数cd是sockaddrӞ你可以在函数调用的时候将一个指? sockaddr_in的指针{换ؓ(f)指向sockaddr的指针;或者相反?<br />  使用bind函数Ӟ可以用下面的赋值实现自动获得本机IP地址和随取一个没有被占用的端口号Q?<br />  my_addr.sin_port = 0; /* pȝ随机选择一个未被用的端口?*/ <br />  my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */ <br />通过my_addr.sin_port|ؓ(f)0Q函C(x)自动Z选择一个未占用的端口来使用。同P通过my_addr.sin_addr.s_addr|ؓ(f)INADDR_ANYQ系l会(x)自动填入本机IP地址?<br />注意在用bind函数是需要将sin_port和sin_addr转换成ؓ(f)|络字节优先序Q而sin_addr则不需要{换?<br />  计算机数据存储有两种字节优先序Q高位字节优先和低位字节优先。Internet上数据以高位字节优先序在网l上传输Q所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时需要进行{换,否则׃(x)出现数据不一致?<br />  下面是几个字节顺序{换函敎ͼ(x) <br />·htonl()Q把32位gL字节序{换成|络字节?<br />·htons()Q把16位gL字节序{换成|络字节?<br />·ntohl()Q把32位g|络字节序{换成L字节?<br />·ntohs()Q把16位g|络字节序{换成L字节?<br />  Bind()函数在成功被调用时返?Q出现错误时q回"-1"q将errno|ؓ(f)相应的错误号。需要注意的是,在调用bind函数时一般不要将端口L(fng)为小?024的|因ؓ(f)1?024是保留端口号Q你可以选择大于1024中的M一个没有被占用的端口号?<br /><br />q接建立 <br />  面向q接的客L(fng)序用Connect函数来配|socketq与q端服务器徏立一个TCPq接Q其函数原型为:(x) <br />  int connect(int sockfd, struct sockaddr *serv_addr,int addrlen); <br />Sockfd 是socket函数q回的socket描述W;serv_addr是包含远端主机IP地址和端口号的指针;addrlen是远端地质结构的长度? Connect函数在出现错误时q回-1Qƈ且设|errno为相应的错误码。进行客L(fng)E序设计无须调用bind()Q因U情况下只需知道目的机器 的IP地址Q而客户通过哪个端口与服务器建立q接q不需要关心,socket执行体ؓ(f)你的E序自动选择一个未被占用的端口Qƈ通知你的E序数据什么时候到 打断口?<br />  Connect函数启动和远端主机的直接q接。只有面向连接的客户E序使用socket时才需要将此socket与远端主机相q。无q接协议从不建立直接q接。面向连接的服务器也从不启动一个连接,它只是被动的在协议端口监听客L(fng)h?<br />  Listen函数使socket处于被动的监听模式,qؓ(f)该socket建立一个输入数据队列,到辄服务h保存在此队列中,直到E序处理它们?<br />  int listen(int sockfdQ?int backlog); <br />Sockfd 是Socketpȝ调用q回的socket 描述W;backlog指定在请求队列中允许的最大请求数Q进入的q接h在队列中等待accept()它们Q参考下文)。Backlog寚w列中{待 服务的请求的数目q行了限Ӟ大多数系l缺省gؓ(f)20。如果一个服务请求到来时Q输入队列已满,该socket拒l连接请求,客户收C个出错信息? <br />当出现错误时listen函数q回-1Qƈ|相应的errno错误码?<br />  accept()函数让服务器接收客户的连接请求。在建立好输入队列后Q服务器p用accept函数Q然后睡眠ƈ{待客户的连接请求?<br />  int accept(int sockfd, void *addr, int *addrlen); <br />   sockfd是被监听的socket描述W,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提接请求服务的L的信? Q某CZ某个端口发出该请求)Qaddrten通常Z个指向gؓ(f)sizeof(struct sockaddr_in)的整型指针变量。出现错误时accept函数q回-1q置相应的errno倹{?<br />  首先Q当accept函数监视? socket收到q接hӞsocket执行体将建立一个新的socketQ执行体这个新socket和请求连接进E的地址联系hQ收到服务请求的 初始socket仍可以l在以前?socket上监听,同时可以在新的socket描述W上q行数据传输操作?<br /><br />数据传输 <br />  Send()和recv()q两个函数用于面向连接的socket上进行数据传输?<br />  Send()函数原型为:(x) <br />  int send(int sockfd, const void *msg, int len, int flags); <br />Sockfd是你想用来传输数据的socket描述W;msg是一个指向要发送数据的指针QLen是以字节为单位的数据的长度;flags一般情况下|ؓ(f)0Q关于该参数的用法可参照man手册Q?<br />  Send()函数q回实际上发送出的字节数Q可能会(x)于你希望发送的数据。在E序中应该将send()的返回gƲ发送的字节数进行比较。当send()q回glen不匹配时Q应该对q种情况q行处理?<br />char *msg = "Hello!"; <br />int len, bytes_sent; <br />…?<br />len = strlen(msg); <br />bytes_sent = send(sockfd, msg,len,0); <br />…?<br />  recv()函数原型为:(x) <br />  int recv(int sockfd,void *buf,int len,unsigned int flags); <br />  Sockfd是接受数据的socket描述W;buf 是存放接收数据的~冲区;len是缓冲的长度。Flags也被|ؓ(f)0。Recv()q回实际上接收的字节敎ͼ当出现错误时Q返?1q置相应的errno倹{?<br />Sendto()和recvfrom()用于在无q接的数据报socket方式下进行数据传输。由于本地socketq没有与q端机器建立q接Q所以在发送数据时应指明目的地址?<br />sendto()函数原型为:(x) <br />  int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen); <br />  该函数比send()函数多了两个参数Qto表示目地机的IP地址和端口号信息Q而tolen常常被赋gؓ(f)sizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时q回-1?<br />  Recvfrom()函数原型为:(x) <br />  int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen); <br />   from是一个struct sockaddrcd的变量,该变量保存源机的IP地址?qing)端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()q回Ӟfromlen包含实际存入from中的数据字节数。Recvfrom()函数q回接收到的字节数或 当出现错误时q回-1Qƈ|相应的errno?<br />如果你对数据报socket调用了connect()函数Ӟ你也可以利用send()和recv()q行数据传输Q但该socket仍然是数据报socketQƈ且利用传输层的UDP服务。但在发送或接收数据报时Q内怼(x)自动Z加上目地和源地址信息?<br /><br />l束传输 <br />  当所有的数据操作l束以后Q你可以调用close()函数来释放该socketQ从而停止在该socket上的M数据操作Q?<br />close(sockfd); <br />  你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输l进行。如你可以关闭某socket的写操作而允许l在该socket上接受数据,直至d所有数据?<br />  int shutdown(int sockfd,int how); <br />  Sockfd是需要关闭的socket的描q符。参?how允许为shutdown操作选择以下几种方式Q?<br />  ·0-------不允许l接收数?<br />  ·1-------不允许l发送数?<br />·2-------不允许l发送和接收数据Q?<br />·均ؓ(f)允许则调用close () <br />  shutdown在操作成功时q回0Q在出现错误时返?1q置相应errno?<br /><br />面向q接的Socket实例 <br />  代码实例中的服务器通过socketq接向客L(fng)发送字W串"Hello, you are connected!"。只要在服务器上q行该服务器软gQ在客户端运行客戯YӞ客户端就?x)收到该字符丌Ӏ?<br />  该服务器软g代码如下Q?<br />#include <stdio.h> <br />#include <stdlib.h> <br />#include <errno.h> <br />#include <string.h> <br />#include <sys/types.h> <br />#include <netinet/in.h> <br />#include <sys/socket.h> <br />#include <sys/wait.h> <br />#define SERVPORT 3333 /*服务器监听端口号 */ <br />#define BACKLOG 10 /* 最大同时连接请求数 */ <br />main() <br />{ <br />int sockfd,client_fd; /*sock_fdQ监听socketQclient_fdQ数据传输socket */ <br /> struct sockaddr_in my_addr; /* 本机地址信息 */ <br /> struct sockaddr_in remote_addr; /* 客户端地址信息 */ <br />if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { <br />  perror("socket创徏出错Q?); exit(1); <br />} <br />my_addr.sin_family=AF_INET; <br /> my_addr.sin_port=htons(SERVPORT); <br /> my_addr.sin_addr.s_addr = INADDR_ANY; <br />bzero(&(my_addr.sin_zero),8); <br /> if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \ <br />   == -1) { <br />perror("bind出错Q?); <br />exit(1); <br />} <br /> if (listen(sockfd, BACKLOG) == -1) { <br />perror("listen出错Q?); <br />exit(1); <br />} <br />while(1) { <br />  sin_size = sizeof(struct sockaddr_in); <br />  if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, \ <br />  &sin_size)) == -1) { <br />perror("accept出错"); <br />continue; <br />} <br />  printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr)); <br />  if (!fork()) { /* 子进E代码段 */ <br />   if (send(client_fd, "Hello, you are connected!\n", 26, 0) == -1) <br />   perror("send出错Q?); <br />close(client_fd); <br />exit(0); <br />} <br />  close(client_fd); <br />  } <br /> } <br />} <br />   服务器的工作程是这L(fng)Q首先调用socket函数创徏一个SocketQ然后调用bind函数其与本机地址以及(qing)一个本地端口号l定Q然后调? listen在相应的socket上监听,当accpet接收C个连接服务请求时Q将生成一个新的socket。服务器昄该客h的IP地址Qƈ通过 新的socket向客L(fng)发送字W串"HelloQyou are connected!"。最后关闭该socket?<br />  代码实例中的fork()函数生成一个子q程来处理数据传输部分,fork()语句对于子进E返回的gؓ(f)0。所以包含fork函数的if语句是子q程代码部分Q它与if语句后面的父q程代码部分是ƈ发执行的?<br /><br />客户端程序代码如下:(x) <br />#include<stdio.h> <br />#include <stdlib.h> <br />#include <errno.h> <br />#include <string.h> <br />#include <netdb.h> <br />#include <sys/types.h> <br />#include <netinet/in.h> <br />#include <sys/socket.h> <br />#define SERVPORT 3333 <br />#define MAXDATASIZE 100 /*每次最大数据传输量 */ <br />main(int argc, char *argv[]){ <br /> int sockfd, recvbytes; <br /> char buf[MAXDATASIZE]; <br /> struct hostent *host; <br /> struct sockaddr_in serv_addr; <br /> if (argc < 2) { <br />fprintf(stderr,"Please enter the server's hostname!\n"); <br />exit(1); <br />} <br /> if((host=gethostbyname(argv[1]))==NULL) { <br />herror("gethostbyname出错Q?); <br />exit(1); <br />} <br /> if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ <br />perror("socket创徏出错Q?); <br />exit(1); <br />} <br /> serv_addr.sin_family=AF_INET; <br /> serv_addr.sin_port=htons(SERVPORT); <br /> serv_addr.sin_addr = *((struct in_addr *)host->h_addr); <br /> bzero(&(serv_addr.sin_zero),8); <br /> if (connect(sockfd, (struct sockaddr *)&serv_addr, \ <br />   sizeof(struct sockaddr)) == -1) { <br />perror("connect出错Q?); <br />exit(1); <br />} <br /> if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1) { <br />perror("recv出错Q?); <br />exit(1); <br />} <br /> buf[recvbytes] = '\0'; <br /> printf("Received: %s",buf); <br /> close(sockfd); <br />} <br />  客户端程序首先通过服务器域名获得服务器的IP地址Q然后创Z个socketQ调用connect函数与服务器建立q接Q连接成功之后接收从服务器发送过来的数据Q最后关闭socket?<br />  函数gethostbyname()是完成域名{换的。由于IP地址难以记忆和读写,所以ؓ(f)了方便,Z常常用域名来表示LQ这需要进行域名和IP地址的{换。函数原型ؓ(f)Q?<br />  struct hostent *gethostbyname(const char *name); <br />  函数q回为hosten的结构类型,它的定义如下Q?<br />  struct hostent { <br />  char *h_name; /* L的官方域?*/ <br />   char **h_aliases; /* 一个以NULLl尾的主机别名数l?*/ <br />   int h_addrtype; /* q回的地址cdQ在Internet环境下ؓ(f)AF-INET */ <br />  int h_length; /* 地址的字节长?*/ <br />   char **h_addr_list; /* 一个以0l尾的数l,包含该主机的所有地址*/ <br />  }; <br />  #define h_addr h_addr_list[0] /*在h-addr-list中的W一个地址*/ <br />  ?gethostname()调用成功Ӟq回指向struct hosten的指针,当调用失败时q回-1。当调用gethostbynameӞ你不能用perror()函数来输出错误信息,而应该用herror()函数来输出?<br /><br />  无连接的客户/服务器程序的在原理上和连接的客户/服务器是一L(fng)Q两者的区别在于无连接的客户/服务器中的客户一般不需要徏立连接,而且在发送接收数据时Q需要指定远端机的地址?<br /><br />d和非d <br />   d函数在完成其指定的Q务以前不允许E序调用另一个函数。例如,E序执行一个读数据的函数调用时Q在此函数完成读操作以前不?x)执行下一E序语句。当 服务器运行到accept语句Ӟ而没有客戯接服务请求到来,服务器就?x)停止在accept语句上等待连接服务请求的到来。这U情늧为阻? QblockingQ。而非d操作则可以立卛_成。比如,如果你希望服务器仅仅注意(g)查是否有客户在等待连接,有就接受q接Q否则就l箋做其他事情,? 可以通过Socket讄为非d方式来实现。非dsocket在没有客户在{待时就使accept调用立即q回?<br />  #include <unistd.h> <br />  #include <fcntl.h> <br />  …?<br />sockfd = socket(AF_INET,SOCK_STREAM,0); <br />fcntl(sockfd,F_SETFL,O_NONBLOCK)Q?<br />…?<br />   通过讄socket为非d方式Q可以实?轮询"若干Socket。当企图从一个没有数据等待处理的非阻塞Socketd数据Ӟ函数立卌 回,q回gؓ(f)-1Qƈ|errnogؓ(f)EWOULDBLOCK。但是这U?轮询"?x)CPU处于忙等待方式,从而降低性能Q浪费系l资源。而调? select()?x)有效地解决q个问题Q它允许你把q程本n挂v来,而同时ɾpȝ内核监听所要求的一l文件描q符的Q何活动,只要认在Q何被监控的文? 描述W上出现zdQselect()调用返回指C文g描述W已准备好的信息Q从而实CE选出随机的变化,而不必由q程本n对输入进行测试而浪? CPU开销。Select函数原型? <br />int select(int numfds,fd_set *readfds,fd_set *writefdsQ?<br />fd_set *exceptfds,struct timeval *timeout); <br />   其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文g描述W集合。如果你希望定是否可以 从标准输入和某个socket描述W读取数据,你只需要将标准输入的文件描q符0和相应的sockdtfd加入到readfds集合中;numfds的? 是需要检查的L(fng)最高的文g描述W加1Q这个例子中numfds的值应为sockfd+1Q当selectq回Ӟreadfds被修改Q指C某个文? 描述W已l准备被dQ你可以通过FD_ISSSET()来测试。ؓ(f)了实现fd_set中对应的文g描述W的讄、复位和试Q它提供了一l宏Q?<br />  FD_ZERO(fd_set *set)----清除一个文件描q符集; <br />  FD_SET(int fd,fd_set *set)----一个文件描q符加入文g描述W集中; <br />  FD_CLR(int fd,fd_set *set)----一个文件描q符从文件描q符集中清除Q?<br />  FD_ISSET(int fd,fd_set *set)----试判断是否文件描q符被置位?<br />  Timeout参数是一个指向struct timevalcd的指针,它可以select()在等待timeout长时间后没有文g描述W准备好卌回。struct timeval数据l构为:(x) <br />  struct timeval { <br />   int tv_sec; /* seconds */ <br />   int tv_usec; /* microseconds */ <br />}; <br /><br />POP3客户端实?<br />  下面的代码实例基于POP3的客户协议,与邮件服务器q接q取回指定用户帐L(fng)邮g。与邮g服务器交互的命o(h)存储在字W串数组POPMessage中,E序通过一个do-while循环依次发送这些命令?<br />#include<stdio.h> <br />#include <stdlib.h> <br />#include <errno.h> <br />#include <string.h> <br />#include <netdb.h> <br />#include <sys/types.h> <br />#include <netinet/in.h> <br />#include <sys/socket.h> <br />#define POP3SERVPORT 110 <br />#define MAXDATASIZE 4096 <br /><br />main(int argc, char *argv[]){ <br />int sockfd; <br />struct hostent *host; <br />struct sockaddr_in serv_addr; <br />char *POPMessage[]={ <br />"USER userid\r\n", <br />"PASS password\r\n", <br />"STAT\r\n", <br />"LIST\r\n", <br />"RETR 1\r\n", <br />"DELE 1\r\n", <br />"QUIT\r\n", <br />NULL <br />}; <br />int iLength; <br />int iMsg=0; <br />int iEnd=0; <br />char buf[MAXDATASIZE]; <br /><br />if((host=gethostbyname("your.server"))==NULL) { <br />perror("gethostbyname error"); <br />exit(1); <br />} <br />if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ <br />perror("socket error"); <br />exit(1); <br />} <br />serv_addr.sin_family=AF_INET; <br />serv_addr.sin_port=htons(POP3SERVPORT); <br />serv_addr.sin_addr = *((struct in_addr *)host->h_addr); <br />bzero(&(serv_addr.sin_zero),8); <br />if (connect(sockfd, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){ <br />perror("connect error"); <br />exit(1); <br />} <br /><br />do { <br />send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0); <br />printf("have sent: %s",POPMessage[iMsg]); <br /><br />iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0); <br />iEnd+=iLength; <br />buf[iEnd]='\0'; <br />printf("received: %s,%d\n",buf,iMsg); <br /><br />iMsg++; <br />} while (POPMessage[iMsg]); <br /><br />close(sockfd); <br />}</span> </td> </tr> <!----> <tr> <td> <hr /> </td> </tr> </tbody> </table> <img src ="http://www.shnenglu.com/lmlf001/aggbug/6367.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/lmlf001/" target="_blank">芥之?/a> 2006-04-27 11:46 <a href="http://www.shnenglu.com/lmlf001/archive/2006/04/27/6367.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.pygsbdg.cn" target="_blank">Ʒþþþþ </a>| <a href="http://www.gdguangjie.cn" target="_blank">þþþ޾Ʒַ</a>| <a href="http://www.cnmncom.cn" target="_blank">þ߿߿</a>| <a href="http://www.zjhongfeng.cn" target="_blank">þ99Ʒþþþþ </a>| <a href="http://www.szac.org.cn" target="_blank">þ99һ</a>| <a href="http://www.xysrsks.cn" target="_blank">þùVһë</a>| <a href="http://www.rljps.cn" target="_blank">Ʒþþþþþþ</a>| <a href="http://www.cqxy168.cn" target="_blank">ƯޱгĻþ</a>| <a href="http://www.zg-ly.cn" target="_blank">þþһƷ99þþƷ66</a>| <a href="http://www.7cfw.cn" target="_blank">þѸƵ</a>| <a href="http://www.zqek.cn" target="_blank">2021ƷþþƷ</a>| <a href="http://www.dangqie.cn" target="_blank">Ʒþþþþþþ</a>| <a href="http://www.iwck.cn" target="_blank">ŷƷ˿þþĻ</a>| <a href="http://www.songli.org.cn" target="_blank">޹Ʒþþž</a>| <a href="http://www.sj-gd.cn" target="_blank">˾Ʒ׽þ69</a>| <a href="http://www.76288.com.cn" target="_blank">þþƷһպAV </a>| <a href="http://www.byzj.net.cn" target="_blank">þþ</a>| <a href="http://www.ppxp.com.cn" target="_blank">ݺɫþþۺϲ</a>| <a href="http://www.wuxicld.cn" target="_blank">պŷۺϾþӰԺDs </a>| <a href="http://www.ttkanshu.cn" target="_blank">þþƷԴվ</a>| <a href="http://www.0553fc.cn" target="_blank">ݺɫþþۺϲ</a>| <a href="http://www.818wg.cn" target="_blank">һþƵ</a>| <a href="http://www.awo6.cn" target="_blank">þþƷ</a>| <a href="http://www.4060262.cn " target="_blank">ھƷ˾þþӰԺ</a>| <a href="http://www.sqcn.com.cn" target="_blank">999þþѾƷ</a>| <a href="http://www.90key.cn" target="_blank">޾Ʒþþ</a>| <a href="http://www.tdpqb.cn" target="_blank">Ʒһþ</a>| <a href="http://www.shawcai.cn" target="_blank">ŷ޹Ʒþø</a>| <a href="http://www.sanbaotong.cn" target="_blank">þþݾþþ</a>| <a href="http://www.dygdgs.cn" target="_blank">ƷһþaaaƬ</a>| <a href="http://www.ww0w.cn" target="_blank">þ99Ʒ鶹</a>| <a href="http://www.py63.cn" target="_blank">þԭavapp</a>| <a href="http://www.sylucq.cn" target="_blank">þۺƵվ</a>| <a href="http://www.68gz.cn" target="_blank">þþƷ</a>| <a href="http://www.rhtyyls.cn" target="_blank">ɫվþþþۺywww</a>| <a href="http://www.baodecai.cn" target="_blank">պݺݾþ͵͵ɫۺ0</a>| <a href="http://www.kyxu.cn" target="_blank">˺ݺۺϾþ88</a>| <a href="http://www.shy114.cn" target="_blank">ٸƷþþһ</a>| <a href="http://www.hygame8888.cn" target="_blank">þ޹Ʒ</a>| <a href="http://www.paysearch.cn" target="_blank">þþƷ91þ鶹 </a>| <a href="http://www.176zfblp.cn" target="_blank">һþaþþƷۺ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>