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

第1章 Winsock简介

2013年10月09日 ⁄ 综合 ⁄ 共 7181字 ⁄ 字号 评论关闭

Microsoft Windows网络编程第2版    杨合庆 译         清华大学出版社

对应的英文书名:Network Programming for Microsoft Windows 2nd

1 winsock是网络接口,不是协议。

2 Winsock有1和2两个版本,通过函数前缀WSA可以区分,Winsock2函

  数前有WSA前缀。但有几个例外WSAStartup、WSACleanup、WSARecvEx、

  WSAGetLastError属于1.1规范。

3 WinCE只支持Winsock1

      winsock1   winsock.h      wsock32.lib

      winsock2   winsock2.h     ws2_32.lib

      编程扩展   mswsock.h      mswsock.dll

4 使用WSAStartup进行Winsock初始化,具体用法见MSDN和书本

  结束时使用WSACleanup释放由Winsock分配资源,也可以让操作系统自动

  释放,但这不符合Winsock规范。

5 SOCKADDR_IN用来指定IP和端口号用

  struct sockaddr_in {

    short sin_family;           // 必须为AF_INET

    unsigned short sin_port;    // 端口号

    struct in_addr sin_addr;    // IP,可用inet_add(char* )得IP

    char sin_zero[8];           // 填充,使其与SOCKADDR结构长度一样

  };

6 Inter86处理器上,多字节是小端(little-endian),Internet联网标准的字

  节序是大端模式(big-endian),称为网络字节序(network-byte)。

  主机字节序转网络字节序函数

        htonl、WSAHtonl、htons、WSAHtons

  网络字节序转主机字节序

        ntohl、WSANtohl、ntohs、WSANtohs

7 创建套接字函数

  socket、WSASocket,创建完套接字后可用下面函数控制套接字的选项和套接字

  行为:setsockopt、getsockopt、ioctlsocket、WSAIoctl

下面是TCP服务器端程序示例

// 模块名:tcpserver.cpp
//
// 描  述:此例子演示了如何开发一个简单的TCP服务器应用程序,
//         此应用程序在5150端口上监听TCP连接并接收数据。此
//         例子以控制台的形式实现,当接受连接或接收到数据时
//         只是简单的在控制台上打印出信息。
//
// 编  译:注意要设置ws2_32.lib库,工程-->设置-->连接,将
//         ws2_32.lib添加到“对象/库模块”的最后。
#include <winsock2.h>
#include <stdio.h>

void main(void)
{
	// 初始化Winsock,使用2.2版本
	int     Ret;
	WSADATA wsaData;
	if ((Ret = WSAStartup(MAKEWORD(2, 2), &wsaData) != 0))
	{
		// 注意:WSAStartup加载失败,我们不能像平常那样使用WSAGetLastError
		//       来得到错误代码,但可以根据返回状态来判断
		printf("WSAStartup失败,错误代码 %d\n", Ret);
		return;
	}

	// 创建socket
	SOCKET ListeningSocket;
	if ((ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
	{
		printf("创建socket失败,错误代码 %d\n", WSAGetLastError());
		WSACleanup();
		return;
	}

	// 设置SOCKADDR_IN结构体,注意要将端口号转换为网络字节序 
	SOCKADDR_IN ServerAddr;
	int         Port = 5150;
	ServerAddr.sin_family = AF_INET;
	ServerAddr.sin_port = htons(Port);
	ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);

	// 将地址信息与socket绑定
	if (bind(ListeningSocket, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
	{
		printf("绑定失败,错误代码 %d\n", WSAGetLastError());
		closesocket(ListeningSocket);
		WSACleanup();
		return;
	}

	// 监听客户端连接,排队队列长度为5
	if (listen(ListeningSocket, 5) == SOCKET_ERROR)
	{
		printf("监听失败,错误代码 %d\n", WSAGetLastError());
		closesocket(ListeningSocket);
		WSACleanup();
		return;
	}

	printf("正在端口 %d 上等待连接。\n", Port);

	SOCKADDR_IN ClientAddr;
	int         ClientAddrLen = sizeof(ClientAddr);
	SOCKET      NewConnection;
	if ((NewConnection = accept(ListeningSocket, (SOCKADDR*)&ClientAddr, &ClientAddrLen)) == INVALID_SOCKET)
	{
		printf("接受连接失败,错误代码 %d\n", WSAGetLastError());
		closesocket(ListeningSocket);
		WSACleanup();
		return;
	}

	printf("从 %s:%d 接收到一个连接。\n", inet_ntoa(ClientAddr.sin_addr), ntohs(ClientAddr.sin_port));

	// 到此为止,有两种选择:一个是使用ListeningSocket继续accept客户端连接并
	// 收发数据。另一种是关闭ListeningSocket不再接收客户端连接。在这我们选择
	// 不再接收客户端连接,仅是简单的与刚建立的连接NewConnection进行收发数据
	closesocket(ListeningSocket);

	// 使用NewConnection进行收发数据,简单起见我们只是收一些数据并报告接收到的长度
	char DataBuffer[1024];
	printf("正在等待接收数据...\n");
	if ((Ret = recv(NewConnection, DataBuffer, sizeof(DataBuffer), 0)) == SOCKET_ERROR)
	{
		printf("接收数据失败,错误代码 %d\n", WSAGetLastError());
		closesocket(NewConnection);
		WSACleanup();
		return;
	}
	printf("成功接收 %d 字节数据\n", Ret);

	// 不再做任何事情,所以关闭客户端连接
	printf("关闭客户端连接\n");
	closesocket(NewConnection);

	// 程序处理完连接后调用WSACleanup
	WSACleanup();
}

下面是TCP客户端程序示例

// 模块名:tcpclient.cpp
//
// 描  述:此例子演示了如何开发一个简单的TCP客户端应用程序,
//         此程序只是简单的往服务器监听的5150端口上发送一个
//         "hello"信息。此例子以控制台的形式实现,当连接成功
//         或将数据发送到服务器时只是简单的在控制台上打印出信息。
//         
// 编  译:注意要设置ws2_32.lib库,工程-->设置-->连接,将
//         ws2_32.lib添加到“对象/库模块”的最后。
#include <winsock2.h>
#include <stdio.h>

void main(int argc, char** argv)
{
	// 必须为IP作为命令行参数
	if (argc <= 1)
	{
		printf("用法:tcpclient <服务器IP>。\n");
		return;
	}

	// 初始化Winsock,使用2.2版本
	int     Ret;
	WSADATA wsaData;
	if ((Ret = WSAStartup(MAKEWORD(2, 2), &wsaData) != 0))
	{
		// 注意:WSAStartup加载失败,我们不能像平常那样使用WSAGetLastError
		//       来得到错误代码,但可以根据返回状态来判断
		printf("WSAStartup失败,错误代码 %d\n", Ret);
		return;
	}

	// 创建socket
	SOCKET s;
	if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
	{
		printf("创建socket失败,错误代码 %d\n", WSAGetLastError());
		WSACleanup();
		return;
	}

	// 设置SOCKADDR_IN结构体,注意要将端口号转换为网络字节序 
	SOCKADDR_IN ServerAddr;
	int         Port = 5150;
	ServerAddr.sin_family = AF_INET;
	ServerAddr.sin_port = htons(Port);
	ServerAddr.sin_addr.s_addr = inet_addr(argv[1]);

	printf("正在连接 %s:%d ...\n", inet_ntoa(ServerAddr.sin_addr), ntohs(ServerAddr.sin_port));
	if (connect(s, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
	{
		printf("无法连接,错误代码 %d\n", WSAGetLastError());
		closesocket(s);
		WSACleanup();
		return;
	}
	printf("连接成功。\n");

	printf("尝试发送“hello”信息\n");
	if ((Ret = send(s, "Hello", 5, 0)) == SOCKET_ERROR)
	{
		printf("发送失败,错误代码 %d\n", WSAGetLastError());
		closesocket(s);
		WSACleanup();
		return;
	}
	printf("成功发送 %d 字节数据\n", Ret);

	// 不再做任何事情,所以关闭客户端连接
	printf("关闭连接\n");
	closesocket(s);

	// 程序处理完连接后调用WSACleanup
	WSACleanup();
}

UDP接收端示例

// 模块名:udpreceiver.cpp
//
// 描  述:此例子演示了如何开发一个简单的UDP接收程序,
//         它在5150端口上等待数据包。例子以控制台的
//         形式实现,当接收到数据时只是简单的在控制
//         台上打印出信息。
//
// 编  译:注意要设置ws2_32.lib库,工程-->设置-->连接,将
//         ws2_32.lib添加到“对象/库模块”的最后。
#include <winsock2.h>
#include <stdio.h>

void main(int argc, char** argv)
{
	// 初始化Winsock,使用2.2版本
	int     Ret;
	WSADATA wsaData;
	if ((Ret = WSAStartup(MAKEWORD(2, 2), &wsaData) != 0))
	{
		// 注意:WSAStartup加载失败,我们不能像平常那样使用WSAGetLastError
		//       来得到错误代码,但可以根据返回状态来判断
		printf("WSAStartup失败,错误代码 %d\n", Ret);
		return;
	}
	
	// 创建socket
	SOCKET ReceivingSocket;
	if ((ReceivingSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
	{
		printf("创建socket失败,错误代码 %d\n", WSAGetLastError());
		WSACleanup();
		return;
	}
	
	// 设置SOCKADDR_IN结构体
	SOCKADDR_IN ReceiverAddr;
	int         Port = 5150;
	ReceiverAddr.sin_family = AF_INET;
	ReceiverAddr.sin_port = htons(Port);
	ReceiverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

	// 将地址信息与socket绑定
	if (bind(ReceivingSocket, (SOCKADDR*)&ReceiverAddr, sizeof(ReceiverAddr)) == SOCKET_ERROR)
	{
		printf("绑定失败,错误代码 %d\n", WSAGetLastError());
		closesocket(ReceivingSocket);
		WSACleanup();
		return;
	}
	
	printf("正在端口 %d 上等待接收数据包。\n", Port);
	
	char        ReceiveBuf[1024];
	int         BufLength = 1024;
	SOCKADDR_IN SenderAddr;
	int         SenderAddrSize = sizeof(SenderAddr);
	if ((Ret = recvfrom(ReceivingSocket, ReceiveBuf, BufLength, 0,
		(SOCKADDR*)&SenderAddr, &SenderAddrSize)) == INVALID_SOCKET)
	{
		printf("recvfrom失败,错误代码 %d\n", WSAGetLastError());
		closesocket(ReceivingSocket);
		WSACleanup();
		return;
	}
	
	printf("从 %s:%d 接收 %d 字节数据\n", inet_ntoa(SenderAddr.sin_addr), ntohs(SenderAddr.sin_port), Ret);
	
	closesocket(ReceivingSocket);
	
	// 程序处理完连接后调用WSACleanup
	WSACleanup();
}

UDP发送端示例

// 模块名:udpsender.cpp
//
// 描  述:此例子演示了如何开发一个简单的UDP发送程序,
//         它会向接收端5150端口上发送一个"hello"消息。
//         例子以控制台的形式实现,当信息发送到服务器时
//         只是简单的在控制台上打印出信息。
//
// 编  译:注意要设置ws2_32.lib库,工程-->设置-->连接,将
//         ws2_32.lib添加到“对象/库模块”的最后。
#include <winsock2.h>
#include <stdio.h>

void main(int argc, char** argv)
{
	// 必须为IP作为命令行参数
	if (argc <= 1)
	{
		printf("用法:udpsender <接收端IP>。\n");
		return;
	}
	
	// 初始化Winsock,使用2.2版本
	int     Ret;
	WSADATA wsaData;
	if ((Ret = WSAStartup(MAKEWORD(2, 2), &wsaData) != 0))
	{
		// 注意:WSAStartup加载失败,我们不能像平常那样使用WSAGetLastError
		//       来得到错误代码,但可以根据返回状态来判断
		printf("WSAStartup失败,错误代码 %d\n", Ret);
		return;
	}
	
	// 创建socket
	SOCKET SendingSocket;
	if ((SendingSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
	{
		printf("创建socket失败,错误代码 %d\n", WSAGetLastError());
		WSACleanup();
		return;
	}
	
	// 设置SOCKADDR_IN结构体
	SOCKADDR_IN ReceiverAddr;
	int         Port = 5150;
	ReceiverAddr.sin_family = AF_INET;
	ReceiverAddr.sin_port = htons(Port);
	ReceiverAddr.sin_addr.s_addr = inet_addr(argv[1]);
	
	// 给接收端发送数据包
	if ((Ret = sendto(SendingSocket, "Hello", 5, 0, (SOCKADDR*)&ReceiverAddr, sizeof(ReceiverAddr))) == SOCKET_ERROR)
	{
		printf("发送失败,错误代码 %d\n", WSAGetLastError());
		closesocket(SendingSocket);
		WSACleanup();
		return;
	}
	printf("成功向 %s:%d 发送 %d 字节数据\n", inet_ntoa(ReceiverAddr.sin_addr), ntohs(ReceiverAddr.sin_port), Ret);
	
	// 发送完数据,关闭连接
	closesocket(SendingSocket);
	
	// 程序处理完连接后调用WSACleanup
	WSACleanup();
}

抱歉!评论已关闭.