关闭

关闭

关闭

封号提示

内容

首页 Linux Socket 编程

Linux Socket 编程.pdf

Linux Socket 编程

zhengsiry 2013-06-26 评分 0 浏览量 0 0 0 0 暂无简介 简介 举报

简介:本文档为《Linux Socket 编程pdf》,可适用于IT/计算机领域,主题内容包含LINUXTCPIP网络程序设计LINUXTCPIP网络程序设计信息科学与工程学院付冲IPTELNETFTPSMTPDNSHTTPSNMPPOPTC符等。

LINUXTCPIP网络程序设计LINUXTCPIP网络程序设计信息科学与工程学院付冲IPTELNETFTPSMTPDNSHTTPSNMPPOPTCPUDPICMPARPRARPARPANETSATNETPacketradioLANApplicationTransportInternetHosttonetworkTCPIP协议集第一章Linux平台网络配置Linux的网络功能连接网络()网卡()Modem:pppd()ADSL:pppoe客户端(client)功能telnet,ftp,mail,news,NetscapeCommunicator服务器端(server)功能http,ftp,telnet,dns,samba,smtp……设置网络功能接口系统通过接口(interface)连接网络每个接口拥有唯一的IP地址常见接口()本地回路:loopback每个Linux系统只有一个本地回路接口即使没有网卡也会有本地回路接口。其作用为测试本地网络功能数据由本地回路送出再送回本主机。本地回路接口名称为:lo固定IP地址为:()以太网卡接口eth,eth,……()PPP接口查看接口ifconfig各字段说明:Hwaddr:网卡地址(物理地址,硬件地址)inetaddr:IP地址Bcast:广播地址Mask:子网掩码MTU:MaxTransmissionUnix,最大传输单元即数据帧最大长度Metric:路由长度TXpackets:传送的数据包总数、错误数、遗失数及溢出数RXpackets:传送的数据包总数、错误数、遗失数及溢出数查看系统中所有的接口(包括未启动的接口):ifconfiga设置接口()启动接口语法:ifconfig接口IP地址broadcast广播地址netmask子网掩码例:ifconfigeth例:ifconfigethbroadcastnetmask注意:ifconfig命令设置的IP地址不能永久保存重新启动计算机后将丢失设置路由(Routing)查看路由表route各字段说明:Destination:目的IP(网络地址)Gateway:这条路径所使用的网关*表示没有Genmask:网络掩码Flag:U:启动H:这条路由的目的地为主机G:网关D:动态路由M:修改Metric:路由的长度Ref:其他的路由经过的数目Use:被使用的次数Iface:使用的网络接口设置路由设置默认网关语法:routeadddefaultgw网关地址例:routeadddefaultgw删除路由语法:routedeldestination例:routedeldefaultroutedel注意:route命令设置的路由信息不能永久保存重新启动计算机后将丢失设置netconfig设置网络参数usedynamicIPconfigurationIPaddressNetmaskPrimarynameserver注意:netconfig命令设置的网络配置信息可以永久保存到配置文件中但只有重新启动计算机后才能生效相关文件etcsysconfignetwork主机名设置etcsysconfignetworkscriptsifcfgethIP地址设置:IPADDR子网掩码设置:NETMASK默认网关设置:GATEWAYetcresolvconfDNS服务器设置etchosts存放一些常用主机的IP地址与主机名称的数据即一个简单的DNS数据库基本格式为:IP主机域名主机别名etcservices记录主机提供的网络服务项目、端口号(port)及使用的传输层协议查看网络状况ping检测是否可与对方主机连通ping目的IP地址CtrlC可终止第二章文本编辑工具Vi功能完备的编辑软件vi启动与模式介绍启动与结束启动:vi文件名文件名可选若不指定则可在编辑结束后存盘时指定退出:按Esc键进入命令模式输入:q回车命令模式()进入输入模式()输入编辑指令()在文件中移动光标()执行ex指令()执行一个Linuxshell输入模式()进入输入模式从命令模式输入命令:a,A,i,Ia:在光标的位置后添加输入的字符A:在光标所在的行尾添加输入的字符i:在光标的前一个位置插入输入的字符I:在光标所在行开头插入输入的字符()退出输入模式从输入模式下按Esc键可退出输入模式进入命令模式尾行(lastline)模式(状态栏指令)在命令模式下可输入,,:,!等开头的操作指令完成相应的功能()向前查找字符串()向后查找字符串():执行文件相关操作命令()!启动一个shell指令编辑操作输入移动光标位置注意:移动指令必须在命令模式下使用指令:h(左),j(下),k(上),l(右)注意:Unix系统不支持上,下,左,右四个方向键Linux系统为方便用户在vi中支持上述方向键删除文本指令:()x()dd复制与粘贴指令:()yy:复制光标所在当前行()yw:复制光标位置到单词结束的字符()p:在当前光标后粘贴()P:在当前光标前粘贴查找文本指令:字符串,,字符串,行号():setnu():setnonu():n()G其他编辑操作还原指令:u文件与其他操作():w():wfilename():w!():q():q!():wq():x等价于:wqLinux平台下CC程序的开发()利用vi编写源程序(扩展名为c)()利用gcc(Unix:cc)命令进行编译生成可执行程序()gcc编译方法:(a)gcc源程序名例如:gccprogc系统将生成名为aout的可执行文件(b)gcco可执行文件名源程序名例如:gccoprogexeprogc系统将生成名为progexe的可执行文件Linux平台下Java程序的开发()下载javaforLinux平台开发包例如:jsdklinuxibin()将其上传或拷贝至usrlocal目录下将其属性改为可执行()在usrlocal目录下执行jsdklinuxibin将其解压缩生成包含java开发工具的目录jsdk()为方便起见建立名为java的符号链接ln–sjsdkjava()修改etcprofile文件添加如下内容PATH=$PATH:usrlocaljavabinexportCLASSPATH=usrlocaljavalib:(javac和java命令在usrlocaljavabin目录下)()执行sourceetcprofile使新增加的变量生效并执行javac和java命令进行简单的测试()利用vi编写源程序(扩展名为java)()利用javac命令进行编译生成class文件并用java命令进行执行第三章UnixLinux平台Socket程序设计问题的引入UNIXLinux系统的IO命令集是从Maltics和早期系统中的命令演变出来的其模式为打开读写关闭(openwritereadclose)。在一个用户进程进行IO操作时它首先调用“打开”获得对指定文件或设备的使用权并返回称为文件描述符的整型数以描述用户在打开的文件或设备上进行IO操作的进程。然后这个用户进程多次调用“读写”以传输数据。当所有的传输操作完成后用户进程关闭调用通知操作系统已经完成了对某对象的使用。TCPIP协议被集成到UNIXLinux内核中时相当于在UNIXLinux系统引入了一种新型的IO操作。UNIXLinux用户进程与网络协议的交互作用比用户进程与传统的IO设备相互作用复杂得多。首先进行网络操作的两个进程在不同机器上如何建立它们之间的联系?其次网络协议存在多种如何建立一种通用机制以支持多种协议?这些都是网络应用编程界面所要解决的问题。在UNIX系统中网络应用编程界面有两类:UNIXBSD的套接字(socket)和UNIXSystemV的TLI。由于Sun公司采用了支持TCPIP的UNIXBSD操作系统使TCPIP的应用有更大的发展其网络应用编程界面套接字(socket)在网络软件中被广泛应用至今已引进微机操作系统DOS和Windows系统中成为开发网络应用软件的强有力工具。套接字编程基本概念在开始使用套接字编程之前首先必须建立以下概念。网间进程通信进程通信的概念最初来源于单机系统。由于每个进程都在自己的地址范围内运行为保证两个相互通信的进程之间既互不干扰又协调一致工作操作系统为进程通信提供了相应设施如UNIXBSD中的管道(pipe)、命名管道(namedpipe)和软中断信号(signal)UNIXsystemV的消息(message)、共享存储区(sharedmemory)和信号量(semaphore)等但都仅限于用在本机进程之间通信。网间进程通信要解决的是不同主机进程间的相互通信问题(可把同机进程通信看作是其中的特例)。为此首先要解决的是网间进程标识问题。同一主机上不同进程可用进程号(processID)唯一标识。但在网络环境下各主机独立分配的进程号不能唯一标识该进程。例如主机A赋于某进程号在B机中也可以存在号进程因此“号进程”这句话就没有意义了。其次操作系统支持的网络协议众多不同协议的工作方式不同地址格式也不同。因此网间进程通信还要解决多重协议的识别问题。为了解决上述问题TCPIP协议引入了下列几个概念。()端口网络中可以被命名和寻址的通信端口是操作系统可分配的一种资源。按照OSI七层协议的描述传输层与网络层在功能上的最大区别是传输层提供进程通信能力。从这个意义上讲网络通信的最终地址就不仅仅是主机地址了还包括可以描述进程的某种标识符。为此TCPIP协议提出了协议端口(protocolport简称端口)的概念用于标识通信的进程。端口是一种抽象的软件结构(包括一些数据结构和IO缓冲区)。应用程序(即进程)通过系统调用与某端口建立连接(binding)后传输层传给该端口的数据都被相应进程所接收相应进程发给传输层的数据都通过该端口输出。在TCPIP协议的实现中端口操作类似于一般的IO操作进程获取一个端口相当于获取本地唯一的IO文件可以用一般的读写原语访问之。类似于文件描述符每个端口都拥有一个叫端口号(portnumber)的整数型标识符用于区别不同端口。由于TCPIP传输层的两个协议TCP和UDP是完全独立的两个软件模块因此各自的端口号也相互独立如TCP有一个号端口UDP也可以有一个号端口二者并不冲突。端口号的分配是一个重要问题。有两种基本分配方式:第一种叫全局分配这是一种集中控制方式由一个公认的中央机构根据用户需要进行统一分配并将结果公布于众。第二种是本地分配又称动态连接即进程需要访问传输层服务时向本地操作系统提出申请操作系统返回一个本地唯一的端口号进程再通过合适的系统调用将自己与该端口号联系起来(绑扎)。TCPIP端口号的分配中综合了上述两种方式。TCPIP将端口号分为两部分少量的作为保留端口以全局方式分配给服务进程。因此每一个标准服务器都拥有一个全局公认的端口(即周知口wellknownport)即使在不同机器上其端口号也相同。剩余的为自由端口以本地方式进行分配。TCP和UDP均规定小于的端口号才能作保留端口。()地址网络通信中通信的两个进程分别在不同的机器上。在互连网络中两台机器可能位于不同的网络这些网络通过网络互连设备(网关网桥路由器等)连接。因此需要三级寻址:某一主机可与多个网络相连必须指定一特定网络地址网络上每一台主机应有其唯一的地址每一主机上的每一进程应有在该主机上的唯一标识符。通常主机地址由网络ID和主机ID组成在TCPIP协议中用位整数值表示TCP和UDP均使用位端口号标识用户进程。()网络字节顺序不同的计算机存放多字节值的顺序不同有的机器在起始地址存放低位字节(低位先存)有的存高位字节(高位先存)。为保证数据的正确性在网络协议中须指定网络字节顺序。TCPIP协议使用位整数和位整数的高位先存格式它们均含在协议头文件中。()半相关综上所述网络中用一个三元组可以在全局唯一标志一个进程:(协议本地地址本地端口号)这样一个三元组叫做一个半相关(halfassociation)它指定连接的每半部分。()全相关一个完整的网间进程通信需要由两个进程组成并且只能使用同一种高层协议。也就是说不可能通信的一端用TCP协议而另一端用UDP协议。因此一个完整的网间通信需要一个五元组来标识:(协议,本地地址,本地端口号,远地地址,远地端口号)这样一个五元组叫做一个相关(association)即两个协议相同的半相关才能组合成一个合适的相关或完全指定组成一连接。服务方式在网络分层结构中各层之间是严格单向依赖的各层次的分工和协作集中体现在相邻层之间的界面上。“服务”是描述相邻层之间关系的抽象概念即网络中各层向紧邻上层提供的一组操作。下层是服务提供者上层是请求服务的用户。服务的表现形式是原语(primitive)如系统调用或库函数。系统调用是操作系统内核向网络应用程序或高层协议提供的服务原语。网络中的n层总要向n层提供比n层更完备的服务否则n层就没有存在的价值。在OSI的术语中网络层及其以下各层又称为通信子网只提供点到点通信没有程序或进程的概念。而传输层实现的是“端到端”通信引进网间进程通信概念同时也要解决差错控制流量控制数据排序(报文排序)连接管理等问题为此提供不同的服务方式:()面向连接(虚电路)或无连接面向连接服务是电话系统服务模式的抽象即每一次完整的数据传输都要经过建立连接使用连接终止连接的过程。在数据传输过程中各数据分组不携带目的地址而使用连接号(connectID)。本质上连接是一个管道收发数据不但顺序一致而且内容相同。TCP协议提供面向连接的虚电路。无连接服务是邮政系统服务的抽象每个分组都携带完整的目的地址各分组在系统中独立传送。无连接服务不能保证分组的先后顺序不进行分组出错的恢复与重传不保证传输的可靠性。UDP协议提供无连接的数据报服务。()顺序在网络传输中两个连续报文在端-端通信中可能经过不同路径这样到达目的地时的顺序可能会与发送时不同。“顺序”是指接收数据顺序与发送数据顺序相同。TCP协议提供这项服务。()差错控制保证应用程序接收的数据无差错的一种机制。检查差错的方法一般是采用检验“检查和(Checksum)”的方法。而保证传送无差错的方法是双方采用确认应答技术。TCP协议提供这项服务。()流控制在数据传输过程中控制数据传输速率的一种机制以保证数据不被丢失。TCP协议提供这项服务。()字节流字节流方式指的是仅把传输中的报文看作是一个字节序列不提供数据流的任何边界。TCP协议提供字节流服务。()报文接收方要保存发送方的报文边界。UDP协议提供报文服务。()全双工半双工端-端间数据同时以两个方向一个方向传送。客户服务器模式在TCPIP网络应用中通信的两个进程间相互作用的主要模式是客户服务器模式(ClientServermodel)即客户向服务器发出服务请求服务器接收到请求后提供相应的服务。客户服务器模式的建立基于以下两点:首先建立网络的起因是网络中软硬件资源、运算能力和信息不均等需要共享从而造就拥有众多资源的主机提供服务资源较少的客户请求服务这一非对等作用。其次网间进程通信完全是异步的相互通信的进程间既不存在父子关系又不共享内存缓冲区因此需要一种机制为希望通信的进程间建立联系为二者的数据交换提供同步这就是基于客户服务器模式的TCPIP。客户服务器模式在操作过程中采取的是主动请求方式:首先服务器方要先启动并根据请求提供相应服务:()打开一通信通道并告知本地主机它愿意在某一公认地址上(周知口如FTP为)接收客户请求()等待客户请求到达该端口()接收到重复服务请求处理该请求并发送应答信号。接收到并发服务请求要激活一新进程来处理这个客户请求(如UNIX系统中用fork、exec)。新进程处理此客户请求并不需要对其它请求作出应答。服务完成后关闭此新进程与客户的通信链路并终止。()返回第二步等待另一客户请求。()关闭服务器客户方:打开一通信通道并连接到服务器所在主机的特定端口向服务器发服务请求报文等待并接收应答继续提出请求请求结束后关闭通信通道并终止。从上面所描述过程可知:客户与服务器进程的作用是非对称的因此编码不同。服务进程一般是先于客户请求而启动的。只要系统运行该服务进程一直存在直到正常或强迫终止。套接字类型TCPIP的socket提供下列三种类型套接字。流式套接字(SOCKSTREAM)提供了一个面向连接、可靠的数据传输服务数据无差错、无重复地发送且按发送顺序接收。内设流量控制避免数据流超限数据被看作是字节流无长度限制。文件传送协议(FTP)即使用流式套接字。数据报式套接字(SOCKDGRAM)提供了一个无连接服务。数据包以独立包形式被发送不提供无错保证数据可能丢失或重复并且接收顺序混乱。网络文件系统(NFS)使用数据报式套接字。原始式套接字(SOCKRAW)该接口允许对较低层协议如IP、ICMP直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。基本套接字系统调用为了更好地说明套接字编程原理下面给出几个基本套接字系统调用说明。创建套接字socket()应用程序在使用套接字前首先必须拥有一个套接字系统调用socket()向应用程序提供创建套接字的手段其调用格式如下:intsocket(intdomain,inttype,intprotocol)domain:说明我们网络程序所在的主机采用的通信协议族(AFUNIX和AFINET等)UNIX系统支持的地址族有:AFUNIX、AFINET、AFNS等而DOS、WINDOWS中仅支持AFINET它是网际网区域。AFUNIX只能够用于单一的Unix系统进程间通信,而AFINET是针对Internet的,因而可以允许在远程主机之间通信。type:网络程序所采用的通信协议(SOCKSTREAM,SOCKDGRAM等)SOCKSTREAM表明我们用的是TCP协议,这样会提供按顺序的,可靠,双向,面向连接的比特流SOCKDGRAM表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信protocol:由于我们指定了type,所以这个地方我们一般只要用来代替就可以了socket为网络通信做基本的准备成功时返回文件描述符,失败时返回,看errno可知道出错的详细情况根据这三个参数建立一个套接字并将相应的资源分配给它同时返回一个整型套接字号。因此socket()系统调用实际上指定了相关五元组中的“协议”这一元。指定本地地址bind()当一个套接字用socket()创建后存在一个名字空间(地址族),但它没有被命名。bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来即将名字赋予套接字以指定本地半相关。其调用格式如下:intbind(intsockfd,structsockaddr*myaddr,intaddrlen)sockfd:是由socket调用返回的文件描述符addrlen:是sockaddr结构的长度myaddr:是一个指向sockaddr的指针sockaddr的定义:structsockaddr{unisgnedshortasfamilycharsadata}不过由于系统的兼容性,我们一般使用另外一个结构(structsockaddrin)来代替sockaddrin的定义:structsockaddrin{shortsinfamily*AFINET*ushortsinport*位端口号网络字节顺序*structinaddrsinaddr*位IP地址网络字节顺序*charsinzero*保留*}我们主要使用Internet所以sinfamily一般为AFINET,sinaddr设置为INADDRANY表示可以和任何的主机通信,sinport是我们要监听的端口号sinzero是用来填充的bind将本地的端口同socket返回的文件描述符捆绑在一起成功是返回,失败的情况和socket一样。建立套接字连接connect()与accept()这两个系统调用用于完成一个完整相关的建立其中connect()用于建立连接。无连接的套接字进程也可以调用connect()但这时在进程之间没有实际的报文交换调用将从本地操作系统直接返回。而accept()用于使服务器等待来自某客户进程的实际连接。connect()的调用格式如下:intconnect(intsockfd,structsockaddr*servaddr,intaddrlen)参数sockfd是欲建立连接的本地套接字描述符。参数servaddr指出说明对方套接字地址结构的指针。对方套接字地址长度由addrlen说明。connect函数是客户端用来同服务端连接的成功时返回,sockfd是同服务端通信的文件描述符,失败时返回accept()的调用格式如下:intaccept(intsockfd,structsockaddr*addr,int*addrlen)accept()调用的参数前应该先调用过listen()。sockfd:是listen后的文件描述符addr,addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了。调用前参数addr指向一个初始值为空的地址结构而addrlen的初始值为调用accept()后服务器等待从编号为sockfd的套接字上接受客户连接请求而连接请求是由客户方的connect()调用发出的。当有连接请求到达时accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入addr和addrlenbind,listen和accept是服务器端用的函数,accept调用时,服务器端的程序会一直阻塞到有一个客户程序发出了连接。accept成功时返回最后的服务器端的文件描述符这个时候服务器端可以向该描述符写信息了失败时返回。四个套接字系统调用socket()、bind()、connect()、accept()可以完成一个完全五元相关的建立。socket()指定五元组中的协议元它的用法与是否为客户或服务器、是否面向连接无关。bind()指定五元组中的本地二元即本地主机地址和端口号其用法与是否面向连接有关:在服务器方无论是否面向连接均要调用bind()在客户方若采用面向连接则可以不调用bind()而通过connect()自动完成。若采用无连接客户方必须使用bind()以获得一个唯一的地址。监听连接listen()此调用用于面向连接服务器表明它愿意接收连接。listen()需在accept()之前调用其调用格式如下:intlisten(intsockfd,intbacklog)参数sockfd标识一个本地已建立、尚未连接的套接字号服务器愿意从它上面接收请求。backlog表示请求连接队列的最大长度用于限制排队请求的个数目前允许的最大值为。如果没有错误发生listen()返回。否则它返回SOCKETERROR。调用listen()是服务器接收一个连接请求的四个步骤中的第三步。它在调用socket()分配一个流套接字且调用bind()给sockfd赋于一个名字之后调用而且一定要在accept()之前调用。在客户服务器模式中有两种类型的服务:重复服务和并发服务。accept()调用为实现并发服务提供了极大方便因为它要返回一个新的套接字号其典型结构为:intinitsockid,newsockidif((initsockid=socket())<)error(“can’tcreatesocket”)if(bind(initsockid,)<)error(“binderror”)if(listen(initsockid,)<)error(“listenerror”)for(){newsockid=accept(initsockid,)*阻塞*if(newsockid<)error(“accepterror“)if(fork()==){*子进程*closesocket(initsockid)do(newsockid)*处理请求*exit()}closesocket(newsockid)*父进程*}这段程序执行的结果是newsockid与客户的套接字建立相关子进程启动后关闭继承下来的主服务器的initsockid,并利用新的newsockid与客户通信。主服务器的initsockid可继续等待新的客户连接请求。由于在Unix等抢先多任务系统中在系统调度下多个进程可以同时进行。因此使用并发服务器可以使服务器进程在同一时间可以有多个子进程和不同的客户程序连接、通信。在客户程序看来服务器可以同时并发地处理多个客户的请求这就是并发服务器名称的来由。面向连接服务器也可以是重复服务器其结构如下:intinitsockid,newsockidif((initsockid=socket())<)error(“can’tcreatesocket”)if(bind(initsockid,)<)error(“binderror”)if(listen(initsockid,)<)error(“listenerror”)for(){newsockid=accept(initsockid,)*阻塞*if(newsockid<)error(“accepterror“)do(newsockid)*处理请求*closesocket(newsockid)}重复服务器在一个时间只能和一个客户程序建立连接它对多个客户程序的处理是采用循环的方式重复进行因此叫重复服务器。并发服务器和重复服务器各有利弊:并发服务器可以改善客户程序的响应速度但它增加了系统调度的开销重复服务器正好与其相反因此用户在决定是使用并发服务器还是重复服务器时要根据应用的实际情况来定。Socket建立流式套接字返回套接字号SBind()将套接字号与本地地址相连Listen(),通知服务器准备好接收TCP连接Accept(),接收连接它等待客户端的连接连接建立accept返回得到新的数据套接字如nsRecv()send(),在套接字ns上读写数据。直到数据交换完Close(),关闭套接字nsClose(),关闭最初套接字s程序结束服务器方Socket建立流式套接字返回套接字号SConnect()将套接字s与远地主机相连Recv()send(),在套接字ns上读写数据。直到数据交换完Close(),关闭最初套接字s程序结束客户机方建立连接服务请求应答基本套接字通信程序实例服务器端:#include<stdioh>#include<errnoh>#include<netdbh>#include<syssocketh>intmain(intargc,char*argv){intsockfd,newfdstructsockaddrinserveraddrstructsockaddrinclientaddrintsinsize,portnumbercharhello="Hello!AreYouFinen"if(argc!=){fprintf(stderr,"Usage:sportnumberan",argv)exit()}if((portnumber=atoi(argv))<){fprintf(stderr,"Usage:sportnumberan",argv)exit()}*服务器端开始建立socket描述符*if((sockfd=socket(AFINET,SOCKSTREAM,))==){fprintf(stderr,"Socketerror:sna",strerror(errno))exit()}*服务器端填充sockaddr结构*bzero(serveraddr,sizeof(structsockaddrin))serveraddrsinfamily=AFINETserveraddrsinaddrsaddr=htonl(INADDRANY)serveraddrsinport=htons(portnumber)*捆绑sockfd描述符*if(bind(sockfd,(structsockaddr*)(serveraddr),sizeof(structsockaddr))==){fprintf(stderr,"Binderror:sna",strerror(errno))exit()}*监听sockfd描述符*if(listen(sockfd,)==){fprintf(stderr,"Listenerror:sna",strerror(errno))exit()}while(){*服务器阻塞,直到客户程序建立连接*sinsize=sizeof(structsockaddrin)if((newfd=accept(sockfd,(structsockaddr*)(clientaddr),sinsize))==){fprintf(stderr,"Accepterror:sna",strerror(errno))exit()}fprintf(stderr,"Servergetconnectionfromsn",inetntoa(clientaddrsinaddr))if(write(newfd,hello,strlen(hello))==){fprintf(stderr,"WriteError:sn",strerror(errno))exit()}*这个通信已经结束*close(newfd)*循环下一个*}close(sockfd)exit()}客户端:#include<stdioh>#include<netdbh>#include<syssocketh>#include<syserrnoh>intmain(intargc,char*argv){intsockfdcharbufferstructsockaddrinserveraddrstructhostent*hostintportnumber,nbytesif(argc!=){fprintf(stderr,"Usage:shostnameportnumberan",argv)exit()}if((host=gethostbyname(argv))==){fprintf(stderr,"Gethostnameerrorn")exit()}if((portnumber=atoi(argv))<){fprintf(stderr,"Usage:shostnameportnumberan",argv)exit()}*客户程序开始建立sockfd描述符*if((sockfd=socket(AFINET,SOCKSTREAM,))==){fprintf(stderr,"SocketError:san",strerror(errno))exit()}*客户程序填充服务端的资料*bzero(serveraddr,sizeof(serveraddr))serveraddrsinfamily=AFINETserveraddrsinport=htons(portnumber)serveraddrsinaddr=*((structinaddr*)host>haddr)*客户程序发起连接请求*if(connect(sockfd,(structsockaddr*)(serveraddr),sizeof(structsockaddr))==){fprintf(stderr,"ConnectError:san",strerror(errno))exit()}*连接成功了*if((nbytes=read(sockfd,buffer,))==){fprintf(stderr,"ReadError:sn",strerror(errno))exit()}buffernbytes=""printf("Ihavereceived:sn",buffer)*结束通信*close(sockfd)exit()}服务器和客户机的信息函数字节转换函数在网络上面有着许多类型的机器,这些机器在表示数据的字节顺序是不同的,比如i芯片是低字节在内存地址的低端,高字节在高端,而alpha芯片却相反为了统一起来,在Linux下面,有专门的字节转换函数unsignedlonginthtonl(unsignedlonginthostlong)unsignedshortinthtons(unisgnedshortinthostshort)unsignedlongintntohl(unsignedlongintnetlong)unsignedshortintntohs(unsignedshortintnetshort)在这四个转换函数中,h代表host,n代表networks代表short,l代表long,第一个函数的意义是将本机器上的long数据转化为网络上的long其他几个函数的意义也差不多IP和域名的转换在网络上标志一台机器可以用IP或者是用域名,那么我们怎么去进行转换呢structhostent*gethostbyname(constchar*hostname)structhostent*gethostbyaddr(constchar*addr,intlen,inttype)structhostent的定义:structhostent{char*hname*主机的正式名称*char*haliases*主机的别名*inthaddrtype*主机的地址类型AFINET*inthlength*主机的地址长度对于IP是字节位*char**haddrlist*主机的IP地址列表*#definehaddrhaddrlist*主机的第一个IP地址*}gethostbyname可以将机器名(如linuxyessuncom)转换为一个结构指针在这个结构里面储存了域名的信息gethostbyaddr可以将一个位的IP地址(CA)转换为结构指针这两个函数失败时返回且设置herrno错误变量,调用hstrerror()可以得到详细的出错信息字符串的IP和位的IP转换在网络上面我们用的IP都是数字加点()构成的,而在structinaddr结构中用的是位的IP,我们上面那个位IP(CA)是的,为了转换我们可以使用下面两个函数intinetaton(constchar*cp,structinaddr*inp)char*inetntoa(structinaddrin)函数里面a代表asciin代表network第一个函数表示将abcd的IP转换为位的IP,存储在inp指针里面第二个是将位IP转换为abcd的格式完整的读写函数在Linux中把我们前面建立的通道看成是文件描述符,这样服务器端和客户端进行通信时候,只要往文件描述符里面读写东西了就象我们往文件读写一样写函数writeintwrite(intfd,constvoid*buf,sizetnbytes)write函数将buf中的nbytes字节内容写入文件描述符fd成功时返回写的字节数失败时返回并设置errno变量在网络程序中,当我们向套接字文件描述符写时有两种可能()write的返回值大于,表示写了部分或者是全部的数据()返回的值小于,此时出现了错误我们要根据错误类型来处理如果错误为EINTR表示在写的时候出现了中断错误如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接)读函数readintread(intfd,void*buf,sizetnbyte)read函数是负责从fd中读取内容当读成功时,read返回实际所读的字节数,如果返回的值是表示已经读到文件的结束了,小于表示出现了错误如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题send()与recv()send和recv函数提供了和read和write差不多的功能send()调用用于在参数s指定的已连接的数据报或流套接字上发送输出数据格式如下:intsend(intsockfd,void*buf,intlen,intflags)参数sockfd为已连接的本地套接字描述符。buf指向存有发送数据的缓冲区的指针其长度由len指定。flags指定传输控制方式如是否发送带外数据等。如果没有错误发生send()返回总共发送的字节数。否则它返回SOCKETERROR。recv()调用用于在参数s指定的已连接的数据报或流套接字上接收输入数据格式如下:intrecv(intsockfd,void*buf,intlen,intflags)参数sockfd为已连接的套接字描述符。Buf指向接收输入数据缓冲区的指针其长度由len指定。flags指定传输控制方式如是否接收带外数据等。如果没有错误发生recv()返回总共接收的字节数。如果连接被关闭返回。否则它返回SOCKETERROR。recv()调用用于在参数sockfd指定的已连接的数据报或流套接字上接收输入数据格式如下:intrecv(intsockfd,void*buf,intlen,intflags)参数sockfd为已连接的套接字描述符。Buf指向接收输入数据缓冲区的指针其长度由len指定。flags指定传输控制方式如是否接收带外数据等。如果没有错误发生recv()返回总共接收的字节数。如果连接被关闭返回。否则它返回SOCKETERROR。关闭套接字close()closesocket()关闭套接字sockfd并释放分配给该套接字的资源如果sockfd涉及一个打开的TCP连接则该连接被释放。closesocket()的调用格式如下:intclose(intsockfd)参数sockfd为待关闭的套接字描述符。如果没有错误发生closesocket()返回

用户评论(0)

0/200

精彩专题

上传我的资料

每篇奖励 +1积分

资料评分:

/29
仅支持在线阅读

意见
反馈

立即扫码关注

爱问共享资料微信公众号

返回
顶部

举报
资料