版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
-rLINUX网辂编程
LINUXTCP/IP网络编程
3匚大,堂信息科考与工程学院付冲
LINUX网辂编程
LinuxTCP/IP网络编程
1.1Linux网络基础
TCP/IP协议集
TELNETFTPSMTPDNSHTTPSNMPPOPApplication
TCPUDPTransport
ICMP
IPInternet
ARPRARP
Host-to-
ARPANETSATNETPacketradioLAN
network
LINUX网辂编程
L本地回路:lo,固定IP地址:
以太网卡:ethO,ethl,.......
2.查看IP地址一ifconfig
各字段说明:
Hwaddr:网卡地址(物理地址,硬件地址)
inetaddr:IP地址
Beast:广播地址
LINUX网辂编程
Mask:子网掩码
MTU:MaxTransmissionUnit,最大传输单元,
即数据帧最大长度
Metric:路由长度
TXpackets:发送的数据包总数、错误数、遗失数
及溢出数
RXpackets:接收的数据包总数、错误数、遗失数
及溢出数
LINUX网辂编程
3.设置IP地址相关信息
语法:ifconfig接口IP地址[broadcast广播
地址netmask子网掩码]
例:ifconfigethO
ifconfigethObroadcast
55netmask
注意:ifconfig命令设置的IP地址不能永久保存,重
新启动计算机后将丢失设置
LINUX网辂编程
4.netconfig命令设置网络参数
usedynamicIPconfiguration
IPaddress
Netmask
Defaultgateway(IP)
Primarynameserver
注意:netconfig命令设置的网络配置信息可以永久
保存到配置文件中,但不会立即生效,刷新方
法:/etc/init.d/networkrestart
LINUX网辂编程
5.网络配置相关文件
(1)/etc/sysconfig/network
HOSTNAME:主机名设置
GATEWAY:默认网关的设置
LINUX网辂编程
(2)/etc/sysconng/network-scnpts/ifcfg-ethO
DEVICE:设备名称
BOOTPROTO:IP地址设置方式(动态或静态)
BROADCAST:广播地址
HWADDR:硬件地址
IPADDR:IP地址设置
NETMASK:子网掩码设置
NETWORK:网络地址
ONBOOT:启动时是否激活
LINUX网辂编程
(3)/etc/resolv.conf
DNS服务器地址设置。
(4)/etc/hosts
存放一些常用主机的IP地址与主机名称的数据,
即一个简单的DNS数据库。
基本格式为:
IP主机域名主机别名
(5)/etc/services
记录主机提供的网络服务项目、端口号(port)及
使用的运输层协议。
LINUX网辂编程
1.2套接字编程基本概念
在UNIX系统中,网络应用编程接口有两类:
UNIXBSD的套接字(socket)
UNIXSystemV的TLI
由于Sim公司采用了支持TCP/IP的UNIXBSD操
作系统,使TCP/IP的应用有更大的发展,其网络应
用编程接口---套接字(socket)在网络软件中被广泛应
用,至今已引进微机操作系统DOS和Windows系统
中,成为开发网络应用软件的强有力工具。
LINUX网辂编程
1.2.1网间进程通信
进程通信的概念最初来源于单机系统。由于每个进
程都在自己的地址范围内运行,为保证两个相互通信
的进程之间既互不干扰又协调一致工作,操作系统为进
程通信提供了相应设施,如UNIXBSD中的管道
(pipe)、命名管道(namedpipe)和软中断信号(signal),
UNIXsystemV的消息(message)、共享存储区(shared
memory)和信号量(semaphore)等,但都仅限于用在本
机进程之间通信。
LINUX网辂编程
网间进程通信要解决的是不同主机进程间的相互通信问
题。
为此,首先要解决的是网间进程标识问题。同一主机
上,不同进程可用进程号(processID)唯一标识。但
在网络环境下,各主机独立分配的进程号不能唯一标识
该进程。例如,主机A赋于某进程号5,在B机中也可
以存在5号进程,因此,“5号进程”这句话就没有意义
了。
其次,操作系统支持的网络协议众多,不同协议的工
作方式不同,地址格式也不同。
LINUX网辂编程
因此,网间进程通信还要解决多重协议的识别问
题。
为了解决上述问题,TCP/IP协议引入了下列几个概
念。
⑴端口
网络中可以被命名和寻址的通信端口,是操作系
统可分配的一种资源。
LINUX网辂编程
按照OSI七层协议的描述,运输层与网络层在功能
上的最大区别是运输层提供进程通信能力。从这个意
义上讲,网络通信的最终地址就不仅仅是主机地址
了,还包括可以描述进程的某种标识符。为此,
TCP/IP协议提出了协议端口(protocolport),简称端
口)的概念,用于标识通信的进程。
端口是一种抽象的软件结构(包括一些数据结构和
DO缓冲区)。
LINUX网辂编程
应用程序(即进程)通过系统调用与某端口建立连接
(binding)后,运输层传给该端口的数据都被相应进程
所接收,相应进程发给运输层的数据都通过该端口输
出。
在TCP/IP协议的实现中,端口操作类似于一般的
DO操作,进程获取一个端口,相当于获取本地唯一的
DO文件,可以用一般的读写原语访问之。
LINUX网辂编程
类似于文件描述符,每个端口都拥有一个叫端口号
(portnumber)的整数型标识符,用于区别不同端口。
由于TCP/IP运输层的两个协议TCP和UDP是完全独立
的两个软件模块,因此各自的端口号也相互独立,如
TCP有一个255号端口,UDP也可以有一个255号端
口,二者并不冲突。
LINUX网辂编程
TCP/IP号端口号分为两部分,少量的作为保留端
口,以全局方式分配给服务进程。因此,每一个标准
服务器都拥有一个全局公认的端口(即周知端口,
well-knownport),即使在不同机器上,其端口号也
相同。剩余的为自由端口,以本地方式进行分配。
TCP和UDP均规定,小于256的端口号才能作保留端
口八
LINUX网辂编程
⑵地址
网络通信中通信的两个进程分别在不同的机器上。
在互连网络中,两台机器可能位于不同的网络,这些
网络通过网络互连设备(网关,网桥,路由器等)连
接。因此需要三级寻址:
(a)某一主机可与多个网络相连,必须指定一特定网络
地址;
LINUX网辂编程
(b)网络上母一台主机应有其唯一的地址;
(c)每一主机上的每一进程应有在该主机上的唯一标识
符。
通常主机地址由网络ID和主机ID组成,在TCP/IP
协议中用32位整数值表示;TCP和UDP均使用16位
端口号标识用户进程。
LINUX网辂编程
(3)网络字节顺序
不同的计算机存放多字节值的顺序不同,有的机器
在起始地址存放低位字节(低位先存),有的存高位
字节(高位先存)。为保证数据的正确性,在网络协
议中须指定网络字节顺序。TCP/IP协议使用16位整数
和32位整数的高位先存格式,它们均含在协议头文件
中。
LINUX网辂编程
(4)半相关
综上所述,网络中用一个三元组可以在全局唯一标
志一个进程:
(协议,本地地址,本地端口号)
这样一个三元组,叫做一个半相关(half-
association),它指定连接的每半部分。
LINUX网辂编程
(5)全相关
一个完整的网间进程通信需要由两个进程组成,并
且只能使用同一种高层协议。也就是说,不可能通信
的一端用TCP协议,而另一端用UDP协议。因此一
个完整的网间通信需要一个五元组来标识:
(协议,本地地址,本地端口号,远地地址,远地端口
号)
LINUX网辂编程
这样一个五元组,叫做一个相关(association),即两
个协议相同的半相关才能组合成一个合适的相关,或
完全指定组成一连接。
LINUX网辂编程
1.2.2客户/服务器模式
在TCP/IP网络应用中,通信的两个进程间相互作
用的主要模式是客户/服务器模式(Client/Server
model),即客户向服务器发出服务请求,服务器接收
到请求后,提供相应的服务。
客户/服务器模式的建立基于以下两点:首先,建立
网络的起因是网络中软硬件资源、运算能力和信息不
均等,需要共享,从而造就拥有众多资源的主机提供
服务,资源较少的客户请求服务这一非对等作用。
LINUX网辂编程
其次,网间进程通信完全是异步的,相互通信的进
程间既不存在父子关系,又不共享内存缓冲区,因此
需要一种机制为希望通信的进程间建立联系,为二者
的数据交换提供同步,这就是基于客户/服务器模式的
TCP/IPo
LINUX网辂编程
客户/服务器模式在操作过程中采取的是主动请求方
式:
首先服务器方要先启动,并根据请求提供相应服
务:
(1)打开一通信通道并告知本地主机,它愿意在某
一公认地址上(周知口,如FTP为21)接收客户请求;
(2)等待客户请求到达该端口;
LINUX网辂编程
(3)接收到重复服务请求,处理该请求并发送应答信
号。接收到并发服务请求,要激活一新进程来处理这
个客户请求(如UNIX系统中用fork,exec)。新进程处
理此客户请求,并不需要对其它请求作出应答。服务
完成后,关闭此新进程与客户的通信链路,并终止。
(4)返回第二步,等待另一客户请求。
(5)关闭服务器。
LINUX网辂编程
客户方:
1.打开一通信通道,并连接到服务器所在主机的
特定端口;
2.向服务器发服务请求报文,等待并接收应答;
继续提出请求……
3.请求结束后关闭通信通道并终止。
LINUX网辂编程
从上面所描述过程可知:
1.客户与服务器进程的作用是非对称的,因此编码
不同。
2.服务进程一般是先于客户请求而启动的。只要系
统运行,该服务进程一直存在,直到正常或强迫终
止。
LINUX网辂编程
1.2.3套接字类型
TCP/IP的socket提供下列三种类型套接字。
1.流式套接字(SOCK_STREAM)
提供了一个面向连接、可靠的数据传输服务,数据
无差错、无重复地发送,且按发送顺序接收。内设流
量控制,避免数据流超限;数据被看作是字节流,无
长度限制。文件传送协议(FTP)即使用流式套接字。
LINUX网辂编程
2.数据报式套接字(SOCK_DGRAM)
提供了一个无连接服务。数据包以独立包形式被发
送,不提供无错保证,数据可能丢失或重复,并且接
收顺序混乱。网络文件系统(NFS)使用数据报式套接
字。
3.原始式套接字(SOCK_RAW)
该接口允许对较低层协议,如IP、ICMP直接访问。
常用于检验新的协议实现或访问现有服务中配置的新
设备。
LINUX网辂编程
1.3基本套接字系统调用
为了更好地说明套接字编程原理,下面给出几个基
本套接字系统调用说明。
1.3.1创建套接字——socket()
应用程序在使用套接字前,首先必须拥有一个套接
字,系统调用socket。向应用程序提供创建套接字的手
段,其调用格式如下:
LINUX网辂编程
mtsocket(mtdomain,mttype,intprotocol)
domain:说明我们网络程序所在的主机采用的通信
协议族(AFJJNIX和AFJNET等).UNIX系统支持的地
址族有:AFJJNIX、AFJNET>AF_NS等,而DOS、
WINDOWS中仅支持AF_INET,它是网际网区域。
AF_UNIX只能够用于单一的Unix系统进程间通信,而
AF_INET是针对Internet的,因而可以允许在远程主机
之间通信。
LINUX网辂编程
type:网络程序所采用的通信协议(SOCK_STREAM,
SOCK_DGRAM等)。
SOCK_STREAM表明采用的是TCP协议,即提供
按顺序的,可靠,双向,面向连接的比特流。
SOCK_DGRAM表明采用的是UDP协议,这样只
会提供定长的,不可靠,无连接的通信。
LINUX网辂编程
protocol:由于我们指定了type,所以这个地方我们一
般只要用0来代替就可以了socket为网络通信做基本
的准备.
成功时返回文件描述符,失败时返回查看errno可
知道出错的详细情况.
根据这三个参数建立一个套接字,并将相应的资源
分配给它,同时返回一个整型套接字号。
因此,socket。系统调用实际上指定了相关五元组中
的“协议”这一元。
LINUX网辂编程
1.3.2指定本地地址——bind()
当一个套接字用socket。创建后,存在一个名字空间
(地址族),但它没有被命名。bind。将套接字地址(包括
本地主机地址和本地端口地址)与所创建的套接字号
联系起来,即将名字赋予套接字,以指定本地半相
关。其调用格式如下:
LINUX网辂编程
mtbind(mtsockfd,structsockaddr*my_addr,mt
addrlen)
sockfd:是由socket调用返回的文件描述符.
addrlen:是sockaddr结构的长度.
my_addr:是一个指向sockaddr的指针.
sockaddr的定义:
LINUX网辂编程
structsockaddr{
unisgnedshortas_family;
charsa_data[14];
);
不过由于系统的兼容性,我们一般使用另外一个结构
(structsockaddr_in)来代替.
sockaddr_in的定义:
LINUX网辂编程
structsockaddr_in{
shortsin_family;
u_shortsin_port;/*16位端口号,网络字节顺序*/
structin_addrsin_addr;/*32位IP地址,网络字节顺序刃
charsin_zero[8];/*保留刃
)
我们主要使用Internet所以sin_family一般为AF_INET,
sinaddr设置为INADDRANY表示可以和任何的主机
LINUX网辂编程
通信,sin_port是我们要监听的端口号.sin_zero⑻是用
来填充的.bind1寻本地的端口同socket返回的文件描述
符捆绑在一起.成功是返回0,失败的情况和socket一
样。
LINUX网辂编程
1.3.3建立套接字连接一connect()与accept()
这两个系统调用用于完成一个完整相关的建立,其中
connect。用于建立连接。无连接的套接字进程也可以调
用connect(),但这时在进程之间没有实际的报文交换,
调用将从本地操作系统直接返回。而accept。用于使服
务器等待来自某客户进程的实际连接。
connect。的调用格式如下:
intconnect(intsockfd,structsockaddr^serv_addr9int
addrlen)
LINUX网辂编程
参数sockfd是欲建立连接的本地套接字描述符。参
数serv_addr指出说明对方套接字地址结构的指针。
对方套接字地址长度由addrlen说明。
connect函数是客户端用来同服务端连接的。成功时
返回0,sockfd是同服务端通信的文件描述符,失败时
返回
LINUX网辂编程
accept。的调用格式如下:
intaccept(intsockfd,structsockaddr*addr,int
^addrlen)
accept()调用前应该先调用过listen()o
sockfd:是listen后的文件描述符。
addr,addrlen是用来给客户端的程序填写的,服务
器端只要传递指针就可以了。
LINUX网辂编程
调用前,参数addr指向一个初始值为空的地址结
构,而addrlen的初始值为0;调用accept()后,服务
器等待从编号为sockfd的套接字上接受客户连接请
求,而连接请求是由客户方的connect()调用发出的。
当有连接请求到达时,accept()调用将请求连接队列上
的第一个客户方套接字地址及长度放入addr和addrlen.
LINUX网辂编程
bind,listen和accept是服务器端用的函数,accept调
用时,服务器端的程序会一直阻塞到有一个客户程序发
出了连接。accept成功时返回最后的服务器端的文件
描述符,这个时候服务器端可以向该描述符写信息
了,失败时返回4。
四个套接字系统调用,socket。、bind。、connect。、
accept(),可以完成一个完全五元相关的建立。socket()
指定五元组中的协议元,它的用法与是否为客户或服
务器、是否面向连接无关。
LINUX网辂编程
bind()指定五元组中的本地二元,即本地主机地址和
端口号,其用法与是否面向连接有关:在服务器方,
无论是否面向连接,均要调用bind。;在客户方,若采
用面向连接,则可以不调用bind(),而通过connect。自
动完成。若采用无连接,客户方必须使用bind。以获得
一个唯一的地址。
LINUX网辂编程
1.3.4监听连接——listen()
此调用用于面向连接服务器,表明它愿意接收连
接。listen。需在accept。之前调用,其调用格式如下:
intlisten(intsockfd,intbacklog)
参数sockfd标识一个本地已建立、尚未连接的套接
字号,服务器愿意从它上面接收请求。
LINUX网辂编程
backlog表示请求连接队列的最大长度,用于限制排
队请求的个数,目前允许的最大值为5。如果没有错误
发生,listen。返回0。否则它返回SOCKET_ERROR。
调用listen。是服务器接收一个连接请求的四个步骤
中的第三步。它在调用socket。分配一个流套接字,且
调用bind。给sockfd赋于一个名字之后调用,而且一
定要在accept。之前调用。
LINUX网辂编程
在客户/服务器模式中,有两种类型的服务:重复服
务和并发服务。accept。调用为实现并发服务提供了极
大方便,因为它要返回一个新的套接字号,其典型结
构为:
intinitsockid,newsockid;
if((initsockid=socket(....))<0)
error(ucan,tcreatesocket");
if(bind(initsockidv...)<0)
error(ubinderror");
LINUX网辂编程
if(listen(initsockid,5)v0)
error(ulistenerror");
for(;;){
newsockid=accept(initsockid,)/*阻塞*/
if(newsockid<0)
error(uaccepterror");
if(fork()==0){/*子进程*/
closesocket(initsockid);
do(newsockid);/*处理请求*/
exit(0);
LINUX网辂编程
closesocket(newsockid);/*父进程*/
)
这段程序执行的结果是newsockid与客户的套接字建
立相关,子进程启动后,关闭继承下来的主服务器的
initsockid,并利用新的newsockid与客户通信。主服务
器的initsockid可继续等待新的客户连接请求。由于在
Unix等抢先多任务系统中,在系统调度下,多个进程
可以同时进行。因此,使用并发服务器可以使服务器
LINUX网辂编程
进程在同一时间可以有多个子进程和不同的客户程序
连接、通信。在客户程序看来,服务器可以同时并发
地处理多个客户的请求,这就是并发服务器名称的来
由。
LINUX网辂编程
面向连接服务器也可以是重复服务器,其结构如下:
intinitsockid,newsockid;
if((initsockid=socket(....))<0)
error(ucan,tcreatesocket");
if(bind(initsockidv...)<0)
error(ubinderror");
if(listen(initsockid,5)<0)
error(ulistenerror");
LINUX网辂编程
for(;;){
newsockid=accept(initsockid,...)/*阻塞❖/
if(newsockid<0)
error(uaccepterror");
do(newsockid);/*处理请求*/
closesocket(newsockid);
)
LINUX网辂编程
重复服务器在一个时间只能和一个客户程序建立连
接,它对多个客户程序的处理是采用循环的方式重复
进行,因此叫重复服务器。
并发服务器和重复服务器各有利弊:并发服务器可
以改善客户程序的响应速度,但它增加了系统调度的
开销;重复服务器正好与其相反,因此用户在决定是
使用并发服务器还是重复服务器时,要根据应用的实
际情况来定。
服务器方
LINUX网辂编程
客户机方
建立连接
服务请求/应答
LINUX网辂编程
1.4基本套接字通信程序实例
服务器端(server-basic.c):
#include<stdio.h>
#include<errno.h>
#include<netdb.h>
#include<sys/socket.h>
intmain(intargc,char*argv[]){
intsockfd,new_fd;
structsockaddr_inserver_addr;
structsockaddr_inclient_addr;
LINUX网辂编程
intsin_size,portnumber;
charhello[]=HHello!AreYouFine?\nM;
if(argc!=2){
H1f
fprintf(stderr9Usage:%sportnumber\a\n,argv[0]);
exit(l);
)
if((portnumber=atoi(argv[l]))<0){
n1
fprintf(stderr9Usage:%sportnumber\a\n,argv[0]);
exit(l);
LINUX网辂编程
/*服务器端开始建立socket描述符*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
(
fprintf(stderr,nSocketerror:%s\n\a,\strerror(errno));
exit(l);
)
/*服务器端填充sockaddr结构*/
bzero(&server_addr,sizeof(structsockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
LINUX网辂编程
/*捆绑sockfd描述符*/
if(bind(sockfd,(structsockaddr*)(&server_addr),
sizeof(structsockaddr))==-1){
fprintf(stderr,nBinderror:%s\n\a*\strerror(errno));
exit(l);
)
/*监听sockfd描述符*/
if(listen(sockfd,5)==-1)
Hn
fprintf(stderr,Listenerror:%s\n\a9strerror(errno));
LINUX网辂编程
exit(l);
)
\¥11阳1){
/*服务器阻塞,直到客户程序建立连接*/
sin_size=sizeof(structsockaddr_in);
if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),
&sin_size))==-1)
Hn
fprintf(stderr,Accepterror:%s\n\a9strerror(errno));
exit(l);
LINUX网辂编程
fprintf(stderr,HServergetconnectionfrom%s\nH,
inet_ntoa(client_addr.sin_addr));
if(write(new_fd,hello,strlen(hello))==-l){
Mn
fprintf(stderr,WriteError:%s\n9strerror(errno));
exit(l);
)
/*这个通信已经结束*/
close(new_fd);
/*循环下一^个*/
LINUX网辂编程
close(sockfd);
exit(O);
)
客户端(client-basic・c):
#include<stdio.h>
#include<netdb.h>
#include<sys/socket.h>
#include<sys/errno.h>
intmain(intargc,char*argv口)
LINUX网辂编程
mtsockfd;
charbuffer[1024];
structsockaddr_inserver_addr;
structhostent*host;
intportnumber,nbytes;
if(argc!=3){
n1
fprintf(stderr9Usage:%shostnameportnumber\a\n*,
argv[0]);
exit(l);
LINUX网辂编程
if((host=gethostbyname(argv[l]))==NULL){
fprintf(stderr,nGethostnameerror\nn);
exit(l);
)
if((portnumber=atoi(argv[2]))<0){
fprintf(stderr,HUsage:%shostnameportnumber\a\n1*,
argv[0]);
exit(l);
)
LINUX网辂编程
/*客户程序开始建立sockfd描述符*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-l){
M
fprintf(stderr9^SocketError:%s\a\n,strerror(errno));
exit(l);
)
/*客户程序填充服务端的资料*/
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((structin_addr*)host->h_addr);
LINUX网辂编程
/*客户程序发起连接请求*/
if(connect(sockfd,(structsockaddr*)(&server_addr),
sizeof(structsockaddr))==-1){
fprintf(stderr,nConnectError:%s\a\nf\strerror(errno));
exit(l);
)
/*连接成功*/
if((nbytes=read(sockfd,buffer,1024))==-1){
fprintf(stderr,nReadError:%s\nf\strerror(errno));
exit(l);
LINUX网辂编程
buffer[nbytes]=H\On;
printf(MIhavereceived:%s\nf\buffer);
/*结束通信*/
close(sockfd);
exit(O);
LINUX网辂编程
1.5服务器和客户机的信息函数
1.5.1字节转换函数
在网络上面有着许多类型的机器,这些机器在表示数
据的字节顺序是不同的,比如i386芯片是低字节在内存
地址的低端,高字节在高端,而alpha芯片却相反.为了
统一起来在Linux下面,有专门的字节转换函数.
LINUX网辂编程
unsignedlongmthtonl(unsignedlongmthostlong)
unsignedshortinthtons(unisgnedshortinthostshort)
unsignedlongintntohl(unsignedlongintnetlong)
unsignedshortintntohs(unsignedshortintnetshort)
在这四个转换函数中,h代表host,n代表network,s
代表short,1代表long,第一个函数的意义是将本机器
上的long数据转化为网络上的long.其他几个函数的意
义同上.
LINUX网辂编程
1.5.2IP和域名的转换
在网络上标志一台机器可以用IP或者是用域名,那
么我们怎么去进行转换呢?
structhostent*gethostbyname(constchar*hostname)
structhostent*gethostbyaddr(constchar*addr,intleu,inttype)
structhostent的定义:
LINUX网辂编程
structhostent{
char*h_name;/*主机的正式名称*/
char*h_aliases;/*主机的别名*/
inth_addrtype;主机的地址类型AF_INET*/
inthJength;/*主机的地址长度对于IP4是4字节32位*/
char**h_addr_list;/*主机的IP地址列表*/
#defineh_addrh_addr_list[O]/*主机的第一个IP地址*/
)
gethostbyname可以将机器名(如)#
换为一个结构指针.在这个结构里面储存了域名的信息.
LINUX网辂编程
gethostbyaddr可以将一个32位的IP地址(C0A80001)
转换为结构指针.
这两个函数失败时返回NULL且设置h_errno错误
变量,调用h_strerror()可以得到详细的出错信息.
1.5.3字符串的IP和32位IP的转换
在网络上面我们用的IP都是数字加点()
构成的,而在structin_addr结构中用的是32位的IP,
我们上面那个32位IP(C0A80001)是的,为
了转换我们可以使用下面两个函数
LINUX网辂编程
mtmet_aton(constchar*cp,structm_addr*mp)
char^inet_ntoa(structin_addrin)
函数里面a代表asciin代表network.第一个函数表
示将a.b.c.d的IP转换为32位的IP,存储在inp指针里
面.第二个是修32位IP转换为a.b.c.d的格式.
LINUX网辂编程
1.6完整的读写函数
在Linux中把我们前面建立的通道看成是文件描述
符,这样服务器端和客户端进行通信时候,只要往文件描
述符里面读写东西了.就象我们往文件读写一样.
1.6.1写函数write
intwrite(intfd,constvoid*buf,size_tnbytes)
write函数将buf中的nbytes字节内容写入文件描述
符fd.成功时返回写的字节数.失败时返回并设置
errno变量.
LINUX网辂编程
在网络程序中,当我们向套接字文件描述符写时有两
种可能.
(1)write的返回值大于0,表示写了部分或者是全部的
数据.
(2)返回的值小于0,此时出现了错误.我们要根据错误
类型来处理.
如果错误为EINTR表示在写的时候出现了中断错误.
LINUX网辂编程
如果为EPIPE表示网络连接出现了问题(对方已经
关闭了连接).
1.6.2读函数read
intread(intfd,void*buf,size_tnbyte)
read函数是负责从fd中读取内容.当读成功时,read
返回实际所读的字节数,如果返回的值是0表示已经读
到文件的结束了,小于0表示出现了错误.如果错误为
EINTR说明读是由中断引起的,如果是ECONNREST
表示网络连接出了问题.
LINUX网辂编程
1.6.3send()与recv()
send和recv函数提供了和read和write差不多的功
能.
send()调用用于在参数s指定的已连接的数据报或流
套接字上发送输出数据,格式如下:
intsend(intsockfd,void*buf,intlen,intflags)
参数sockfd为已连接的本地套接字描述符。buf指
向存有发送数据的缓冲区的指针,其长度由len指定。
flags指定传输控制方式,如是否发送带外数据等。
LINUX网辂编程
如果没有错误发生,send。返回总共发送的字节数。否
则它返回SOCKET_ERROR。
recv()调用用于在参数sockfd指定的已连接的数据
报或流套接字上接收输入数据,格式如下:
intrecv(intsockfd,void*buf,intleu,intflags)
参数sockfd为已连接的套接字描述符。Buf指向接收
输入数据缓冲区的指针,其长度由len指定。flags指
定传输控制方式,如是否接收带外数据等。
LINUX网辂编程
如果没有错误发生,recv()返回总共接收的字节数。如
果连接被关闭,返回0。否则它返回
SOCKETERRORo
LINUX网辂编程
1.6.4关闭套接字——close()
closesocket()关闭套接字sockfd,并释放分配给该套
接字的资源;如果sockfd涉及一个打开的TCP连接,
则该连接被释放。dosesocket。的调用格式如下:
intclose(intsockfd)
参数sockfd为待关闭的套接字描述符。如果没有错
误发生,closesocket()返回0。否则返回值
SOCKETERRORo
LINUX网辂编程
1.7完整的数据收发与套接字关闭程序
服务器端(server-standard.c):
#include<stdio.h>
#include<errno.h>
#include<netdb.h>
#include<sys/socket.h>
intmain(intargc,char*argv[])
intsockfd,new_fd;
structsockaddr_inserver_addr;
structsockaddr_inclient_addr;
LINUX网辂编程
intsin_size,portnumber;
charhello[]=HHello!AreYouFine?\nM;
if(argc!=2)
(
H1
fprintf(stderr9Usage:%sportnumber\a\n*,argv[0]);
exit(l);
)
if((portnumber=atoi(argv[l]))<0)
(
fprintf(stderr,MUsage:%sportnumber\a\n1,argv[0]);
exit(l);
)
LINUX网辂编程
/*服务器端开始建立socket描述符*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
(
fprintf(stderr,nSocketerror:%s\n\a,\strerror(errno));
exit(l);
)
/*服务器端填充sockaddr结构*/
bzero(&server_addr,sizeof(structsockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
LINUX网辂编程
/*捆绑sockfd描述符*/
if(bind(sockfd,(structsockaddr*)(&server_addr)9sizeof(struct
sockaddr))==-1)
(
Kn
fprintf(stderr,Bmderror:%s\n\a9strerror(errno));
exit(l);
)
/*监听sockfd描述符*/
if(listen(sockfd,5)==-1){
fprintf(stderr,^Listenerror:%s\n\a,\strerror(errno));
LINUX网辂编程
exit(l);
)
\¥11阳1){
/*服务器阻塞,直到客户程序建立连接*/
sin_size=sizeof(structsockaddr_in);
if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),
&sin_size))==-1)
Hn
fprintf(stderr,Accepterror:%s\n\a9strerror(errno));
exit(l);
LINUX网辂编程
fprintf(stderr,HServergetconnectionfrom
inet_ntoa(client_addr.sin_addr));
if(write(new_fd,hello,strlen(hello))==-l){
1n
fprintf(stderr,,WriteError:%s\n9strerror(errno));
exit(l);
)
while(l)
intflag;
LINUX网辂编程
if((flag=read(new_fd,buf,1024))<0)
(
printf(nReadingdataerror!\nn);
break;
)
if(flag==0)
(
printf(MEndingcurrentconnection!\nH);
break;
)
else
LINUX网辂编程
prmtf(—>%s\n\buf);
if(strstr(buf,nexitn)){
printf(HEndingcurrentconnection!\nM);
break;
)
//if(strstr(buf,nbyen))
//{
//close(new_fd);
//close(sockfd);
//exit(O);
//)
LINUX网辂编程
}//else
}//while
close(new_fd);
}//while
close(sockfd);
exit(O);
LINUX网辂编程
客户端(client.standard.c):
#include<stdio.h>
#include<netdb.h>
#include<sys/socket.h>
#include<sys/errno.h>
intmain(intargc,char*argv[]){
LINUX网辂编程
mtsockfd;
charbuffer[1024];
structsockaddr_inserver_addr;
structhostent*host;
intportnumber,nbytes;
if(argc!=3)
(
fprintf(stderr,HUsage:%shostnameportnumber\a\n1,
argv[0]);
exit(l);
LINUX网辂编程
if((host=gethostbyname(argv[l]))==NULL)
(
fprintf(stderr,"Gethostnameerror\nM);
exit(l);
)
if((portnumber=atoi(argv[2]))<0)
(
n1
fprintf(stderr9Usage:%shostnameportnumber\a\n*,
argv[0]);
exit(l);
LINUX网辂编程
/*客户程序开始建立sockfd描述符*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
(
nf
fprintf(stderr9SocketError:%s\a\n\strerror(errno));
exit(l);
)
/*客户程序填充服务端的资料*/
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((structin_addr*)host->h_addr);
LINUX网辂编程
/*客户程序发起连接请求*/
if(connect(sockfd,(structsockaddr*)(&server_addr),
sizeof(structsockaddr))==-1)
(
nn
fprintf(stderr,ConnectError:%s\a\n9strerror(errno));
exit(l);
)
/*连接成功了*/
if((nbytes=read(sockfd,buffer,1024))==-1)
LINUX网辂编程
fprintf(stderr,HReadError:%s\n,\strerror(errno));
exit(l);
)
buffer[nbytes]=**\0n;
Mn
printf(Ihavereceived:%s\n9buffer);
while(l)
gets(buffer);
if(wri阳sockfd,buffer,sizeof(buffer))<0)
printf(Msendingdataerror!\nn);
LINUX网辂编程
if(strstr(buffer,exitn))
break;
)
/*结束通信*/
close(sockfd);
exit(O);
LINUX网辂编程
1.8完整的多进程套接字通信程序
月艮务器端(server-advance.c):
#include<stdio.h>
#include<errno.h>
#include<netdb.h>
#include<sys/socket.h>
//maybeusebygetpid()andgetppid()
#include<unistd.h>
intmain(intargc,char*argv[])
LINUX网辂编程
intsockfd,new_fd;
structsockaddr_inserver_addr;
structsockaddr_inclient_addr;
intsin_size,portnumber;
charhello[]=nHello!AreYouFine?'';
charbuf[1024];
intflag;
if(argc!=2)
M11
fprintf(stderr9Usage:%sportnumber\a\n,argv[0]);
exit(l);
LINUX网辂编程
if((portnumber=atoi(argv[l]))<0)
(
fprintf(stderr,nUsage:%sportnumber\a\n1*,argv[O]);
exit(l);
)
/*服务器端开始建立socket描述符*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
nM
fprintf(stderr,Socketerror:%s\n\a9strerror(errno));
exit(l);
LINUX网辂编程
/*服务器立而填充sockaddr结构*/
bzero(&server_addr,sizeof(structsockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
/*捆绑sockfd描述符*/
if(bind(sockfd,(structsockaddr*)(&server_addr),sizeof(struct
sockaddr))=="l)
(
fprintf(stderr,HBmderror:%s\n\af\strerror(errno));
exit(l);
)
LINUX网辂编程
/*监听sockfd描述符*/
if(listen(sockfd95)==-1)
(
fprintf(stderr,HListenerror:%s\n\af\strerror(errno));
exit(l);
)
//printfmyPIDnum
printf(nStartingServeratport:%dSuccess.MyPIDis:%d\n
AcceptingRequest…\n'',portnumber,getpid());
while(l)
/*服务器阻塞,直到客户程序建立连接*/
LINUX网辂编程
sin_size=sizeof(structsockaddr_in);
if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),
&sin_size))=="l)
(
fprintf(stderr,nAc
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 与个人签订采购合同范例
- 餐饮家具卸货合同范例
- 合伙经营物流合同模板
- 软件对接合同范例
- 2024年度企业咨询服务合同及商业机密保护协议2篇
- 小区园林拆除合同模板
- 电力土建施工合同范例
- 2024年房地产开发商与建筑设计院合作合同
- 青浦专业叉车租赁合同范例
- 2024年度幼儿舞蹈表演活动策划合同3篇
- 审计实务中建筑工程转包违规分包问题研究
- 应急响应流程各种图表
- 印染厂染色安全生产注意事项
- 涂色画简笔画已排可直接打印涂色
- 工作证明(通用)
- 政协提案关于加强企业诚信建设的建议
- SPC&CPK 超全EXCEL模板
- 化工设计说明书
- 部编本语文八年级上全册文言文课下注释
- 德力西系列变频器说明书
- UleadGifAnimator教程
评论
0/150
提交评论