网路编程学习(四)

网路编程学习(四)

原始套接字

原始套接字,指在传输层下面使用的套接字。流式套接字和数据报套接字这两种套接字工作在传输层,主要为应用层的应用程序提供服务,并且在接收和发送时只能操作数据部分,而不能对IP首部或TCP和UDP首部进行操作,通常把这两种套接字称为标准套接字。

但是,如果我们开发的是更底层的应用,比如发送一个自定义的IP包、UDP包、TCP包或ICMP包,捕获所有经过本机网卡的数据包,伪装本机的IP,想要操作IP首部或传输层协议首部,等等,这些功能对于这两种套接字就无能为力了。这些功能需要使用另一种套接字来实现,这种套接字叫作原始套接字,功能更强大,更底层。

原始套接字可以在链路层收发数据帧。

原始套接字可以自动组装数据包(伪装本地IP和本地MAC),可以接收本机网卡上所有的数据帧(数据包)。另外,必须在管理员权限下才能使用原始套接字。

原始套接字的使用

1、根据需要设置套接字的选项,在默认的情况下,IP自动填充数据包的首部。如果需要自己填写IP数据包首部时,可以在原始套接字上设置套接字选项IP_HDRINCL

#pragma once
typedef struct IP {
	//unsigned char version;//4位IP版本号
	unsigned char headLen;//4位首部长度
	unsigned char serviceType;//8位服务类型
	unsigned short totalLen;//16位总长度
	unsigned short identifier;//16位标识符
	unsigned short flags;//3位标志位
	//unsigned short fragOffset;//13位片偏移
	unsigned char timeToLive;//8位生存时间
	unsigned char protocal;//8位协议
	unsigned short headCheckSum;//16位首部校验和
	unsigned int sourceAddr;//32位源地址
	unsigned int destinAddr;//32位目的地址
}IPHeader;

//TCP报文格式
typedef struct TCP {
	unsigned short sourcePort;//16位源端口号
	unsigned short destinPort;//16位目的端口号
	unsigned int seqNum;//32位序列号
	unsigned int ackNum;//32位确认号
	unsigned char headLen;//4位首部长度
	//unsigned char resv;//4位保留字
	unsigned char flags;//8位标志位
	unsigned short winSize;//16位窗口大小
	unsigned short checkNum;//16位校验和
	unsigned short urgPointer;//16位紧急指针
}TCPHeader;


#include <WinSock2.h>
#include <iostream>
#include "define.h"
using namespace std;

#pragma comment(lib,"ws2_32.lib")

#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)//将SIO_RCVALL定义为_WSAIOW(IOC_VENDOR,1)

int main() {

	IP* ip;
	TCP* tcp;

	WSADATA wsd;
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) {
		cout << "error:" << WSAGetLastError() << endl;
		return -1;
	}

	SOCKET sock;
	sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
/*SOCKET socket(
int af,
int type,
int protocol,
);

  1   参数protocol用来指明所要接收的协议包,如果是象IPPROTO_TCP(6)这种非0、非255的协议,当操作系统内核碰到ip头中protocol域和创建socket所使用参数protocol相同的IP包,就会交给这个raw   socket来处理,因此,一般来说,要想接收什么样的数据包,就应该在参数protocol里来指定相应的协议。当内核向此raw   socket交付数据包的时候,是包括整个IP头的,并且已经是重组好的IP包。
 
2   如果protocol是IPPROTO_RAW(255),这时候,这个socket只能用来发送IP包,而不能接收任何的数据。发送的数据需要自己填充IP包头,并且自己计算校验和。  
3   对于protocol为0(IPPROTO_IP)的raw   socket。用于接收任何的IP数据包。其中的校验和和协议由自己完成。*/
	if (sock == INVALID_SOCKET) {
		cout << "error:" << WSAGetLastError() << endl;
		closesocket(sock);
		WSACleanup();
		return -2;
	}
	BOOL flag = true;
	if (setsockopt(sock, IPPROTO_IP, 2, (char*)&flag, sizeof(flag)) == SOCKET_ERROR) {
		cout << "error:" << WSAGetLastError() << endl;
		closesocket(sock);
		WSACleanup();
		return -3;
	}
	/*
	创建了原始套接字后,就要设置套接字选项,这要通过setsocketopt函数来实现,setsocketopt函数的声明如下:
	int setsocketopt (SOCKET s,int level,int optname,const char FAR *optval,int optlen );
	参数s是标识套接口的描述字,要注意的是选项对这个套接字必须是有效的。
	参数Level表明选项定义的层次,对TCP/IP协议族而言,支持SOL_SOCKET、IPPROTO_IP和IPPROTO_TCP层次。
	参数Optname是需要设置的选项名,这些选项名是在Winsock头文件内定义的常数值。
	参数optval是一个指针,它指向存放选项值的缓冲区。
	参数optlen指示optval缓冲区的长度
	 获取或者设置与某个套接字关联的选 项。选项可能存在于多层协议中,它们总会出现在最上面的套接字层。当操作套接字选项时,选项位于的层和选项的名称必须给出。为了操作套接字层的选项,应该 将层的值指定为SOL_SOCKET。为了操作其它层的选项,控制选项的合适协议号必须给出。例如,为了表示一个选项由TCP协议解析,层应该设定为协议 号TCP。

用法:

#include <sys/types.h>
#include <sys/socket.h>

int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
参数:  
sock:将要被设置或者获取选项的套接字。
level:选项所在的协议层。
optname:需要访问的选项名。
optval:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向包含新选项值的缓冲。
optlen:对于getsockopt(),作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现选项的长度。

返回说明:

成功执行时,返回0。失败返回-1,errno被设为以下的某个值  
EBADF:sock不是有效的文件描述词
EFAULT:optval指向的内存并非有效的进程空间
EINVAL:在调用setsockopt()时,optlen无效
ENOPROTOOPT:指定的协议层不能识别选项
ENOTSOCK:sock描述的不是套接字

参数详细说明:

level指定控制套接字的层次.可以取三种值:
1)SOL_SOCKET:通用套接字选项.
2)IPPROTO_IP:IP选项.
3)IPPROTO_TCP:TCP选项. 
optname指定控制的方式(选项的名称),我们下面详细解释 
optval获得或者是设置套接字选项.根据选项名称的数据类型进行转换 

选项名称        说明                  数据类型
========================================================================
            SOL_SOCKET
------------------------------------------------------------------------
SO_BROADCAST      允许发送广播数据            int
SO_DEBUG        允许调试                int
SO_DONTROUTE      不查找路由               int
SO_ERROR        获得套接字错误             int
SO_KEEPALIVE      保持连接                int
SO_LINGER        延迟关闭连接              struct linger
SO_OOBINLINE      带外数据放入正常数据流         int
SO_RCVBUF        接收缓冲区大小             int
SO_SNDBUF        发送缓冲区大小             int
SO_RCVLOWAT       接收缓冲区下限             int
SO_SNDLOWAT       发送缓冲区下限             int
SO_RCVTIMEO       接收超时                struct timeval
SO_SNDTIMEO       发送超时                struct timeval
SO_REUSERADDR      允许重用本地地址和端口         int
SO_TYPE         获得套接字类型             int
SO_BSDCOMPAT      与BSD系统兼容              int
========================================================================
            IPPROTO_IP
------------------------------------------------------------------------
IP_HDRINCL       在数据包中包含IP首部          int
IP_OPTINOS       IP首部选项               int
IP_TOS         服务类型
IP_TTL         生存时间                int
========================================================================
            IPPRO_TCP
------------------------------------------------------------------------
TCP_MAXSEG       TCP最大数据段的大小           int
TCP_NODELAY       不使用Nagle算法             int
========================================================================

SO_RCVBUF和SO_SNDBUF每个套接口都有一个发送缓冲区和一个接收缓冲区,使用这两个套接口选项可以改变缺省缓冲区大小。

// 接收缓冲区
int nRecvBuf=32*1024;         //设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//发送缓冲区
int nSendBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
注意:

当设置TCP套接口接收缓冲区的大小时,函数调用顺序是很重要的,因为TCP的窗口规模选项是在建立连接时用SYN与对方互换得到的。对于客户,O_RCVBUF选项必须在connect之前设置;对于服务器,SO_RCVBUF选项必须在listen前设置。

结合原理说明:

 1.每个套接口都有一个发送缓冲区和一个接收缓冲区。 接收缓冲区被TCP和UDP用来将接收到的数据一直保存到由应用进程来读。 TCP:TCP通告另一端的窗口大小。 TCP套接口接收缓冲区不可能溢出,因为对方不允许发出超过所通告窗口大小的数据。 这就是TCP的流量控制,如果对方无视窗口大小而发出了超过窗口大小的数据,则接 收方TCP将丢弃它。 UDP:当接收到的数据报装不进套接口接收缓冲区时,此数据报就被丢弃。UDP是没有流量控制的;快的发送者可以很容易地就淹没慢的接收者,导致接收方的UDP丢弃数据报。
2.我们经常听说tcp协议的三次握手,但三次握手到底是什么,其细节是什么,为什么要这么做呢?
 第一次:客户端发送连接请求给服务器,服务器接收;
 第二次:服务器返回给客户端一个确认码,附带一个从服务器到客户端的连接请求,客户机接收,确认客户端到服务器的连接.
 第三次:客户机返回服务器上次发送请求的确认码,服务器接收,确认服务器到客户端的连接.
我们可以看到:
        1> tcp的每个连接都需要确认.
        2> 客户端到服务器和服务器到客户端的连接是独立的.
我们再想想tcp协议的特点:连接的,可靠的,全双工的,实际上tcp的三次握手正是为了保证这些特性的实现.

使用情况:

1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:

BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));
2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历TIME_WAIT的过程:

BOOL bDontLinger = FALSE;
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));
3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限:

int nNetTimeout=1000;//1秒
//发送时限
setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));
//接收时限
setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));
4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发:

// 接收缓冲区
int nRecvBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//发送缓冲区
int nSendBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
5. 如果在发送数据的时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响程序的性能:

int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));
6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区):

int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int));
7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:

BOOL bBroadcast=TRUE;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));
8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大)

BOOL bConditionalAccept=TRUE;
setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL));
9.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(),以前我们一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了,如何设置让程序满足具体应用的要求(即让没发完的数据发送出去后在关闭socket)?

struct linger {
u_short l_onoff;
u_short l_linger;
};
linger m_sLinger;
m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留)
// 如果m_sLinger.l_onoff=0;则功能和2.)作用相同;
m_sLinger.l_linger=5;//(容许逗留的时间为5秒)
setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));
 
	*/

	SOCKADDR_IN addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(2591);
	addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
		cout << "error:" << WSAGetLastError() << endl;
		closesocket(sock);
		WSACleanup();
		return -4;
	}

	DWORD dwBytesReturned;
	DWORD dwBufferInLen = 1;
	//将网卡设置为混听模式,就是接收所有数据
	if (ioctlsocket(sock, SIO_RCVALL, &dwBufferInLen) == SOCKET_ERROR) {
		cout << "error:" << WSAGetLastError() << endl;
		closesocket(sock);
		WSACleanup();
		return -5;
	}
	/*
	ioctsocket功能是控制套接口的模式。可用于任一状态的任一套接口。它用于获取与套接口相关的操作参数,而与具体协议或通讯子系统无关。
	int ioctlsocket( int s, long cmd, u_long * argp);
	s:一个标识套接口的描述字。
	cmd:对套接口s的操作命令。
	argp:指向cmd命令所带参数的指针。
	*/

	int bytesRecv;
	char buffer[65535];//接收缓冲区的内容
	//SOCKADDR_IN from;
	struct sockaddr_in from;
	int fromSize = sizeof(from);
	//循环监听;
	while (true) {
		memset(buffer, 0, 65535);
		bytesRecv = recvfrom(sock, buffer, 65535, 0, (struct sockaddr*)&from, &fromSize);
		if (bytesRecv == SOCKET_ERROR) {
			cout << "error:" << WSAGetLastError() << endl;
			closesocket(sock);
			WSACleanup();
			return -6;
		}
		/*
		与recv的功能差不多,都是接收数据,但是from可以适用于UDP,因为多了一个from,你懂的。
		*/

		ip = (struct IP*)buffer;

		if (ip->protocal == 6) {//过滤其他协议,只留下TCP协议
			tcp = (struct TCP*)(buffer + (4 * ip->headLen & 0xf0 >> 4));//得到TCP头
			cout << "Network+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";//网络层数据
			cout << "IP报文字节数:" << bytesRecv << "\n";
			cout << "源IP:" << inet_ntoa(*(in_addr*)&ip->sourceAddr) << "\n";
			cout << "目的IP:" << inet_ntoa(*(in_addr*)&ip->destinAddr) << "\n";
			cout << "Transportation++++++++++++++++++++++++++++++++++++++++++++++++++++\n";//运输层数据
			cout << "源端口:" << ntohs(tcp->sourcePort) << "\n";
			cout << "目的端口:" << ntohs(tcp->destinPort) << "\n";
			cout << "Applications++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";//应用层数据

			char* start = buffer + 5 + 4 * ((tcp->headLen & 0xf0) >> 4 | 0);//计算数据头指针,从何处开始数据
			int dataSize = bytesRecv - 5 - 4 * ((tcp->headLen & 0xf0) >> 4 | 0);//计算数据长度
			cout << "数据内容:";
			memcpy(buffer, start, dataSize);
			for (int i = 0; i < dataSize; i++) {
				if (buffer[i] >= 32 && buffer[i] < 255) {
					printf("%c", (unsigned char)buffer[i]);
				}
				else {
					printf(".");
				}
			}
			cout << "\n";
		}
	}

}
#include<WinSock2.h>
#include <iostream>
using namespace std;

#pragma comment(lib,"ws2_32.lib")

int main() {

	WSADATA wsd;//定义	WSADATA对象
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) {//初始化WSA
		WSACleanup();
		return -1;
	}

	SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
	if (clientSocket == INVALID_SOCKET) {
		cout << "error:" << WSAGetLastError() << endl;
		WSACleanup();
		return -2;
	}

	SOCKADDR_IN client;
	client.sin_family = AF_INET;
	client.sin_port = htons(2591);
	client.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	int const SERVER_MSG_SIZE = 128;
	char inMSG[SERVER_MSG_SIZE] = { 0 };//用户输入的消息
	char outMSG[SERVER_MSG_SIZE];//要发送给服务器的消息

	//连接服务器失败
	if (connect(clientSocket, (struct sockaddr*)&client, sizeof(client)) < 0) {
		cout << "error:" << WSAGetLastError() << endl;
		closesocket(clientSocket);
		WSACleanup();
		return -3;
	}
	//连接服务器成功
	else {
		cout << "连接服务器成功。。。。。。\n" << endl;
		while (true) {
			memset(outMSG, 0, SERVER_MSG_SIZE);
			cout << "请输入请求。。。。。。:" << endl;
			cin >> outMSG;
			send(clientSocket, outMSG, SERVER_MSG_SIZE, 0);
			if (strcmp(outMSG, "退出连接") == 0) {
				break;
			}
			int size = recv(clientSocket, inMSG, SERVER_MSG_SIZE, 0);
			cout << "服务器端回答:" << inMSG << endl;
			memset(inMSG, 0, SERVER_MSG_SIZE);
		}
	}

	closesocket(clientSocket);
	WSACleanup();

	system("pause");
	return 0;

}


WSAGetLastError()部分常见返回值

10004 —WSAEINTR
函数调用中断。该错误表明由于对W S A C a n c e l B l o c k i n g C a l l的调用,造成了一次调用被强
行中断。

10009 —WSAEBADF

文件句柄错误。该错误表明提供的文件句柄无效。在Microsoft Windows CE 下,s o c k e t函数可能返回这个错误,表明共享串口处于“忙”状态。

10013 —WSAEACCES

权限被拒。尝试对套接字进行操作,但被禁止。若试图在s e n d t o 或W S A S e n d To中使用一个广播地址,但是尚未用s e t s o c k o p t 和S O _ B R O A D C A S T这两个选项设置广播权限,便会产生这类错误。

项目右键-属性-链接器-清单文件-uac执行级别选择requireAdministrator

10014 —WSAEFAULT

地址无效。传给Wi n s o c k 函数的指针地址无效。若指定的缓冲区太小,也会产生这个错误。

10022 —WSAEINVAL
参数无效。指定了一个无效参数。例如,假如为W S A I o c t l调用指定了一个无效控制代码,便会产生这个错误。另外,它也可能表明套接字当前的状态有错,例如在一个目前没有监听的套接字上调用a c ce p t 或W S A A c c e p t 。

10024 —WSAEMFILE
打开文件过多。提示打开的套接字太多了。通常,M i c r o s o f t 提供者只受到系统内可用资源数量的限制。

10035 —WSAEWOULDBLOCK
资源暂时不可用。对非锁定套接字来说,如果请求操作不能立即执行的话,通常会返回这个错误。比如说,在一个非暂停套接字上调用c o n ne c t ,就会返回这个错误。因为连接请求不能立即执行。

10036 —WSAEINPROGRESS
操作正在进行中。当前正在执行非锁定操作。一般来说不会出现这个错误,除非正在开发1 6 位Wi n s o c k 应用程序。

10037 —WSAEALREADY
操作已完成。一般来说,在非锁定套接字上尝试已处于进程中的操作时,会产生这个错误。比如,在一个已处于连接进程的非锁定套接字上,再一次调用co n n e c t 或W S A C o n n e c t 。另外,服务提供者处于执行回调函数(针对支持回调例程的Wi n so c k 函数)的进程中时,也会?出现这个错误。

10038 —WSAENOTSOCK
无效套接字上的套接字操作。任何一个把S O C K E T 句柄当作参数的Wi n s o c k函数都会返回这个错误。它表明提供的套接字句柄无效。

10039 —WSAEDESTADDRREQ
需要目标地址。这个错误表明没有提供具体地址。比方说,假如在调用s e n d t o 时,将目标地址设为I N A D D R _ AN Y (任意地址),便会返回这个错误。

10040 —WSAEMSGSIZE
消息过长。这个错误的含义很多。如果在一个数据报套接字上发送一条消息,这条消息对内部缓冲区而言太大的话,就会产生这个错误。再比如,由于网络本身的限制,使一条消息过长,也会产生这个错误。最后,如果收到数据报之后,缓冲区太小,不能接收消息时,也会产生这个错误。

10041 —WSAEPROTOTYPE
套接字协议类型有误。在s o c k e t 或W S A S o c k e t调用中指定的协议不支持指定的套接字类型。比如,要求建立S O C K _ S T R E A M 类型的一个I P套接字,同时指定协议为I P P R O TO _ U D P ,便会产生这样的错误。

10042 —WSAENOPROTOOPT
协议选项错误。表明在g e t s o c k o p t 或s e t s o c k o p t调用中,指定的套接字选项或级别不明、未获支持或者无效。

10043 ——WSAEPROTONOSUPPORT
不支持的协议。系统中没有安装请求的协议或没有相应的实施方案。比如,如果系统中没有安装T C P / I P ,而试着建立T C P或U D P 套接字时,就会产生这个错误。

10044 —WSAESOCKTNOSUPPORT
不支持的套接字类型。对指定的地址家族来说,没有相应的具体套接字类型支持。比如,在向一个不支持原始套接字的协议请求建立一个S O C K_ R AW 套接字类型时,就会产生这个错误。

10045 —WSAEOPNOTSUPP
不支持的操作。表明针对指定的对象,试图采取的操作未获支持。通常,如果试着在一个不支持调用Wi n s o c k函数的套接字上调用了Wi n s o c k 时,就会产生这个错误。比如,在一个数据报套接字上调用a c c e p t 或W S AA c c e p t 函数时,就会产生这样的错误。

10046 —WSAEPFNOSUPPORT
不支持的协议家族。请求的协议家族不存在,或系统内尚未安装。多数情况下,这个错误可与W S A E A F N O S U P P ORT 互换(两者等价);后者出现得更为频繁。

10047 —WSAEAFNOSUPPORT
地址家族不支持请求的操作。对套接字类型不支持的操作来说,在试着执行它时,就会出现这个错误。比如,在类型为S O C K _ S T RE A M 的一个套接字上调用s e n d t o 或W S A S e n d To 函数时,就会产生这个错误。另外,在调用s oc k e t 或W S A S o c k e t函数的时候,若同时请求了一个无效的地址家族、套接字类型及协议组合,也会产生这个错误。

10048 —WSAEADDRINUSE
地址正在使用。正常情况下,每个套接字只允许使用一个套接字地址(例如,一个I P 套接字地址由本地I P地址及端口号组成)。这个错误一般和b i n d 、c o n n e c t 和W S A C o n n e c t这三个函数有关。可在s e t s o c k o p t 函数中设置套接字选项S O _ R E U S E A D D R,允许多个套接字访问同一个本地I P 地址及端口号(详情见第9 章)。

10049 —WSAEADDRNOTAVAIL
不能分配请求的地址。A P I 调用中指定的地址对那个函数来说无效时,就会产生这样的错误。例如,若在b i n d 调用中指定一个IP 地址,但却没有对应的本地I P 接口,便会产生这样的错误。另外,通过c o n n e c t 、W S A C o n n ec t 、s e n d t o 、W S A S e n d To 和W S A J o i n L e a f这四个函数为准备连接的远程计算机指定端口0 时,也会产生这样的错误。

10050 —WSAENETDOWN
网络断开。试图采取一项操作时,却发现网络连接中断。这可能是由于网络堆栈的错误,网络接口的故障,或者本地网络的问题造成的。

10051 —WSAENETUNREACH
网络不可抵达。试图采取一项操作时,却发现目标网络不可抵达(不可访问)。这意味着本地主机不知道如何抵达一个远程主机。换言之,目前没有已知的路由可抵达那个目标主机。

10052 —WSAENETRESET
网络重设时断开了连接。由于“保持活动”操作检测到一个错误,造成网络连接的中断。若在一个已经无效的连接之上,通过s e t s o ck o p t 函数设置S O _ K E E PA L I V E 选项,也会出现这样的错误。

10053 —WSAECONNABORTED
软件造成连接取消。由于软件错误,造成一个已经建立的连接被取消。典型情况下,这意味着连接是由于协议或超时错误而被取消的。

10054 —WSAECONNRESET
连接被对方重设。一个已经建立的连接被远程主机强行关闭。若远程主机上的进程异常中止运行(由于内存冲突或硬件故障),或者针对套接字执行了一次强行关闭,便会产生这样的错误。针对强行关闭的情况,可用SO _ L I N G E R 套接字选项和s e t s o c k o p t 来配置一个套接字(欲知详情,请参阅第9章)。

10055 —WSAENOBUFS
没有缓冲区空间。由于系统缺少足够的缓冲区空间,请求的操作不能执行。

10056 —WSAEISCONN

套接字已经连接。表明在一个已建立连接的套接字上,试图再建立一个连接。要注意的是,数据报和数据流套接字均有可能出现这样的错误。使用数据报套接字时,假如事先已通过co n n e c t 或W S A C o n n e c t 调用,为数据报通信关联了一个端点的地址,那么以后试图再次调用s en d t o 或W S A S e n d To ,便会产生这样的错误。

10057 —WSAENOTCONN
套接字尚未连接。若在一个尚未建立连接的“面向连接”套接字上发出数据收发请求,便会产生这样的错误。

10058 —WSAESHUTDOWN
套接字关闭后不能发送。表明已通过对s h u t d o w n的一次调用,部分关闭了套接字,但又请求进行数据的收发操作。要注意的是,这种错误只会在已经关闭的那个数据流动方向上才会发生。举个例子来说,完成数据发送后,若调用sh u t d o w n ,那么以后任何数据发送调用都会产生这样的错误。

10060 —WSAETIMEDOUT
连接超时。若发出了一个连接请求,但经过规定的时间,远程计算机仍未作出正确的响应(或根本没有任何响应),便会发生这样的错误。要想收到这样的错误,通常需要先在套接字上设置好SO _ S N D T I M E O 和S O _ R C V T I M E O 选项,然后调用c o n n e c t 及WS A C o n n e c t 函数。要想了解在套接字上设置S O _ S N D T I M E O 和S O _ R C VT I M E O 选项的详情,可参考第9 章。

10061 —WSAECONNREFUSED
连接被拒。由于被目标机器拒绝,连接无法建立。这通常是由于在远程机器上,没有任何应用程序可在那个地址之上,为连接提供服务。

10064 —WSAEHOSTDOWN
主机关闭。这个错误指出由于目标主机关闭,造成操作失败。然而,应用程序此时更有可能收到的是一条W S A E T I M E D O UT (连接超时)错误,因为对方关机的情况通常是在试图建立一个连接的时候发生的。

10065 —WSAEHOSTUNREACH

没有到主机的路由。应用程序试图访问一个不可抵达的主机。该错误类似于W S A E N ET U N R E A C H 。

10067 —WSAEPROCLIM

进程过多。有些Wi n s o c k 服务提供者对能够同时访问它们的进程数量进行了限制。

10091 —WSASYSNOTREADY
网络子系统不可用。调用W S A S t a r t u p时,若提供者不能正常工作(由于提供服务的基层系统不可用),便会返回这种错误。

10092 —WSAVERNOTSUPPORTED
Wi n s o c k . d l l 版本有误。表明不支持请求的Wi n s o c k 提供者版本。

10093 —WSANOTINITIALISED
Wi n s o c k 尚未初始化。尚未成功完成对W S A S t a r t u p 的一次调用。

10101 —WSAEDISCON
正在从容关闭。这个错误是由W S A R e c v 和W S A R e c v F r o m返回的,指出远程主机已初始化了一次从容关闭操作。该错误是在像AT M 这样的“面向消息”协议上发生的。

10102 —WSAENOMORE
找不到更多的记录。这个错误自W S A L o o k u p S e r v i c e N e x t函数返回,指出已经没有留下更多的记录。这个错误通常可与W S A _ E _ N O _ M O R E互换使用。在应用程序中,应同时检查这个错误以及W S A _ E _ N O _ M O R E 。

10103 —WSAECANCELLED
操作被取消。这个错误指出当W S A L o o k u p S e r v i c e N e x t 调用仍在处理期间,发出了对WS A L o o k u p S e r v i c e E n d (服务中止)的一个调用。此时,W S A L o o k up S e r v i c e N e x t 便会返回这个错误。这个错误代码可与W S A _ E _ C A N C E L LE D 互换使用。作为应用程序,应同时检查这个错误以及W S A _ E _ C A N C E L L E D 。

10104 —WSAEINVALIDPROCTABLE
进程调用表无效。该错误通常是在进程表包含了无效条目的情况下,由一个服务提供者返回的。欲知服务提供者的详情,可参考第1 4 章。

10105 —WSAEINVALIDPROVIDER
无效的服务提供者。这个错误同服务提供者关联在一起,在提供者不能建立正确的Wi n s o c k版本,从而无法正常工作的前提下产生。

10106 —WSAEPROVIDERFAILEDINIT
提供者初始化失败。这个错误同服务提供者关联在一起,通常见于提供者不能载入需要的D L L 时。

10107 —WSASYSCALLFAILURE
系统调用失败。表明绝对不应失败的一个系统调用却令人遗憾地失败了。

10108 —WSASERVICE_NOT_FOUND
找不到这样的服务。这个错误通常与注册和名字解析函数相关,在查询服务时产生(第1 0章对这些函数进行了详尽解释)。该错误表明,在给定的名字空间内,找不到请求的服务。

10109 —WSATYPE_NOT_FOUND
找不到类的类型。该错误也与注册及名字解析函数关联在一起,在处理服务类(S e r v i c eC l a s s)时发生。若注册好一个服务的实例,它必须引用一个以前通过W S A I n s t a l l S e r v i c e C l as s安装好的服务。

10110 —WSA_E_NO_MORE
找不到更多的记录。这个错误是自W S A L o o k u p S e r v i c e N e x t调用返回的,指出已经没有剩下的记录。该错误通常可与W S A E N O M O R E互换使用。作为一个应用程序,应同时检查这个错误以及W S A E N O M O R E 。

10111 —WSA_E_CANCELLED
操作被取消。该错误指出在对W S A L o o k u p S e r v i c e N e x t的调用尚未完成的时候,又发出了对W S A L o o k u p S e r v i c e E n d(中止服务)的一个调用。这样,W S A L o o k u p S e r v i c e N e x t就会返回该错误。这个错误代码可与W S A E C A N C E L L E D互换使用。作为一个应用程序,应同时检查这个错误以及W S A E C A N C E L L E D 。

10112 —WSAEREFUSED
查询被拒。由于被主动拒绝,所以一个数据库查询操作失败。

11001 —WSAHOST_NOT_FOUND
主机没有找到。这个错误是在调用g e t h o s t b y n a m e 和g e t h o s t b y a d d r时产生的,表明没有找到一个授权应答主机(Authoritative Answer Host )。

11002 —WSATRY_AGAIN
非授权主机没有找到。这个错误也是在调用g e t h o s t b y n a m e 和g e t h o s t b y a dd r 时产生的,表明没有找到一个非授权主机,或者遇到了服务器故障。

11003 —WSANO_RECOVERY
遇到一个不可恢复的错误。这个错误也是在调用g e t h o s t b y n a m e 和g e t h o s t b y ad d r 时产生的,指出遇到一个不可恢复的错误,应再次尝试操作。

11004 —WSANO_DATA
附录C 计Winsock 错误代码计计485 下载?没有找到请求类型的数据记录。这个错误也是在调用g e t h o s t b yn a m e 和g e t h o s t b y a d d r时产生的,指出尽管提供的名字有效,但却没有找到与请求类型对应的数据记录。

11005 —WSA_QOS_RECEIVERS
至少有一条预约消息抵达。这个值同I P 服务质量(Q o S )有着密切的关系,其实并不是一个真正的“错误”(Q o S 的详情见第12 章)。它指出网络上至少有一个进程希望接收Q o S 通信。

11006 —WSA_QOS_SENDERS
至少有一条路径消息抵达。这个值同Q o S 关联在一起,其实更像一种状态报告消息。它指出在网络上,至少有一个进程希望进行Q o S数据的发送。

11007 —WSA_QOS_NO_SENDERS
没有Q o S 发送者。这个值同Q o S 关联在一起,指出不再有任何进程对Q o S 数据的发送有兴趣。请参阅第1 2章,了解在发生这样的错误时,对所发生情况的一系列完整说明。

11008 —WSA_QOS_NO_RECEIVERS
没有Q o S 接收者。这个值同Q o S 关联在一起,指出不再有任何进程对Q o S 数据的接收有兴趣。请参阅第1 2章,查阅对这个错误的完整说明。

11009 —WSA_QOS_REQUEST_CONFIRMED
预约请求已被确认。Q o S应用可事先发出请求,希望在批准了自己对网络带宽的预约请求后,收到通知。若发出了这样的请求,一旦批准,便会收到这样的消息。请参阅第1 2章,了解对此消息的详细说明。

11010 —WSA_QOS_ADMISSION_FAILURE
缺乏资源致错。资源不够,以至于无法满足Q o S 带宽请求。

11011 —WSA_QOS_POLICY_FAILURE
证书无效。表明发出Q o S 预约请求的时候,要么用户并不具备正确的权限,要么提供的证书无效。

11012 —WSA_QOS_BAD_STYLE
未知或冲突的样式。Q o S应用程序可针对一个指定的会话,建立不同的过滤器样式。若出现这一错误,表明指定的样式类型要么未知,要么存在冲突。请参阅第1 2章,了解对过滤器样式的详细说明。

11013 —WSA_QOS_BAD_OBJECT
无效的F I LT E R S P E C 结构或者提供者特有对象。假如为Q o S 对象提供的F I LT E R S P E C结构无效,或者提供者特有的缓冲区无效,便会返回这样的错误,详见第1 2 章。

11014 —WSA_QOS_TRAFFIC_CTRL_ERROR
F L O W S P E C 有问题。假如通信控制组件发现指定的F L O W S P E C 参数存在问题(作为Q oS对象的一个成员传递),便会返回这样的错误。

11015 —WSA_QOS_GENERIC_ERROR
常规Q o S 错误。这是一个比较泛泛的错误;假如其他Q o S 错误都不适合,便返回这个错误。

6 —WSA_INVALID_HANDLE
指定的事件对象无效。若使用与Wi n 3 2 函数对应的Wi n s o c k 函数,便有可能产生这样的Wi n 3 2错误。它表明传递给W S AWa i t F o r M u l t i p l e E v e n t s的一个句柄是无效的。

8 —WSA_NOT_ENOUGH_MEMORY
这个Wi n 3 2 错误指出内存数量不足,无法完成指定的操作。

87 —WSA_INVALID_PARAMETER
一个或多个参数无效。这个Wi n 3 2 错误表明传递到函数内部的参数无效。假若事件计数参数无效,那么在执行W S AWa i t Fo r M u l t i p l e E v e n t s 的时候,也会发生这样的错误。

258 —WSA_WAIT_TIMEOUT
操作超时。这个Wi n 3 2 错误指出重叠I / O 操作未在规定的时间内完成。

995 —WSA_OPERATION_ABORTED
重叠操作被取消。这个Wi n 3 2 错误指出由于套接字的关闭,造成一次重叠I / O 操作的取消。除此以外,该错误也可能在执行S IO _ F L U S H 这个I / O 控制命令时出现。

996 —WSA_IO_INCOMPLETE
重叠I / O 事件对象未处于传信状态。这个Wi n 3 2 错误也和重叠I / O 操作密切相关,在调用W S A G e t Ov e r l a p p e d R e s u l t s 函数的时候产生,指出重叠I / O 操作尚未完成。

997 —WSA_IO_PENDING
重叠操作将在以后完成。用Wi n s o c k 函数发出一次重叠I / O 操作时,若出现这样的Wi n 3 2错误,便表明操作尚未完成,而且会在以后的某个时间完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值