首页
社区
课程
招聘
[原创]Winsock 初探
发表于: 2008-10-17 11:28 8207

[原创]Winsock 初探

2008-10-17 11:28
8207
在编写Windows网络应用程序的时候,最常用的便是Winsock接口,注意它不是网络协议,你可以理解它为网络应用API。在百度百科中是这样介绍的:

” Windows下网络编程的规范-Windows Sockets是Windows下得到广泛应用的、开放的、支持多种协议的网络编程接口。从1991年的1.0版到1995年的2.0.8版,经过不断完善并在Intel、Microsoft、Sun、SGI、Informix、Novell等公司的全力支持下,已成为Windows网络编程的事实上的标准。 “

实际上Windows95以后的各个windows版本都支持Winsock2.2版本,但是Window CE 只支持1.1版本。Winsock2.2提供了更多的功能和API函数,而且区分两个版本的函数非常容易,2.2版本的API函数都以WSA开头的,除了WSAStartup, WSACleanup, WSARecvEx, 和 WSAGetLastError这几个函数也出现在1.1版本中。

使用Winsock写应用程序所需要的头文件和类库有:
// 对于1.1版本
#include<winsock.h>
#pragma comment(lib, "wsock32.lib")
// 对于2.2版本
#include<winsock2.h>
#pragma comment(lib, "ws2_32.lib")
现在我们来看看一个通用的Winsock应用程序框架:
#include<winsock2.h>  // 头文件
#pragma comment(lib, "ws2_32.lib") // 库文件加载
void main(void)
{
   WSADATA wsaData;  // WSADATA 结构体主要包含了系统所支持的Winsock版本信息         
   
   // 初始化Winsock 2.2。使用WSAStartup函数,第一个参数是所要用的Winsock版本号
   // 第二个参数就是WSADATA结构体的指针。如果初始化成功则返回0
   // 要注意任何WinsockAPI函数都必须在初始化后使用,包括错误检查函数
   // WSAGetLastError (用于查看出错详细信息)
   if( WSAStartup( MAKEWORD(2,2), &wsaData) != 0 )
   {
      printf( "WSAStartup 无法初始化!");
      return;
   }
    // winsock 应用代码

    // 最后应该做一些清除工作
    if( WSACleanup() == SOCKET_ERROR )
        printf( "WSACleanup 出错!");
}
这里需要说明的有两点:
1.WSADATA结构体:
typedef struct WSAData
{
    WORD           wVersion;
    WORD           wHighVersion;
    char           szDescription[WSADESCRIPTION_LEN + 1];
    char           szSystemStatus[WSASYS_STATUS_LEN + 1];
    unsigned short iMaxSockets;
    unsigned short iMaxUdpDg;
    char FAR *     lpVendorInfo;
} WSADATA, * LPWSADATA;
wVersion 就是对应你所要的版本,比如1.2,高位字节对应2,低位字节对应1。
wHiVersion 是系统所支持的最高版本,如果初始化失败,这个参数仍然返回。

其他成员基本都用不到,所以不必关心。

2.对应每一个WSAStartup都要有一个WSACleanup与之对应

[Winsock 初探II ]

前些天写了Winsock 初探一文,现在继续往下写。本文的目的不是想介绍一个完整的网络应用程序,而只是作为一个初步介绍如何写一个网络程序(基本思路),正因为是初探,所以代码比较简单,为了使代码清晰起见,我省略了错误检查代码部分,但这不意味着错误检查不重要。

写一个网络应用程序分为两个方面:服务器程序和客户端程序,大家可以在一个solution中建立srv和client两个projects。

首先回忆一下程序框架,见Winsock 初探 一文

#include<winsock2.h> // 头文件
#pragma comment(lib, "ws2_32.lib") // 库文件加载
void main(void)
{
WSADATA wsaData; // WSADATA 结构体主要包含了系统所支持的Winsock版本信息

// 初始化Winsock 2.2。使用WSAStartup函数,第一个参数是所要用的Winsock版本号
// 第二个参数就是WSADATA结构体的指针。如果初始化成功则返回0
// 要注意任何WinsockAPI函数都必须在初始化后使用,包括错误检查函数
// WSAGetLastError (用于查看出错详细信息)


if( WSAStartup( MAKEWORD(2,2), &wsaData) != 0 )
{
printf( "WSAStartup 无法初始化!");
return;
}

// winsock 应用代码

// 最后应该做一些清除工作
if( WSACleanup() == SOCKET_ERROR )
printf( "WSACleanup 出错!");

}
现在我们的任务是填写红色的部分,即winsock应用代码

1. 简单TCP/IP

思路:
[服务器程序]

建立socket -----> 绑定bind------>监听listen------>接受accept------>发送和接收send 和 recv------>关闭closesocket

[客户端程序]


建立socket -----> 连接connect------>发送和接收send 和 recv------>关闭closesocket

代码:

[服务器程序]

/*使用IP地址家族就必须要用AF_INET,由于我们要建立可靠的传送,因此我们选择SOCK_STREAM,基于数据流的传送,最后一个参数表示使用TCP协议,实际上这个参数可以设置为0,让程序自己根据情况判断填写*/

SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP )

/*绑定的意思是把socket绑定到IP地址上,所以我们需要一个SOCKADDR结构来存放IP地址信息,这里为了方便填写可以使用SOCKADDR_IN结构,这个结构的大小和SOCKADDR一样,可以强制类型转换到SOCKADDR类型,填写给bind函数的参数。这里要注意的是要把IP地址和端口号转化为网络存储顺序,即高位存放在低字节内存中,可以用htonl和htons函数转化*/

SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_addr.s_addr = htonl( INADDR_ANY); // INADR_ANY表示使用默认IP地址
addrSrv.sin_port = htons( 27015 );//尽量不要使用在1024以下的端口

bind( sockSrv, (SOCKADDR*)&addrSrv, sizeof(addrSrv) );

/*监听,第二个参数表示等待序列的最大个数,就好比最多只能容纳5个人排队,第六个就被拒绝排队了。*/
listen( sockSrv, 5 );

/* 接受, 就好比处理房间中第一个人的事务,那么这个队列就空出一个人的位置,其他人还可以来排队。这里通过accept函数可以返回一个表示连接进来的客户的socket,并且通过传递参数可以知道客户端的IP地址信息*/

SOCKADDR_IN addrClient;
int len = sizeof(addrClient);

// 循环接受客户,这里没有写退出条件,实际上退出条件可以来自判断所读入的信息
while(1)
{
SOCKET sockClient = accept( sockSrv, (SOCKADDR*)&adrClient, &len );

// 发送和接收数据 char sendbuf[32] = "Server: Sending Data.";
char recvbuf[32] = "";

// recv 和 send的参数都是 socket,buffer,buffer的大小以及一个flag,具体可参考MSDN
bytesRecv = recv( sockClient, recvbuf, 32, 0 );
printf( "Recv: %s\n", recvbuf );

bytesSent = send( sockClient, sendbuf, strlen(sendbuf), 0 );

}

// 关闭socket
closesocket(sockClient);
closesocket(sockSrv);

[客户端程序]

SOCKET sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP )

SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_addr.s_addr = htonl( INADDR_ANY); // INADR_ANY表示使用默认IP地址
addrSrv.sin_port = htons( 27015 );//尽量不要使用在1024以下的端口

// 连接
connect( sockSrv, (SOCKADDR*)&addrSrv, sizeof(addrSrv) );

// 发送和接收数据
char sendbuf[32] = "Client: Sending Data.";
char recvbuf[32] = "";

// recv 和 send的参数都是 socket,buffer,buffer的大小以及一个flag,具体可参考MSDN
bytesRecv = recv( sockClient, recvbuf, 32, 0 );
printf( "Recv: %s\n", recv);

bytesSent = send( sockClient, sendbuf, strlen(sendbuf), 0 );

// 关闭socket
closesocket(sockClient);

2. 简单UDP/IP

思路:

[服务器程序]

建立socket -----> 绑定bind------>发送和接收sendto 和 recvfrom------>关闭closesocket

[客户端程序]

建立socket -----> 发送和接收send 和 recv------>关闭closesocket

UDP的实现更加简单,服务器只要绑定就可以发送接收信息,而客户端只要有socket就可以了。具体的实现代码可以参考MSDN中的例子。

要注意的是
TCP中用SOCK_STREAM 建立socket,并且使用send和recv函数发送接收信息。
UDP中用SOCK_DGRAM 建立socket,并且使用sendto和recvfrom来发送和接收信息。sendto 比send多几个参数,主要是对方的IP地址信息,recvfrom也是一样比recv多几个参数。

以上所讨论的仅仅是为了显示一下编写网络应用程序基本思路,至于具体实际的代码还是要看看具体的源代码,如果要使用图形界面则所要用的函数和类会更加多。

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
免费 7
支持
分享
最新回复 (7)
雪    币: 237
活跃值: (31)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持楼主写完,偶也一直在关注这方面的内容,可没撒成果
2008-10-17 13:31
0
雪    币: 247
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
谢谢支持,我会继续写下去。

我也是初学Winsock

主要参考《 Network Programming for Microsoft Windows - Second Edition 》一书。
2008-10-17 13:47
0
雪    币: 148
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
跟你一起学习,谢谢
2008-10-20 14:30
0
雪    币: 234
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
支持楼主继续写下去
2008-10-20 20:09
0
雪    币: 189
活跃值: (4810)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
支持楼主继续写下去!!!
2008-10-22 23:27
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
收藏一下,希望继续写下去!

随便小问一下 HOSTS 到底是 谁读的?WS2_32.DLL里有 读取的字样!

于是NOP掉  HOSTS

文件竟然还被读取到!水平有限希望有牛人解答一下!
2009-1-22 16:00
0
雪    币: 2384
活跃值: (766)
能力值: (RANK:410 )
在线值:
发帖
回帖
粉丝
8
支持楼主继续完成他。
2009-1-22 16:26
0
游客
登录 | 注册 方可回帖
返回
//