现在的位置: 首页 > 综合 > 正文

33、Windows API 网络通信

2012年06月22日 ⁄ 综合 ⁄ 共 6331字 ⁄ 字号 评论关闭

    Socket就是系统提供的一种使用传输层的网络协议进行数据传输的程序设计接口规范,经过Socket封装的应用程序不再需要处理与TCP协议相关的内容。诸如连接时的三次握手、分包、包头解析、重传、滑动窗口等行为应用程序都不需关注,而只需要像使用系统中的其他I/O接口一样进行输入和输出操作即可。

IP HelperWindows系统与IP协议相关的配置和管理的重要接口,通过IP Helper可以实现一些在网络通信软件中常用的功能,比如获取本机IP网关,配置、获得统计信息、网卡数量和连接状态等。

Windows除了为网络通信程序设计提供Socket这样的传输层接口外,还提供了很多应用层协议接口,包括HttpSNMP等。此外还有DNSDHCP等常用网络协议。

1Socket通信

Socket套接字是使用传输程序协议(TCPUDP等)进行网络通信的程序设计接口。Socket程序分为服务端与客户端,服务端程序监听端口,等待客户端程序的连接;客户端程序发起连接,等待服务端的响应。客户端程序需要知道服务端程序的IP地址和监听端口。服务端与客户端在建立连接后,双方都可以发送和接收数据。

所有接口函数都由ws2_32.dll导出,相关数据类型、结构定义、函数声明等位于头文件winsock2.h中。

1)客户端

客户端的功能是连接到服务端。Socket的连接是由客户端发起的;客户端在连接服务端的指定端口时如果指定端口开放,而且服务端响应了客户端的连接请求,连接就会建立。客户端在连接前需知道服务端程序所监听的端口和服务端程序所在主机的IP地址。在连接建立后,需要得到Socket,然后就可以向服务端发送数据,或者从服务端接收数据。

1)客户端程序过程

一个Socket客户端程序的典型过程:

(1)客户端程序在运行后,首先需要使调用WSAStartup函数,确保进程加载socket应用程序所必须的环境和库文件,如Ws2_32.dll

(2)调用函数Socket创建SOCKET,在创建时需指定使用的网络协议、连接类型等。

(3)填充SOCKADDR结构,指定服务端的地址、端口等。

(4)调用connect函数连接到服务端。

(5)如果连接成功,就可以使用sendrecv函数发送和接收数据。

(6)在数据传输完成后,可调用closesocket函数关闭Socket

(7)调用WSACleanup函数释放资源。[2]

系统在实现TCP协议时都为数据的接收保留了缓存。协议收到数据包后,解包并将数据放入缓存中,直到recv函数将数据接收。如果recv函数长时间不接收数据,在协议的缓存存满之后,对方的send函数就不能再发送数据。

当调用recv函数后,如果协议栈缓存中的数据还没有recv接收缓存的大,那么缓存中的数据有多少recv函数接收多少,然后返回。如果当前协议缓存中没有数据,那么一旦有数据到达就接收到达的数据,然后返回。如果协议缓存中的数据大于recv接收缓存,那么recv将接收缓存填满之后返回。

2)服务端

服务端的功能在指定的端口上监听,等待客户端的连接。在连接建立后可使用sendrecv函数发送、接收数据。

一般情况下,Socket程序服务端过程如下:

(1)程序在运行后,首先需要使调用WSAStartup加载Ws2_32.dll

(2)调用函数socket创建用于监听的SOCKET,在创建时需指定使用的网络协议、连接类型等。

(3)调用bind函数将Socket绑定到网络地址和端口。

(4)调用listen函数开始监听。

(5)调用accept函数等待客户端连接。在客户端连接后,accept函数返回,得到连接Socket。在accept函数返回后,可立即再调用,以处理其他客户端的连接。

(6)得到连接Socket后,可调用sendrecv发送、接收数据。

(7)在数据传输完成后,可调用closesocket函数关闭Socket

(8)调用WSACleanup函数释放DLL

listen函数的功能是将Socket的状态设置为监听,以使客户端程序可以进行连接。accept函数的功能是接收客户端的连接,accept函数直到客户端有连接后才会返回。

3)相关API

Socket函数[3]

connect函数的功能是与服务端建立连接。这个函数只能由客户端程序调用。

示例Socket通信

Socket通信

* client.c
**************************************/
/* 头文件 */
#include
<stdio.h>
#include
"winsock2.h"
/* 常量 */
#define RECV_BUFFER_SIZE 8192
#pragma comment(lib,"ws2_32.lib")
/*************************************
* main
* 功能 socket通信客户端
*************************************
*/
void main(int argc, char* argv[])
{
// 变量定义
SOCKADDR_IN clientService;// 地址
SOCKET ConnectSocket;// socket
WSADATA wsaData;//
LPVOID recvbuf;// 接收缓存
int bytesSent;
int bytesRecv = 0;
char sendbuf[32] = "get information";// 默认发送的数据

// 初始化socket库, 保存ws2_32.dll已经加载
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != NO_ERROR)
printf(
"Error at WSAStartup()\n");

// 创建socket
ConnectSocket = socket(AF_INET, // IPv4
SOCK_STREAM, // 顺序的、可靠的、基于连接的、双向的数据流通信
IPPROTO_TCP// 使用TCP协议
);
if (ConnectSocket == INVALID_SOCKET)
{
printf(
"Error at socket(): %ld\n", WSAGetLastError());
WSACleanup();
return;
}

// 设置服务端的通信协议、IP地址、端口
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr
= inet_addr( "127.0.0.1" );
clientService.sin_port
= htons( 10000 );

// 连接到服务端
if ( connect(
ConnectSocket,
// socket
(SOCKADDR*) &clientService, // 地址
sizeof(clientService) // 地址的大小
) == SOCKET_ERROR)
{
printf(
"Failed to connect(%d)\n",WSAGetLastError() );
WSACleanup();
return;
}
// 准备发送数据
// 如果输入参数是-d,那么发送的数据是“download file”否则是"get information"
if(argc ==2 && (!lstrcmp(argv[1], "-d")))
{
lstrcpyn(sendbuf,
"download file", 32);
}
// 向服务端发送数据
bytesSent = send( ConnectSocket, // socket
sendbuf,// 发送的数据
lstrlen(sendbuf)+1,// 数据长度
0 );// 无标志

if(bytesSent == SOCKET_ERROR)
{
printf(
"send error (%d)\n", WSAGetLastError());
closesocket(ConnectSocket);
return;
}
printf(
"Bytes Sent: %ld\n", bytesSent );

// 准备接收数据
recvbuf = HeapAlloc(GetProcessHeap(), 0, RECV_BUFFER_SIZE);
// 循环接收
while( bytesRecv != SOCKET_ERROR )
{
//Sleep(50);
bytesRecv = recv( ConnectSocket, // socket
(char*)recvbuf, // 接收数据缓存
RECV_BUFFER_SIZE,// 缓存大小
0 );// 无标志
if ( bytesRecv == 0 )
{
printf(
"Connection Closed.\n");
break;
}
// TODO,处理接收的数据,这里只简单的将收到的数据大小显示
printf( "Bytes Recv: %ld\n", bytesRecv );
}
HeapFree(GetProcessHeap(),
0, recvbuf);
WSACleanup();
return;
}

* server.c
**************************************/
/* 头文件 */
#include
<winsock2.h>
#include
<ws2tcpip.h>
#include
<stdio.h>
/* 常量 */
#define DEFAULT_PORT "10000" // 端口
#define MAX_REQUEST 1024 // 接收数据的缓存大小
#define BUF_SIZE 4096 // 发送数据的缓存大小

#pragma comment(lib,"ws2_32.lib")
/*************************************
* CommunicationThread
* 功能 用于接收和发送数据的线程
* 为每一个连接的客户端创建一个接收发送数据的线程,
* 可以使用多个客户端同时连接到服务端
* 参数 lpParameter,SOKCET
*************************************
*/
DWORD WINAPI CommunicationThread(
LPVOID lpParameter
)
{
DWORD dwTid
= GetCurrentThreadId();
// 获得参数sokcet
SOCKET socket = (SOCKET)lpParameter;
// 为接收数据分配空间
LPSTR szRequest = (LPSTR)HeapAlloc(GetProcessHeap(),0, MAX_REQUEST);
int iResult;
int bytesSent;// 用于保存send的返回值,实际发送的数据的大小

// 接收数据
iResult = recv(socket, // socket
szRequest, // 接收缓存
MAX_REQUEST, // 缓存大小
0);// 标志
if (iResult == 0)// 接收数据失败,连接已经关闭
{
printf(
"Connection closing...\n");
HeapFree(GetProcessHeap(),
0 ,szRequest);
closesocket(socket);
return 1;
}
else if (iResult == SOCKET_ERROR)// 接收数据失败,socket错误
{
printf(
"recv failed: %d\n", WSAGetLastError());
HeapFree(GetProcessHeap(),
0 ,szRequest);
closesocket(socket);
return 1;
}
else if (iResult > 0) // 接收数据成功
{
// 显示接收到的数据
printf("\tCommunicationThread(%d)\tBytes received: %d\n", dwTid, iResult);
printf(
"\tCommunicationThread(%d)\trequest string is (%s)\n",dwTid, szRequest);

// 如果接收到的数据是"download file"
if (lstrcmpi(szRequest, "download file") == 0)
{
// 读取文件download.txt将发送
HANDLE hFile;
LPVOID lpReadBuf;
// 发送缓存
DWORD dwBytesRead;
DWORD dwFileSize;
DWORD dwSendFile
= 0;
hFile
= CreateFile("download.txt",
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);

if (hFile == INVALID_HANDLE_VALUE)
{
printf(
"\tCommunicationThread\tCould not open file (error %d)\n",
GetLastError());
send(socket,
"error", 6, 0);
closesocket(socket);
return 1;
}
// 分配发送数据缓存
lpReadBuf = HeapAlloc(GetProcessHeap(), 0 , BUF_SIZE);
// 获取文件大小
dwFileSize = GetFileSize(hFile, NULL);
// 循环发送
while(1)
{
// 读文件到缓存
if(!ReadFile(hFile, lpReadBuf, BUF_SIZE, &dwBytesRead, NULL))
{
printf(
"\tCommunicationThread\tCould not read from file (error %d)\n",
GetLastError());
closesocket(socket);
CloseHandle(hFile);
return 1;
}
// 发送读取的文件数据
bytesSent = send(socket, (const char *)lpReadBuf, dwBytesRead, 0);
if( bytesSent == SOCKET_ERROR)
{
printf(
"\tCommunicationThread\tsend error %d\n",
WSAGetLastError());
closesocket(socket);
CloseHandle(hFile);
return 1;
}
// 显示发送数据的大小
printf("\tCommunicationThread(%d)\tsend %d bytes\n", dwTid, bytesSent);
// 累加,已经发送的大小
dwSendFile += dwBytesRead;
// 如果所有文件数据都已经发送
if(dwSendFile == dwFileSize)
{
printf(
"\tCommunicationThread\tFile download ok\n");
break;// 退出循环
}
}
// 释放内存、关闭连接,关闭文件
HeapFree(GetProcessHeap(), 0 , lpReadBuf);
CloseHandle(hFile);
closesocket(socket);
}
// 如果接收到的数据是"get information"
else if (lstrcmpi(szRequest, "get information") == 0)
{
// 发送数据
bytesSent = send(socket, // socket
"this is information", // 数据
lstrlen("this is information"

抱歉!评论已关闭.