C语言Windows网络编程
〇、网络编程错误码 错误码
错误码 错误原因 10045 参考的对象种类不支持尝试的操作 10046 协议家族尚未配置到系统中或没有它的存在迹象 10047 使用了与请求的协议不兼容的地址 10048 通常每个套接字地址 (协议/网络地址/端口)只允许使用一次 10049 在其上下文中,该请求的地址无效 10050 套接字操作遇到了一个已死的网络 10051 向一个无法连接的网络尝试了一个套接字操作 10052 当该操作在进行中,由于保持活动的操作检测到一个故障,该连接中断 10053 您的主机中的软件放弃了一个已建立的连接 10054 远程主机强迫关闭了一个现有的连接 10055 由于系统缓冲区空间不足或列队已满,不能执行套接字上的操作 10056 在一个已经连接的套接字上做了一个连接请求 10057 由于套接字没有连接并且 (当使用一个 sendto 调用发送数据报套接字时) 没有提供地址,发送或接收数据的请求没有被接受 10058 由于以前的关闭调用,套接字在那个方向已经关闭,发送或接收数据的请求没有被接受 10059 对某个内核对象的引用过多 10060 由于连接方在一段时间后没有正确的答复或连接的主机没有反应,连接尝试失败 10061 不能做任何连接,因为目标机器积极地拒绝它 10062 无法翻译名称 10063 名称组件或名称太长 10064 由于目标主机坏了,套接字操作失败 10065 套接字操作尝试一个无法连接的主机 10066 不能删除目录,除非它是空的 10067 一个 Windows 套接字操作可能在可以同时使用的应用程序数目上有限制 10068 超过限额 10069 超过磁盘限额 10070 文件句柄引用不再有效 10071 项目在本地不可用 10091 因为它使用提供网络服务的系统目前无效,WSAStartup 目前不能正常工作 10092 不支持请求的 Windows 套接字版本 10093 应用程序没有调用 WSAStartup,或者 WSAStartup 失败 10101 由 WSARecv 或 WSARecvFrom 返回表示远程方面已经开始了关闭步骤 10102 WSALookupServiceNext 不能返回更多的结果 10103 在处理这个调用时,就开始调用 WSALookupServiceEnd该调用被删除 10104 过程调用无效 10105 请求的服务提供程序无效 10106 没有加载或初始化请求的服务提供程序 10107 从来不应失败的系统调用失败了 10108 没有已知的此服务在指定的名称空间中找不这个服务 10109 找不到指定的类别 10110 WSALookupServiceNext 不能返回更多的结果 10111 在处理这个调用时,就开始调用 WSALookupServiceEnd该调用被删除 10112 由于被拒绝,数据查询失败 11001 不知道这样的主机 11002 这是在主机名解析时常出现的暂时错误,并且意味着本地服务器没有从权威服务器上收到响应 11003 在数据寻找中出现一个不可恢复的错误 11004 请求的名称有效并且是在数据库中找到,但是它没有相关的正确的数据 11005 至少到达了一个保留 11006 至少到达了一个路径 11007 没有发送方 11008 没有接受方 11009 保留已经确认 11010 错误是由于资源不足造成 11011 由于管理原因被拒绝 无效凭据 11012 未知或有冲突类型 11013 某一部分的 filterspec 或 providerspecific 缓冲区有问题 11014 flowspec 的某部分有问题 11015 一般性 QOS 错误 11016 在流程规格中发现一个无效的或不可识别的服务类型 11017 在 QOS 结构中发现一个无效的或不一致的流程规格 11018 无效的 QOS 提供程序特定缓冲区 11019 使用了无效的 QOS 筛选器样式 11020 使用了无效的 QOS 筛选器类型 11021 FLOWDESCRIPTOR 中指定的 QOS FILTERSPEC 数量不正确
一、基本概念 1 计算机网络和TCPIP协议
五层结构:
2 网络通信模型
C/S:
B/S:
P2P:
3 网络数据传输
字节顺序:
大端顺序:高字节存在内存低地址。
小端顺序:低字节存在内存低地址。
例如:对于0x12345678 大端: 地址0 [ 0x12 , 0x34 , 0x56 , 0x78 ] 地址31 小端: 地址0 [ 0x78 , 0x56 , 0x34 , 0x12 ] 地址31
一般情况下:网络传输用大端模式,内存存储用小端模式。(例如基于x86平台的PC机)
对齐:C语言结构体存储会自动对齐。
编码:Base64是网络上最常见的用于传输8比特方式之一,可用于在HTTP环境下传递较长的标识信息。
4 编程概念
接口功能:
基本功能:
分配用于通信的本地资源。
指定本地与远程通信端点。
(客户端)启动连接。
(服务器)等待连接到来。
发送或接收数据。
判断数据何时到来。
从容终止连接。
辅助功能:
产生紧急数据。
处理到来的紧急数据。
处理来自远程端点的连接终止。
异常终止通信。
处理错误条件或连接异常终止。
连接结束后释放本地资源。
UNIX中的基本I/O功能:
5 套接字
协议:
PF_INET:IPv4协议簇
PF_INET6:IPv6协议簇
PF_IPX:IPX/SPX协议簇
PF_NETBIOS:NetBIOS协议簇
套接字类型:
SOCK_STREAM:TCP协议
SOCK_DGRAM:UDP协议
SOCK_RAW:IP协议
端点地址:
AF_INET:IPv4地址族
AF_INET6:IPv6地址族
AF_IPX:IPX/SPX地址族
AF_NETBIOS:NetBIOS地址族
AF_UNSPEC 则意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址。
通信过程:
建立一个Socket。
配置Socket。
连接Socket。(可选)
通过Socket发送数据。
通过Socket接收数据。
关闭Socket。
6 多线程编程
使用:
创建线程函数。
创建线程。
激活线程。
结束线程。
二、函数 1 头文件 #define _CRT_SECURE_NO_WARNINGS #define _WINSOCK_DEPRECATED_NO_WARNINGS #include <stdio.h> #include <WinSock2.h> #include <WS2tcpip.h> #include <stdlib.h> #pragma comment (lib, "Ws2_32.lib" ) #pragma comment (lib, "Mswsock.lib" ) #pragma comment (lib, "AdvApi32.lib" )
2 网络数据函数
对齐:#pragma pack([show]|[push|pop][,identifier],n);
:
#pragma pack用法
#pragma pack()
设置的是默认对齐模数。
show:可选参数,显示当前对齐的模板,以警告消息的形式显示。
push:对齐模数n压栈,未指定n则压入当前对齐模数。push一般带n。
pop:n出栈。不带n设置出栈数为对齐模数,设置n则会出栈并设置n为对齐模数。pop一般不带n。
identifier:编译器执行这条执行时会从栈顶向下顺序查找匹配的identifier,找到identifier相同的这个数之后将从栈顶到identifier,包括找到identifier全部pop弹出, 若没有找到则不进行任何操作。
n:对齐模数。
u_short WSAPPI ntohs (__in u_short netshort) ; u_long WSAPPI ntohl (__in u_long netlong) ; u_short WSAPPI htons ( __in u_short hostshort) ; u_long WSAPPI htonl ( __in u_long hostlong) ; u_short in_cksum (u_short* pchBuffer,int iSize) { u_long ulCksum = 0 ; while (iSize > 1 ) { ulCksum += *pchBuffer++; iSize -= sizeof (u_short); } if (iSize) ulCksum += *(UCHAR*)pchBuffer; ulCksum = (ulCksum >> 16 ) + (ulCksum & 0xffff ); ulCksum += (ulCksum >> 16 ); return (USHORT)(~ulCksum); }
3 Windows套接字函数 3.1 WSAStartup()
Windows Sockets DLL的初始化。
int WSAStartup ( __in WORD wVersionRequested , __out LPWSADATA lpWSAData ) ;WSADATA wsaData; int iResult; iResult = WSAStartup (MAKEWORD (2 ,2 ),&wsaData); if (iResult != 0 ){ printf ("WSAStartup failed with error: %d\n" ,iResult); return -1 ; }
3.2 WSACleanup()
Windows Sockets DLL的释放。
每一次WSAStartup()必须有一个对应的WSACleanup()。
int WSACleanup (void ) ;int iResult; iResult = WSACleanup (); if (iResult != 0 ){ printf ("WSAStartup failed with error: %d\n" ,iResult); return -1 ; }
3.3 IPv4地址结构
使用时应该把 sockaddr_in强转为sockaddr传递。
struct sockaddr { ushort sa_family ; char sa_data[14 ] ; } typedef struct in_addr { union { struct { u_char s_b1, s_b2, s_b3, s_b4; } S_un_b; struct { u_short s_w1, s_w2; } S_un_w; u_long S_addr; } S_un; }IN_ADDR,*PIN_ADDR,FAR* LPIN_ADDR; struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8 ]; };
3.4 IPv6地址结构
使用时应该把 sockaddr_in6强转为sockaddr传递。
typedef struct in6_addr { union { u_char Byte[16 ]; u_short Word[8 ]; }u; }IN6_ADDR,*PIN6_ADDR,FAR* LPIN6_ADDR; struct sockaddr_in6 { short sin6_family; u_short sin6_port; u_long sin6_flowinfo; struct in6_addr sin6_addr; char sin6_scope_id; };
addrinfo
WinSock2增加的一个以链表形式保存地址信息的函数。
typedef struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; char * ai_canonname; struct sockaddr * ai_addr; struct addrinfo * ai_next; }ADDRINFOA,*PADDRINFOA; int getaddrinfo ( const char * hostname, const char * service, const struct addrinfo* hints, struct addrinfo** result ) ;
3.5 IPv4点分与无符号整形转化 unsigned long inet_addr ( __in const char * cp) ; char *FAR inet_ntoa ( __in struct in_addr in) ;
3.6 套接字选项
套接字选项
套接口
int getsockopt ( __in socket s, __in int level, __in int optname, __out char * optval, __inout int * optlen, ) ;int setsockopt ( __in socket s, __in int level, __in int optname, __in const char * optval, __in int optlen, ) ;int time = 1000 ; int r = setsockopt (s,SOL_SOCKET,SO_RCVTIMEO,(const char *)&time,sizeof (time));unsigned long time = 1 ;setsockopt (s,SOL_SOCKET,SO_BROADCAST,(const char *)&time,sizeof (time));
3.7 I/O控制命令
int WSAAPI WSAIoctl() )
int ioctlsocket ( __in int s, __in long cmd, __in_out u_long * argp ) ;u_long optival = 1 ; isoctlsocket (s,SIO_RCVALL,&optival);
3.8 补充
WSAGetLastError():获得上次失败操作的错误码。
shutdown() ):禁止某些操作。
4 TCP函数 4.1 TCP通信过程 WSAStartup (MAKEWORD (2 , 2 ), &wsaData);socket ();getaddrinfo ();bind ();listen ();accept ();send ();recv ();shutdown ();closesocket ();WSACleanup ();WSAStartup (MAKEWORD (2 , 2 ), &wsaData);socket ();getaddrinfo ();connect ();send ();recv ();shutdown ();closesocket ();WSACleanup ();
4.2 套接字
socket WSASocket() ):创建套接字。
TCP参数:socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
socket WSAAPI socket ( __in int af, __in int type, __in int protocol, ) ;ConnectSocket = socket (ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (ConnectSocket == INVALID_SOCKET){ printf ("socket failed with error: %d\n" , WSAGetLastError ()); WSACleanup (); return 1 ; } int closesocket ( __in SOCKET s ) ;closesocket (ConnectSocket);
4.3 获取地址
getaddrinfo() :获得主机地址。
gethostbyname() :依据主机名获得IP地址。
int bind ( __in SOCKET s, __in const struct sockaddr* name, __in int namelen ) iResult = bind (ListenSocket, result->ai_addr, (int )result->ai_addrlen);if (iResult == SOCKET_ERROR){ printf ("bind failed with error: %d\n" , WSAGetLastError ()); freeaddrinfo (result); closesocket (ListenSocket); WSACleanup (); return 1 ; } int getsockname ( __in SOCKET s, __in struct sockaddr* name, __in int *namelen ) int getsockname ( __in SOCKET s, __out struct sockaddr* name, __inout int * namelen )
4.4 连接请求
socket_listen里面第二个参数backlog的用处
int connect ( __in SOCKET s, __in const struct sockaddr* name, __in int namelen ) ;iResult = connect (ConnectSocket,ptr->ai_addr,(int )ptr->ai_addrlen); if (iResult == SOCKET_ERROR){ closesocket (ConnectSocket); ConnectSocket = INVALID_SOCKET; continue ; } int listen ( __in SOCKET s, __in int backlog, ) iResult = listen (ListenSocket, SOMAXCONN);if (iResult == SOCKET_ERROR){ printf ("listen failed with error: %d\n" , WSAGetLastError ()); closesocket (ListenSocket); WSACleanup (); return 1 ; } SOECKT accept ( __in SOCEKT s, __out struct sockaddr* addr, __inout int *addrlen ) ClientSocket = accept (ListenSocket,NULL ,NULL );if (ClientSocket == INVALID_SOCKET){ printf ("accept failed with error: %d\n" , WSAGetLastError ()); closesocket (ListenSocket); WSACleanup (); return 1 ; }
4.5 传输数据 int send ( __in SOCKET s, __in const char * buf, __in int len, __in int flags, ) ;iResult = send (ConnectSocket,sendbuf,(int )strlen (sendbuf),0 ); if (iResult == SOCKET_ERROR){ printf ("send failed with error: %d\n" , WSAGetLastError ()); closesocket (ConnectSocket); WSACleanup (); return 1 ; } int recv ( __in SOCKET s, __out char * buf, __in int len, __in int flags ) iResult = recv (ConnectSocket,recvbuf,recvbuflen,0 );if (iResult > 0 ) printf ("Bytes received: %d\n" , iResult); else if (iResult == 0 ) printf ("Connection closed\n" ); else printf ("recv failed with error: %d\n" ,WSAGetLastError ());
5 UDP函数 5.1 UDP通信过程
UDP连接模式:
connect() 连接,不同于TCP三次握手,仅仅是绑定IP和port,不会产生网络活动
connect() 重新绑定套接字可以更新IP和port
再次调用connect() 时,若之前把套接字地址结构设置成AF_UNSPEC,后续的send()和recv()将出错
UDP参数:socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
WSAStartup (MAKEWORD (2 , 2 ), &wsaData);socket ();getaddrinfo ();bind ();recvfrom ();sendto ();shutdown ();closesocket ();WSACleanup ();WSAStartup (MAKEWORD (2 , 2 ), &wsaData);socket ();getaddrinfo ();recvfrom ();sendto ();shutdown ();closesocket ();WSACleanup ();
5.2 传输数据 int sendto ( __in SOCKET s, __in const char * buf, __in int len, __in int flags, __in const struct sockaddr* to, __in int tolen ) ;iResult = sendto (ConnectLessSocket,sendbuf,(int )strlen (sendbuf),0 ,result->ai_addr,(int )result->ai_addrlen); if (iResult == SOCKET_ERROR){ printf ("sendto failed with error: %d\n" , WSAGetLastError ()); closesocket (ConnectLessSocket); WSACleanup (); return 1 ; } int recvfrom ( __in SOCKET s, __out char * buf, __in int len, __in int flags __out struct sockaddr* from, __in_outopt_ int *fromlen ) iResult = recvfrom (ConnectLessSocket,recvbuf, recvbuflen,0 ,NULL ,NULL );if (iResult > 0 ) printf ("Bytes received: %d\n" , iResult); else if (iResult == 0 ) printf ("Connection closed\n" ); else printf ("recv failed with error: %d\n" , WSAGetLastError ());
6 原始套接字函数
使用场景:
作用于网络层
发送和接受ICMP IGMP等包。
发送和接受内核不处理其协议字段的IPv4数据包。实现非常规协议OSPF。
控制IPv4首部,伪造IP地址。
IP参数:
准备工作:
本地地址关联——bind()(不常见)
远端地址关联——connect()(不常见)
常用协议协议名
值
协议
IPPROTO_IP
0
IP协议
IPPROTO_ICMP
1
ICMP协议
IPPROTO_IGMP
2
IGMP协议
IPPROTO_RFCOMM
3
PFCOMM协议
IPPROTO_IPv4
4
IPv4协议
IPPROTO_TCP
6
TCP协议
IPPROTO_UDP
17
UDP协议
IPPROTO_IPv6
41
IPv6协议
IPPROTO_ICMPv6
58
ICMPv6协议
IPPROTO_RAW
255
RAW协议
u_long optival = 1 ; isoctlsocket(s,SIO_RCVALL, &optival);
7 多线程函数 7.1 线程函数
终止线程的方式:
线程函数返回(最好)。
通过调用ExitThread函数,线程将自行撤销。
同一个进程的另一个线程调用TerminateThread函数。
包含线程的进程终止。
DWORD WINAPI ThreadFunc (LPVOID lpvThreadParm) ;HANDLE CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ) ;DWORD SuspendThread (HANDLE hThread) ;DWORD ResumeThread (HANDLE hThread) ;VOID Sleep (DWORD dwMilliseconds) ;VOID ExitThread (DWORD dwExitCode) ;BOOL TerminateThread (HANDLE hThread,DWORD dwExitCode) ;
7.2 线程通信
通过临界段通信:
临界段:以原子方式执行关键代码段。
关键代码段:是指一小段代码,同一个时刻,只能有一个线程具有访问权。
多个线程访问同一个临界区的原则
一次最多只能一个线程停留在临界区内。
不能让一个线程无限地停留在临界区内,否则其它线程将不能进入该临界区。
通过事件通信:
在Windows环境下,事件被理解为可以通过代码响应或处理的操作。
事件主要用于标识一个操作是否已经完成。
有两种不同类型的事件对象,一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
事件对象属于内核对象,它包含三个主要内容:
一个使用计数。
一个布尔值,指明该事件是自动复位事件(false),还是人工复位事件(true)。
一个布尔值,指明该事件是已通知状态(true),还是未通知状态(false) 。
事件实例
CRITICAL_SECTION cs; InitializeCriticalSection (&cs);EnterCriticalSection (&cs);LeaveCriticalSection (&cs);DeleteCriticalSection (&cs);
HANDLE WINAPI CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName ) ;BOOL WINAPI SetEvent (HANDLE hEvent) ;BOOL WINAPI ResetEvent (HANDLE hEvent) ;DWORD WaitForSingleObject (HANDLE hObject,DWORD dwMilliseconds) ;DWORD WaitForMultipleObjects ( DWORD nCount, const HANDLE* lpHandles, BOO bWaitAll, DWORD dwMilliseconds ) ;
三、拓展处理 1 TCP 1.1 TCP的流传输控制 1.2 面向连接程序的可靠性保护 2 UDP 2.1 可靠性 2.2 并发性
四、I/O操作 1 阻塞I/O模型
通信过程
初始化
recv()
return:
2 非阻塞I/O模型
通信过程:
初始化
recv()
return :
0:关闭
>0:请求—>重复
<0:WSAEWOULD重复 | 其他 处理错误
设置非阻塞:函数在第二章。
int iMode = 1 ;int iResult = ioctlsocket (s,FIONBIO,(u_long*)&iMode);
3 I/O复用模型
通信过程:等待事件
typedef struct fd_set { u_int fd_count; SOCKET fd_array[FD_SETSIZE]; }fd_set; in select ( __in int nfds, __int_out fd_set* readfds, __int_out fd_set* writefds, __int_out fd_set* execptfds, __in const struct timeval* timeout ) ;FD_CLR (s,*set);FD_ISSET (s,*set);FD_SET (s,*set);FD_ZERO (*set);
4 基于消息的WSAAsyncSelect模型 5 基于事件的WSAEventSelect模型
通信过程:等待事件
WSAEVENT WSACreateEvent (void ) ;BOOL WSAReserEvent (__in WSAEVENT hEvent) ;BOOL WSAReserEvent (__in WSAEVENT hEvent) ;BOOL WSACloseEvent ( __in WSAEVENT hEvent ) ;int WSAEventSelect ( __int SOCKET s, __in WSAEVENT hEventObject, __in long lNetworkEvents ) ;DWORD WSAWaitForMultipleEvents ( __in DWORD cEvents, __in const WSAEVENT* lphEvents, __in BOOL fWaitAll, __in DWORD dwTimeout, __in BOOL fAlertable ) ;typedef struct _WSANETWORKEVENTS { long lNetworkEvents; int iErrorCode[FD_MAX_EVENTS]; }WSANETWORKEVENTS,*LPWSANETWORKEVENTS; int WSAEnumNetworkEvents ( __in SOCKET s, __in WSAEVENT hEventObject, __out LPWSANETWORKEVENTS lpNetworkEvents ) ;
6 重叠I/O模型 7 完成端口模型
五、WinPcap编程 1 Winpcap基础 2 环境配置
问题:
配置流程:
C/C++ 常规:附加包含目录:WpdPack\Include
链接器 常规:附加库目录:WpdPack\Lib
(64位系统继续选择:[ \64x ]
)
链接器 输入:附加依赖项目: Packet.lib;wpcap.lib;ws2_32.lib
3 wpcap.dll 4 Packet.dll
六、端口扫描 1 原理 2 工具 3 实现
七、网络安全传输 1 数据传输
主要功能:使用DES和RSA混合加密传输网络数据。客户端为数据发送端,服务器端为数据接收端。
客户端主要步骤:
接收公钥并返回确认。
生成DES的密钥。
用户输入发送数据。
使用DES密钥加密数据。
使用RSA公钥加密DES密钥。
将将被加密的密钥和被加密的数据一起发送给服务器。
服务器端主要步骤:
生成RSA的公钥私钥对。
发送公钥给客户端。
接收被加密的密钥和数据并返回确认。
使用私钥解密DES密钥。
使用DES密钥解密数据。
显示数据。
八、实例
my::全部实例