《计算机组网实验教程》课件第11章_第1页
《计算机组网实验教程》课件第11章_第2页
《计算机组网实验教程》课件第11章_第3页
《计算机组网实验教程》课件第11章_第4页
《计算机组网实验教程》课件第11章_第5页
已阅读5页,还剩86页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第11章网络编程11.1套接字概述11.2Winsock函数11.3通信程序设计11.4基于TCP的端口扫描程序

11.1套

11.1.1WindowsSocket简介在20世纪80年代,美国加利福尼亚大学Berkeley(伯克利)分校在BSDUNIX系统下实现了通信协议TCP/IP的开发接口Socket(套接字),并很快流行起来,成为主要应用于BSDUNIX系统的通用网络编程接口。Winsock(WindowsSocket的简称)就是由BerkeleySocket演变而来的,它定义了Windows系统下的网络编程接口。

WindowsSocket规范提供给应用程序开发者一套简单的API,并让各个网络软件供应商共同遵守。该规范还定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。遵守这套WindowsSocket规范的网络软件,我们称之为WindowsSocket兼容的,而WindowsSocket兼容实现的提供者,我们称之为WindowsSocket提供者。一个网络软件实现WindowsSocket规范才能做到与WindowsSocket兼容。任何能够与WindowsSocket兼容的应用程序就被认为是具有WindowsSocket接口。WindowsSocket规范定义并记录了如何使用API与TCP/IP协议连接,应用程序调用WindowsSocket的API实现相互之间的通信,WindowsSocket又利用下层的网络通信协议功能和操作系统调用实现实际的通信工作。

11.1.2Winsock基本概念

1.端口网络中可以被命名和寻址的通信端口,是操作系统可分配的一种资源。按照OSI体系七层结构的描述,传输层与网络层在功能上的最大区别是传输层提供进程间通信的能力,即网络通信最终是在两个应用进程之间交互的,可以用IP地址来标示一台主机,而一台主机上往往存在多个应用进程,每一个应用进程也需要一个惟一的标识符。为此,TCP/IP协议提出了协议端口(ProtocolPort,简称端口)的概念,用于标识通信的应用进程。端口是一种抽象的软件结构(包括一些数据结构和I/O缓冲区)。应用进程通过系统调用与某端口建立连接后,传输层传给该端口的数据都被相应进程所接收,且相应进程发给传输层的数据通过该端口输出。在TCP/IP协议的实现中,端口操作类似于一般的I/O操作,进程获取一个端口,相当于获取本地惟一的I/O文件,可以用一般的读写原语访问。类似于文件描述符,每个端口都拥有一个称为端口号(PortNumber)的整数型标识符,用于区别不同端口。端口号的分配是一个重要问题,有两种基本分配方式:第一种叫全局分配,这是一种集中控制方式,由一个公认的中央机构根据用户需要进行统一分配,并将结果公布于众;

第二种是本地分配,又称动态连接,即进程需要访问传输层服务时,向本地操作系统提出申请,操作系统返回一个本地惟一的端口号,进程再通过合适的系统调用将自己与该端口号联系起来(绑定)。TCP/IP端口号的分配中综合了上述两种方式。TCP/IP将端口号分为两部分,少量的作为保留端口,以全局方式分配给服务进程。因此,每一个标准服务器进程都拥有一个全局公认的端口号(即周知端口,Well-KnownPort),即使在不同机器上,其端口号也相同。剩余的为自由端口,以本地方式进行分配。TCP和UDP均规定,小于1024的端口号才能作为保留端口。

2.套接字套接字是支持TCP/IP协议网络通信的基本操作单元。一个套接字是通信的一个端点,通常由一个与进程相关联的端口号以及主机的IP地址来标识。一个正在被使用的套接字有自己的类型和与其相关的进程,相互交互的两个进程通过各自的套接字进行通信。套接字存在于通信域(即地址族)中,是为了处理线程间通信而引进的抽象概念,一般情况下通信发生在同一通信域中的套接字之间,Winsock规范只支持单一的通信域,即Internet域。套接字可以根据通信的性质进行分类,并且按这种方法可分为两类:流式套接字和数据报套接字。应用程序一般仅在同一类套接字之间通信。

3.字节顺序不同的计算机使用不同的字节顺序存储数据。Intel处理器使用的字节顺序称为“Little-Endian”,即高字节在前,低字节在后;而Internet网络的字节顺序称为“Big-Endian”,它和Little-Endian的字节顺序是相反的。因此用户在使用时要特别注意字节的正确顺序。任何Winsock函数对IP地址和端口号的使用均是按照网络字节顺序组织的。在很多情况下,用户要在本地主机字节顺序和网络字节顺序之间进行转换,此时应该使用WinsockAPI中标准的转换函数,而不要自己编写转换代码。因为将来的Winsock实现有可能在本地主机字节顺序和网络字节顺序相同的机器上运行,因此只有使用标准的转换函数应用程序才可以移植。为了统一起见,通常无论本地主机字节顺序与网络字节顺序是否一致,在进行网络传输时都将本地主机字节顺序转换成网络字节顺序,在接收时再将网络字节顺序转换成本地主机字节顺序。

4.阻塞和非阻塞套接字可以处于阻塞模式或非阻塞模式。在阻塞模式下,I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回,这就意味着任一个线程在某一时刻只能执行一个I/O操作,而且应用程序很难同时通过多个建好连接的套接字进行通信;而非阻塞模式下没有这样的要求,Winsock函数无论如何都会返回并交出程序的控制权。在默认的情况下,套接字处于阻塞模式。

5.错误检查与控制要成功编写Winsock应用程序,错误检查和控制是至关重要的。因为对于Winsock函数而言,返回错误值是非常常见的。但是多数情况下,这些错误是无关紧要的,通信仍可在套接字上进行。返回的错误值可以有多种,但最常见的错误是SOCK_ERROR。SOCK_ERROR的值是-1,但这个值仅仅是函数的返回值,由此还不能看出错误的具体类型。要想获得具体的错误代码,还必须在调用Winsock函数之后,用WSAGetLastError函数来获得错误代码,这个错误代码能明确地表明产生错误的原因。该函数的定义为

int WSAGetLastError(void);11.2Winsock函数

11.2.1Winsock初始化函数

1.WSAStartup()

WSAStartup()函数用于在应用程序中初始化WindowsSocketsDLL,只有此函数调用成功后,应用程序才可以再调用其他WindowsSocketsDLL中的API函数。此函数若调用成功,则返回0,否则返回错误。

函数原型:intPASCALFARWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);参数说明:wVersionRequested:用于指定WindowsSocketsAPI版本。lpWSAData:是指向WSADATA结构的指针,WSAData结构用来存储系统返回的关于Winsock的信息。

2.WSACleanup()

WSACleanup()函数用于结束对WinsockDLL的使用,并释放资源,该函数不带任何参数,若调用成功则返回0,否则返回错误。调用格式:intWSACleanup(void);11.2.2基本Winsock函数

1.socket()初始化Winsock的动态链接库后,可以调用socket()函数来建立Socket,并定义此Socket所使用的通信协议。此函数调用成功返回Socket对象,失败则返回INVALID_SOCKET(调用WSAGetLastError()函数可得知原因,所有Winsock的函数都可以使用这个函数来获取失败的原因)。

函数原型:

SOCKETsocket(intaf,inttype,intprotocol);

参数说明:

af:指协议簇,对于TCP/IP协议,为AF_INET;

type:Socket的类型,如果是TCP则为SOCK_STREAM,是UDP则为SOCK_DGRAM;

protocol:使用的通信协议,可以指定为IPPROTO_IP、IPPROTO_UDP等,如果使用者不指定,默认为0,表示为TCP/IP协议,例如:

s=socket(AF_INET,SOCK_STREAM,0);注释:WinsockAPI是建立在套接字基础上的。套接字从实质上讲,就是一个指向传输提供者的句柄。Win32中套接字不同于其他文件描述符,它是一个独立的类型——Socket。

2.bind()把定义的Socket同一个地址绑定,要调用bind()函数,该函数调用成功返回0,否则返回SOCKET_ERROR。函数原型:

intbind(SOCKETs,conststructsockaddrFAR

*name,intnamelen);参数说明:

s:Socket对象名;

name:指向类型为structsockaddr的套接字地址结构的指针。

namelen:name的长度。

structsockaddr定义如下:structsockaddr{

unsignedshortsa_family;

charsa_data[14];

};

sa_family地址族,一般为AF_INET;sa_data则包含该Socket的IP地址和端口号。

另外还有一种结构类型定义如下:

structsockaddr_in{

shortintsin_family; unsignedshortintsin_port;

structin_addrsin_addr; unsignedcharsin_zero[8];

};

这个结构使用更为方便。sin_family通常被赋AF_INET;sin_port指端口号;sin_addr填入IP地址;sin_zero,用来将sockaddr_in结构的长度填充到与structsockaddr同样的长度。如果使用者不在意地址或端口的值,那么可以设定sin_addr为INADDR_ANY,sin_port为0,WindowsSocket会自动设置一个地址及端口值(1024~5000之间的值),通常在客户端编程时采用。此后可以调用getsockname()函数来获知其被设定的值。

注释:bind()函数将指定的套接字同一个已知地址和端口绑定在一起。一旦出错,bind()函数会返回SOCKET_ERROR。对bind()函数来说,最常见的错误是WSAEADDRINUSE,表示另一个进程已经同本地IP和端口号绑定在一起,或者本地IP和端口号处于TIME_WAIT状态。假如对一个已绑定套接字调用bind()函数,便会返回WSAEFAULT错误。

3.listen()

listen()函数使Socket进入监听状态,并设定可以建立的最大连接数。该函数调用成功返回0,否则返回SOCKET_ERROR。函数原型:

intlisten(SOCKETs,intbacklog);参数说明:

s:需要建立监听的Socket。

backlog:正在等待连接的最大队列长度;在服务器端该参数的设置非常重要,因为可能同时有多个客户端同时向服务器发出连接请求。客户端的请求会存放在服务器的“等待处理”队列中。服务器端的Socket调用完listen()函数后,如果此时客户端调用connect()函数提出连接请求,则客户端的请求信息会存放在服务器端的“等待处理”队列中。接下来,服务器端必须调用accept()函数,从“等待处理”队列中提取客户端的请求信息进行处理,这样服务器端和客户端才算正式完成了通信程序的连接动作。

4.accept()当客户提出连接请求时,为了使服务器端接受客户端的连接请求,就要使用accept()函数,该函数新建一个Socket与客户端的Socket相通,原先监听的Socket继续进入监听状态,等待其他连接要求。该函数调用成功,返回一个新产生的Socket对象,否则返回INVALID_SOCKET。函数原型:

SOCKETaccept(SCOKETs,structsockaddrFAR*addr,intFAR*addrlen);参数说明:

s:已经建立的、处在监听模式的Socket;

addr:存放请求连接的客户端的地址;

addrlen:addr的长度。注释:accept()函数返回一个新的套接字,其中addr参数变量中会包含发出连接请求的客户机的地址信息,而addrlen参数指出该结构的长度。对于该客户端后续的所有操作,都应使用这个新套接字。至于原来那个套接字,它仍然用于接受其他客户端的连接,而且仍然处于侦听状态。如果有新的连接请求,可以通过accept()函数的再一次调用而获得接受;否则,服务进程被阻塞。如果调用accept()函数出错,会返回INVALID_SOCKET,如果要获得具体错误代码可以调用WSAGetLastError()函数。

5.Connect()客户端的Socket使用connect()函数来提出与服务器端的Socket建立连接的申请,函数调用成功返回0,否则返回SOCKET_ERROR。函数原型:

intconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen);参数说明:

s:标识一个还未连接的Socket;

name:Socket想要连接的对方的地址信息;

namelen:name的长度。

6.closesocket()结束服务器和客户端的通信连接是很简单的,这一过程可以由服务器或客户机的任一端启动,只要调用closesocket()函数即可,而要关闭Server端监听状态的Socket,同样也是利用此函数。另外,与程序启动时调用WSAStartup()函数相对应,程序结束前,需要调用

WSACleanup()函数来通知WinsockStack释放Socket所占用的资源。这两个函数都是调用成功返回0,否则返回SOCKET_ERROR。

函数原型:

intclosesocket(SOCKETs);参数说明:

s:准备关闭的Socket。注释:closesocket()函数的调用会释放套接字句柄s,以后再对该套接字的访问均以WSAENOTSOCK错误返回。

7.shutdown()

shutdown()函数用于禁止在一个套接字上进行数据的接收与发送。函数原型:

intshutdown(SOCKETs,inthow);参数说明:

s:被禁止的Socket。

how:标志,用于描述禁止哪些操作。可能的取值是SD_REVIEVE,SD_SEND或SD_BOTH。SD_RECEIVE表示不允许再调用接收函数;SD_SEND表示不允许再调用发送函数。SD_BOTH表示取消连接两端的收发操作。

11.2.3数据传输函数

1.send()

send()函数用于在套接字连接成功的情况下发送数据。函数原型:

intsend(SOCKETs,constcharFAR*buf,intlen,intflags);参数说明:

s:已连接的Socket;

buf:存放要传送资料的缓冲区的地址;

len:buf的长度;

flags:此函数被调用的方式。

2.recv()

recv()函数用于在套接字连接成功的情况下接收数据。函数原型:

intrecv (SOCKETs,charFAR*buf,intlen,intflags);参数说明:

s:已连接的Socket;

buf:存放接收到的资料的缓冲区;

len:buf的长度;

flags:此函数被调用的方式。

3.sendto()

sendto()函数用于向一指定目的地发送数据。函数原型:

intsendto(SOCKETs,constcharFAR*buf,intlen,intflags,conststructsockaddrFAR*to,

inttolen);参数说明:

s:表示Socket。

buf:待发送数据的缓冲区。

len:待发送的字节数。

flags:调用方式标志位,可以为0、MSG_DONTROUTE、MSG_OOB或这些标志按位“或”运算的结果。MSG_DONTROUTE用于告知IP协议,目的主机在本地网络,没有必要查找路由表,可以将报文直接传送给目的地址。这个标志一般用在网络诊断和路由程序里面。MSG_OOB标志数据应被带外发送,带外数据指TCP紧急数据。

to:指针,指向目的套接字的地址结构。

tolen:to所指地址结构的长度。

4.recvfrom()

recvfrom()函数用于接收一个数据报并保存、发送地方地址结构信息。函数原型:

intrecvfrom(SOCKETs,constcharFAR*buf,intlen,intflags,structsockaddrFAR*from,intFAR*fromlen);参数说明:

s:表示Socket。

buf:接收数据缓冲区。

len:准备接收的字节数或buf缓冲区长度。

flags:调用操作方式,可以为0、MSG_PEEK、MSG_OOB或这些标志按位“或”运算的结果。其中:0表示无特殊行为;MSG_PEEK使有用的数据复制到提供的接收端缓冲区内,但没有把它从系统缓冲区中删除。

from:指针,发送方套接字地址结构信息。

fromlen:指针,指向SOCKADDR地址结构的长度。注释:recvfrom()函数调用成功则返回实际接收的字节数,发生错误时,返回-1。

11.2.4网络信息查询函数

1.getpeername()

getpeername()获取通信对方的套接字地址结构信息。函数原型:

intgetpeername(SOCKETs,structsockaddrFAR*name,intFAR*namelen);参数说明:

s:已连接的socket;

name:通信对方的套接字地址结构;

namelen:指向地址结构长度的指针。

2.getsockname()

getsockname()函数是getpeer的对应函数,用于获取指定套接字的本地地址结构信息。函数原型::

intgetsockname(SOCKETs,structsockaddrFAR*name,intFAR*namelen);参数说明:

s:标示一个已绑定套接字的句柄;

name:套接字的地址结构;

namelen:指向地址结构长度的指针。

3.gethostbyname()

gethostbyname()函数用于返回对应于给定主机名的主机信息,在已知主机名并打算查找其IP时,可以使用该函数。函数原型::

structhostentFAR*gethostbyname(constcharFAR*name);参数说明:

name:指向主机名的指针。

4.gethostbyaddr()

gethostbyaddr()函数用于返回对应于给定IP地址的主机信息,在已知IP地址并打算查找其主机名时,可以使用该函数。函数原型::

structhostentFAR*gethostbyaddr(constcharFAR*addr,intlen,inttype);参数说明:

addr:指向网络字节顺序IP地址的指针;

len:地址的长度,在AF_INET类型地址为4;

type:地址类型,应为AF_INET。注释:gethostbyaddr()函数返回对应于给定IP地址的包含主机名字和地址信息的hostent结构指针。

5.getservbyname()

getservbyname()函数用于返回对应于给定服务名字和协议名的相关服务信息,在已知服务的情况下,要查找其对应的端口号,可以使用该函数。函数原型::

sturctserventFAR*getservbyname(constcharFAR*name,constcharFAR*proto);参数说明:

name:指向服务名的指针;

proto:指向协议名的指针。

11.3通信程序设计

11.3.1客户/服务器模式网络应用进程自身功能的实现往往是通过存在于不同主机中的多个应用进程间的通信和协同工作来完成的。客户和服务器是通信中涉及的两个应用进程。客户是主动请求服务的一方,服务器是接受请求,提供服务的一方。一个服务器进程通常在一个众所周知的端口监听是否有客户的请求到达,即服务器进程总是处于等待状态,直到某一客户进程发出的请求到来,此时,服务器进程开始响应客户的请求,为客户提供服务。

11.3.2面向连接服务和无连接服务

1.面向连接服务在面向连接的服务中,通信双方在进行数据交换之前,必须先建立一条连接,数据传输结束后释放连接。面向连接服务确定通信双方之间存在连接,保证通信双方处于活动状态,且数据是按序传送的,从而保证了数据通信的可靠性。面向连接的服务只支持点到点通信。面向连接服务比较适合于在一段时间间隔内向同一目的地发送大量报文的情况。对于发送少量零星报文的情况,连接的建立和释放所带来的开销就显得过大了。

2.无连接服务在无连接服务中,发送方可以随时发送数据而无需事先建立连接。发送方并不清楚接收方的状态以及网络中是否有路径可以到达接收方,因此数据包可能会丢失,且由于前后数据的传输路径可能不同,因此无法保证数据按序到达目的地。这些问题留给应用程序自行解决。无连接服务可以实现点对点通信以及点对多点通信,适合于少量零星报文的传送以及对传输的可靠性没有要求的应用。

11.3.3流式套接字和数据报套接字

1.流式套接字流式套接字提供双向、有序、无重复并且无报文段边界的数据流服务,即一种可靠的面向连接的数据传输方法。流式套接字使用TCP协议,当要发送大批量数据或要保证数据按顺序、无重复地到达目的地时,需要使用流式套接字。流式套接字是面向连接的,因此服务器进程和客户进程在通信前必须建立各自的套接字,并进行连接,然后才能对相应的套接字进行“读”、“写”操作,实现数据的传输。流式套接字编程的流程如图11.1所示。

图11.1流式套接字编程流程

2.数据报套接字数据报套接字支持双向通信,提供不可靠的、无连接的数据报通信方式。也就是说,一个从数据报套接字接收信息的进程可能发现信息重复、接收顺序与发送顺序不同等情况,接收程序应具有处理这些情况的能力。数据报套接字使用UDP协议,具有向多个目标地址发送广播数据报的能力。数据报套接字是无连接的,它不能保证接收端是否正在等待接收,因此,数据报并不十分可靠,需要应用程序负责管理数据报的排序和可靠性。数据报套接字的编程过程比流式套接字简单。数据报套接字编程使用的基本函数与流式套接字使用的函数一样,而数据传输函数则不同:发送数据使用sendto()函数,接收数据使用recyfrom()函数。

图11.2数据报套接字编程流程11.3.4实验

面向连接通信程序1.实验要求(1)掌握网络编程的概念。(2)通过程序设计理解流式套接字编程流程。2.实验设备计算机1台。

3.实验过程和主要步骤

(1)服务器。服务器程序先运行,首先初始化Winsock,然后创建套接字,在2000端口上进行绑定,接着在2000端口上进行侦听,并进入等待连接状态。当与客户建立连接后,服务器接收客户发来的文件,并将其放在相应位置。以上过程的实现程序如下:#include"stdafx.h"#include"winsock.h"#include"windows.h"#include"stdio.h"#pragmacomment(lib,"wsock32.lib")#defineRECV_PORT2000#defineSEND_PORT3000#defineMAX_FILESIZE32*1024

SOCKETsock,sock1;sockaddr_inServerAddr;sockaddr_inClientAddr;

structFiledata{

charffname[30]; charffdata[MAX_FILESIZE]; intlen;}DataPacket;intAddrlen;

DWORDStartSock(){ WSADATAWSAData; if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0) {

printf("sockinitfail!\n"); return(-1); } return(1);}

DWORDCreatesocket(){ sock=socket(AF_INET,SOCK_STREAM,0); if(sock==SOCKET_ERROR) {printf("sockcreatefail!\n");

WSACleanup();

return(-1);

} ServerAddr.sin_family=AF_INET; ServerAddr.sin_addr.s_addr=htonl(INADDR_ANY);

ServerAddr.sin_port=htons(RECV_PORT);

if(bind(sock,(structsockaddrFAR*)&ServerAddr,sizeof(ServerAddr))==SOCKET_ERROR) { printf("bindistheerror)"); return(-1); } return(1);}DWORDWriteFile(char*fname,char*fdata,intflen){ inti; FILE*fp; fp=fopen(fname,"w"); if(fp==NULL) { printf("cannotopenthisifle\n"); } i=0; for(i=0;i<flen;i++) { fputc(fdata[i],fp);

} fclose(fp); return(1);}

DWORDConnectProcess(){ Addrlen=sizeof(sockaddr_in); if(listen(sock,5)<0) { printf("Listenerror"); return(-1); } printf("listening...\n");

for(;;) {

sock1=accept(sock,(structsockaddrFAR*)&ClientAddr,

&Addrlen);

if(sock1!=-1)

{ printf("Acceptedaconnection!\n");

for(;;) { memset(DataPacket.ffname,0,30); memset(DataPacket.ffdata,0,MAX_FILESIZE);

DataPacket.len=0;

if(recv(sock1,(char*)&DataPacket,

sizeof(DataPacket),0)<=0)

{

break;

}

printf("Hasrecievedfile:%s,lengthis%d",

DataPacket.ffname,DataPacket.len);

WriteFile(DataPacket.ffname,DataPacket.ffdata,DataPacket.len);

printf("\n");

}

}

}}intmain(intargc,char*argv[]){ if(StartSock()==-1) return(-1); if(Createsocket()==-1)

return(-1); if(ConnectProcess()==-1) return(-1);return(1);}

(2)客户。客户程序后运行,同样先初始化Winsock,然后创建套接字,接着尝试与服务器连接,连接建立后,显示“连接成功”,然后客户端提示输入要发送的文件名,该文件名可以带路径。以上过程的实现程序如下:#include"stdafx.h"#include"winsock.h"#include"windows.h"#include"stdio.h"#include"stdlib.h"#include"string.h"#pragmacomment(lib,"wsock32.lib")#defineRECV_PORT2000#defineSEND_PORT3000#defineMAX_FILESIZE32*1024

SOCKETsock;sockaddr_inServerAddr;

structFiledata{

charffname[30]; charffdata[MAX_FILESIZE]; intlen;}DataPacket;

DWORDStartSock(){ WSADATAWSAData; if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0) { printf("sockinitfail!\n"); return(-1); }

ServerAddr.sin_family=AF_INET; ServerAddr.sin_addr.s_addr=inet_addr("127.0.0.1");

ServerAddr.sin_port=htons(RECV_PORT);

return(1);}

DWORDCreatesocket(){ sock=socket(AF_INET,SOCK_STREAM,0); if(sock==SOCKET_ERROR) {

printf("Sockcreatefail!\n"); WSACleanup(); return(-1); } return(1);}voidCallServer(){ Createsocket(); while(connect(sock,(structsockaddr*)&ServerAddr, sizeof(ServerAddr))==SOCKET_ERROR) {

printf("Connect...\n"); }}

DWORDGetFile(char*fname){ FILE*fp; intFilesize; inti; intcount,total=0; charbuffer[100]; charSenddata[MAX_FILESIZE]; fp=fopen(fname,"r");

if(fp==NULL) { printf("cannotopenthisfile\n"); return(0); } i=0; Filesize=0; memset(Senddata,0,MAX_FILESIZE); while(!feof(fp)) { count=fread(buffer,sizeof(char),100,fp); if(ferror(fp)) {

printf("ReadFileerror"); break; } Filesize+=count; if(Filesize>MAX_FILESIZE) { printf("yourfileistoobig\n"); fclose(fp); return(0); } memcpy(&Senddata[i],buffer,count); i+=count; }

fclose(fp); Senddata[i]='\0'; strcpy(DataPacket.ffname,fname); memcpy(DataPacket.ffdata,Senddata,Filesize); DataPacket.len=Filesize; printf("%s%d\n",DataPacket.ffname,DataPacket.len); return(1);

}

DWORDTCPSendPacket(structFiledataPacket)

{

intlength; length=send(sock,(char*)&Packet,sizeof(DataPacket),0); if(length<=0) { printf("sendFiledataerror!\n"); closesocket(sock); WSACleanup(); return(-1); } return(1);

}

intmain(intargc,char*argv[])

{ charsendfilename[30]; StartSock(); CallServer(); printf("connectok!\n"); while(1) {

printf("pleaseinputyourfilenamerosend(exit-退出)\n");

scanf("%s",sendfilename);

if(strcmp(sendfilename,"exit")==0) break;

if(GetFile(sendfilename)==0) continue;TCPSendPacket(DataPacket); } closesocket(sock); return(0);}

(3)读者可以结合运行结果,分析理解通信流程,并在此基础上完成以下工作:①

修改程序参数实现客户/服务器两端分别在不同计算机上通信。②

修改程序,使得当服务器关闭后,客户/服务器间的连接断开,在客户端提示“连接断开,请重新连接”等信息。

11.4基于TCP的端口扫描程序

11.4.1端口扫描的原理端口的作用就是让应用层的各种应用进程能将其数据通过端口向下交付给传输层,以及让传输层将其报文段中的数据向上通过端口交付给应用层相应的进程。从这个意义上讲,端口是用来标识应用层的进程。为了知道对方计算机相应的端口是否在工作,可以采用TCP协议,通过Socket试图连接对方计算机的某端口,和该端口建立连接。根据返回结果,判断连接是否成功。如果连接建立成功,则说明对方开放了该端口,否则,对方没有开放该端口。

11.4.2编程所用类库介绍

1.CAsyncSockets类的编程简介(1)构造CAsyncSockets。①

服务器端:CAsyncsocketssrvrsocket;intnPort=21;//nPort是一个端口地址,21只是一个例子srvrsocket.Create(nPort,SOCK_DGRAM);//数据报类型②

客户端:Csocketclentsocket;clientsocket.Create();

(2)调用对象的成员函数来执行通信任务。①

服务器端:

Srvsocket.Listen()

//重载OnAccept()函数处理FD_ACCEPT事件,调用Accept()函数

Srvsocket.OnAccept();②

客户端:

Clientsocket.Connect(StrAddr,nPort);

(3)当通信结束时,删除CasyncSocket对象。如果使用new运算符在堆中构造对象,必须调用delete运算符。

2.Csocket类编程简介

1)Csocket类相关成员函数

Csocket类相关成员函数实际上是从CasyncSocket类继承的。

BOOLCreate(UINTnSocketPort=0,int

nSocketType=SOCK_STREAM,longlEvent=FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE,LPCTRTRlpszSocketAddress=NULL):该函数的作用是建立套接字。

BOOLBind(UINTnSocketPort,LPCTSTRlpszSocketAddress=NULL):该函数的作用是将Socket端口与网络地址绑定起来。

BOOLListen(intnConnectionBacklog):该函数的作用是等待Socket请求,nConnection-Backlog表示等待队列的长度,默认值为5。

VirtualBOOLAccept(CasyncSocket&rConnectedSocket,SOCKADDR*lpSockAddr=NULL,int*lpSockAddrLen=NULL):该函数的作用是取得队列上第一个连接请求并建立一个具有与Sock

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论