您的位置:首页 > 理论基础 > 计算机网络

网络编程 C++ ———WinSock

2012-04-22 00:09 169 查看
网络编程 C++ ———WinSock

Windows Socket (即Winsock)以U.C. Berkeley大学BSD UNIX中流行的Socket接口为范例定义了一套microsoft Windows下网络编程接口。它不仅包含了人们所熟悉的Berkeley Socket风格的库函数;也包含了一组针对Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。

通信的基础是套接口(Socket),一个套接口是通讯的一端。包含5种信息:

本地协议端口
本地主机地址
远程协议端口
远程主机地址
协议 (程序在网络上传输数据是使用的协议)

套接字分类:

流式套接字 (SOCK_STREAM)

提供了双向、有序的、无重复的以及无记录边界的数据流服务,适合处理大量数据。它是面向联结的,必须建立数据传输链路,同时还必须对传输的数据进行验证, 确保数据的准确性。因此,系统开销较大。
数据报套接字 (SOCK_DGRAM)

数据报套接字也支持双向的数据流,但不保证传输数据的准确性,但保留了记录边界。由于数据报套接字是无联接的,例如广播时的联接,所以并不保证接收端是否正在侦听。数据报套接字传输效率比较高。
原始套接字 (SOCK_RAW)

原始套接字保存了数据包中的完整IP头,前面两种套接字只能收到用户数据。因此可以通过原始套接字对数据进行分析。支持更底层的协议 ,多用于实验新的协议和抓包嗅探。

套接字结构体:
struct sockaddr {
u_short sa_family; /* address family */
char sa_data[14]; /* up to 14 bytes of direct address */
};
该结构体地址为通用Socket地址结构 。sa_family 可以选择不同的通信域。 下面的sockaddr_in则是专门针对Internet域的Socket地址结构体。
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;

主机地址结构体:
struct hostent {
char FAR * h_name; /* official name of host */
char FAR * FAR * h_aliases; /* alias list */
short h_addrtype; /* host address type */
short h_length; /* length of address */
char FAR * FAR * h_addr_list; /* list of addresses */
#define h_addr h_addr_list[0] /* address, for backward compat */
};


h_name为主机名字。
h_aliases为主机别名列表。
h_addrtype为地址类型。
h_length为地址类型。
h_addr_list为IP地址,如果该主机有多个网卡,就包括地址的列表。

数据类型的基本定义:

typedef unsigned char u_char;

typedef unsigned short u_short;

typedef unsigned int u_int;

typedef unsigned long u_long;

几个常用的字节(网络 与主机)转换函数:
unsigned long inet_addr(const char FAR * cp )

用法: unsigned long addr=inet_addr("192.1.8.84")
u_short ntohs(u_short nPort ) network to host short
u_short htons(u_short nPort )

u_long htonl(u_long nAddr)
u_long
ntohl(u_long nAddr)

WinSock 编程需要包含头文件Winsock2.0 和库文件WS2_32.LIB 和动态库WS2_32.DLL。在使用其库函数之前必须加载套接字库。
int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );wVersionRequested

   wVersionRequested:一个WORD(双字节)型数值,指定了应用程序需要使用的Winsock规范的最高版本。

  lpWSAData 指向WSADATA数据结构的指针,用来接收Windows Sockets实现的细节。

在应用程序关闭套接字之后,应该调用WSACleanUp()终止Winsock DLL的调用,释放资源,以备下一次使用。

Winsock API

SOCKET socket(int af,int type,int protocol)
int bind( SOCKET s, const struct sockaddr FAR * name, int namelen )
int listen(SOCKET s, int backlog )
SOCKET accept( SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen )
int connect(SOCKET s, const struct sockaddr FAR * name, int namelen )

int send(SOCKET s, const char FAR * buf, int len, int flags )

int recv( SOCKET s, char FAR * buf, int len, int flags )

int shutdown(SOCKET s, int how)
int closesocket( SOCKET s )

int getsockopt(SOCKET s, int level, int optname, char FAR * optval, int FAR * optlen)

读取socket属性,s为欲读取属性的套接字。level为套接字选项的级别,大多数是特定协议和套接字专有的。如IP协议应为 IPPROTO_IP。

optname为读取选项的名称

optval为存放选项值的缓冲区指针。

optlen为缓冲区的长度

用法:

int ttl=0; //读取TTL值

int rc = getsockopt( s, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl));

int setsockopt(SOCKET s,int level, int optname,const char FAR * optval, int optlen)

设置socket属性,s为欲设置属性的套接字。

level为套接字选项的级别,用法同上。

optname为设置选项的名称

optval为存放选项值的缓冲区指针。

optlen为缓冲区的长度

用法:

int ttl=32; //设置TTL值

int rc = setsockopt( s, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl));

流式套接字编程模型:



数据报套接字编程模型:



WinSock的TCP通信 例子程序(VC++6.0 实现):
//TCPSrv
#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib,"WS2_32.lib")

int InitSock(BYTE minorVer=2,BYTE majorVer=2)
{
WSADATA wsadata;
WORD sockVersion=MAKEWORD(minorVer,majorVer);
if(::WSAStartup(sockVersion,&wsadata)!=0)
{
printf("failed WSAStarUp() ");
return 0;
}
return 1;
}

void UnInitSock()
{
::WSACleanup();
}

int main()
{
if(!InitSock())
return 0;
SOCKET sListen=::socket (AF_INET,SOCK_STREAM,IPPROTO_IP);
if(sListen==INVALID_SOCKET)
{
printf("failed socket()");
return 0;
}
// 填充地址结构体
sockaddr_in sAddr;
sAddr.sin_family=AF_INET;
sAddr.sin_port=htons(7565);
sAddr.sin_addr.S_un.S_addr=INADDR_ANY;

if(::bind (sListen,(LPSOCKADDR)&sAddr,sizeof(sAddr))==SOCKET_ERROR)
{
printf("failed bind()");
return 0;
}

if(::listen (sListen,2)==SOCKET_ERROR)
{
printf("failed listen()");
return 0;
}

sockaddr_in cAddr;
int nAddrLen=sizeof(cAddr);
SOCKET sClient;
char szText[]="TCP Server Demo! \r\n";
while(TRUE)
{
sClient=::accept(sListen,(SOCKADDR*)&cAddr,&nAddrLen);
if(sClient==INVALID_SOCKET)
{
printf("failed accpet()");
continue;
}
printf("½ÓÊÕµ½Ò»¸öÁ¬½Ó£º%s \r\n",inet_ntoa(cAddr.sin_addr));

::send(sClient,szText,strlen(szText),0);
::closesocket(sClient);
}

::closesocket(sListen);
UnInitSock();
return 0;
}


//TCPClient

#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib,"WS2_32.lib")

int InitSock(BYTE minorVer=2,BYTE majorVer=2)
{
WSADATA wsadata;
WORD sockVersion=MAKEWORD(minorVer,majorVer);
if(::WSAStartup(sockVersion,&wsadata)!=0)
{
printf("failed WSAStarUp() ");
return 0;
}
return 1;
}

void UnInitSock()
{
::WSACleanup();
}

int main()
{
if(!InitSock())
return 0;
SOCKET s=::socket (AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(s==INVALID_SOCKET)
{
printf("failed socket()");
return 0;
}

sockaddr_in servAddr;
servAddr.sin_family=AF_INET;
servAddr.sin_port=htons(7565);
servAddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); //ÔÚ±¾»ú²âÊÔ £¬ÔÚÁ½Ì¨²»Í¬»úÆ÷ÔÚʹÓÃÏìÓ¦µÄIpµØÖ·¼´¿É

if(::connect(s,(sockaddr*)&servAddr,sizeof(servAddr))==-1)
{
printf("failed connect()");
return 0;
}

char buff[256];
int nRecv=::recv(s,buff,256,0);
if(nRecv>0)
{
buff[nRecv]='\0';
printf("接收到数据%s",buff);
}
::closesocket(s);
UnInitSock();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: