WinSock网络编程基础


Winsock是Windows环境下的网络编程接口,它最初基于UNIX环境下的BSD Socket,是一个与网络协议无关的编程接口。在Visual Studio中可以使用WinSock API开发网络应用程序,实现计算机之间的通信。

构建WinSock应用程序框架

在使用WinSock 2.2实现网络通信功能时,需要引用头文件winsock2.h和库文件ws2_32.lib,代码如下:

#include <winsock.h>
#pragma comment(lib, "wsock32.lib")

WSADATA是一种数据结构,用来存储被WSAStart函数调用后返回的Windows Sockets数据,包含了系统所支持的Winsock版本信息

WSAStartup()函数用于初始化Windows Sockets,并返回WSADATA结构体。只有调用WSAStartup()函数后,应用程序才能调用其它Windows Sockets API 函数,实现网络通信

下面是使用WinSock2.2实现网络通信的应用程序框架

#include <winsock.h>
#pragma comment(lib, "wsock32.lib")
//主函数
int main()
{
	WSADATA wsaData;
	//初始化WinSock2.2
	if (WSAStartup(MAKEWORD(2,2), &wsaData) !=0)
	{
		printf("初始化失败");
		return 0;
	}
	//使用WinSock实现网络通信
	//......
	//最后应该做一些清除工作
	if (WSACleanup() == SOCKET_ERROR)
	{
		printf("WSACleanup出错!");
		return 0;
	}
}

Socket函数

socket()函数

socket()函数用于创建与指定的服务提供者绑定嵌套字,函数原型如下:

SOCKET socket(
	int af,				//指定协议的地址家族,通常使用AF_INET
	int type,			//指定套接字的类型
	int protocol		//原始套接字,可以用于接收本机网卡上的数据帧或数据包
);
套接字类型 说明
SOCK_STREAM 提供顺序、可靠、双向和面向连接的字节流数据传输机制,使用TCP
SOCK_DGRAM 支持无连接的数据报,使用UDP
SOCK_RAW 原始套接字,可以用于接收本机网卡上的数据帧或者数据包

如果函数执行成功,则返回新Socket的句柄;如果调用失败,则返回INVALID_SOCKET

创建Socket代码如下:

SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
//可以将 protocol 的值设为 0,系统会自动根据嵌套字类型推演出应该使用什么协议
if (s == INVALID_SOCKET)
{
	printf("socket error!");
}

bind()函数

bind()函数可以将本地地址与一个Socket绑定在一起,函数原型如下:

int bind(
	SOCKET s,								//标识一个未绑定的Socket的描述符
	const struct sockaddr FAR* name,		//绑定到Sockets的sockaddr结构体地址
	int namelen								//参数name的长度
);

如果未发生错误,则函数返回0;否则返回SOCKET_ERROR
bind()函数应用在未连接的socket上,在调用connect()和listen()函数之前被调用。

使用bind()函数绑定Socket到本地地址的1234端口,代码如下:

//指定绑定地址
struct sockaddr_in addr;
//定义服务器地址
addr.sin_family = PF_INET;  					//使用IPv4地址
addr.sin_addr.s_addr = htonl(INADDR_ANY); 		//自动获取IP地址
addr.sin_port = htons(1234);  					//端口
//绑定到Socket
bind(s, (SOCKADDR*)&addr, sizeof(SOCKADDR));
if ( errCode == SOCKET_ERROR)
{
	printf("bind error!");
	exit(1);
}

listen()函数

listen()函数可以将嵌套字设置为监听接入连接的状态,函数原型如下:

int listen(
	SOCKET s,		//指定一个已经绑定但尚未链接的套接字
	int backlog		//指定等待连接队列的最大长度
);

如果函数执行成功,则返回0;否则返回SOCKET_ERROR

accept()函数

在服务器端调用listen()函数监听接入连接后,可以调用accept()函数来等待接收连接请求,函数原型如下:

SOCKET accept(
	SOCKET s,					//通过调用listen()函数设置为监听状态的Socket
	struct sockaddr FAR* addr,	//输出参数,用于接收接入地址信息。可选参数,可以使用NULL
	int FAR* addrlen			//输出参数,指定接入地址的长度。可选参数
);

如果函数调用成功,则返回一个新建的Socket句柄,该Socket用于实现服务器和客户端之间的通信。如果调用失败,则返回INVALID_SOCKET

使用accept()函数在处于监听状态的套接字上接受接入连接,代码如下:

SOCKET sockAccept;			//执行accept()函数后新建的用于实际通信的套接字
struct sockaddr_in from;	//用于接收接入地址
int len = sizeof(from);
sockAccept = accept(s, (struct sockaddr *)&from,&len);
if (sockAccept ==INVALID_SOCKET)
{
	printf("accept error");
}

connect()函数

connect()函数用于建立到Socket的连接,该Socket必须处于监听状态,函数原型如下:

int connect(
	SOCKET s,							//未连接的Socket句柄
	const struct sockaddr FAR* name,	//name指定要建立连接的Socket的名称
	int namelen							//指定Socket名的长度

如果没有发生错误,则connect()函数返回0;否则返回SOCKET_ERROR

recv()函数

调用recv()函数可以从已连接的Socket中接受数据,函数原型如下:

int recv(
	SOCKET s,		//已连接的Socket
	char* buf,		//用于接收数据的缓冲区
	int len,		//buf缓冲区的长度,单位为字节
	int flags		//用于影响函数的行为
);

如果函数调用成功,则返回接收数据的字节数;如果连接已经关闭,则返回0;否则返回SOCKET_ERROR

send()函数

调用recv()函数可以在已连接的Socket上发送数据,函数原型如下:

int send(
	SOCKET s,				//已连接的Socket
	const char FAR* buf,	//包含要发送数据的缓冲区
	int len,				//buf缓冲区的长度,单位为字节
	int flags				//用于影响函数的行为

如果函数调用成功,则返回发送数据的字节数;如果出现错误,则返回SOCKET_ERROR

在客户端使用recv()函数和send()函数发送和接收数据的代码如下:

//初始化WinSock
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) !=0)
{
	printf("初始化失败");
	return 0;
}

//创建连接到服务器的SOCKET对象
SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == INVALID_SOCKET)
{
	printf("socket error!");
	WSACleanup();
	return;
}

//构建地址信息
struct sockaddr_in addr;
//定义服务器地址
addr.sin_family = AF_INET; 
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1234); 

//连接到服务器
if (connect(s,(SOCKADDR*)&addr,sizeof(addr))==SOCKET_ERROR)
{
	printf("fail to connect!");
	WSACleanup();
	return;
}

//声明和初始化变量
int bytesSent;
int bytesRecv = SOCKET_ERROR;
char sendbuff[32] = "Client: Sending data.";
char recvbuff[32] = " ";

//发送数据
bytesSent = send(s,sendbuff,strlen(sendbuff),0);
printf("Bytes Sent: %1d\n", bytesSent);

//接收数据
while(bytesRecv != SOCKET_ERROR)
{
	bytesRecv = recv(s,recvbuff, 32,0);
	if (bytesRecv == 0 || bytesRecv == WSAECONNRESET)
	{
		printf("连接关闭\n");
		break;
	}
	printf("Bytes Recv: %1d\n", bytesRecv);
}

closesocket()函数

closesocket()函数用于关闭一个Socket,释放其占用的所有资源,函数原型如下:

int closesocket(
	SOCKET s
);

如果没有发生错误,则closesocket()函数返回0;否则返回SOCKET_ERROR

shutdown()函数

shutdown()函数用于禁止在指定的Socket上发送和接收数据,函数原型如下:

int shutdown(
	SOCKET s,
	int how		//指定禁用的操作
);

当how被设置为SD_RECEIVE时,不允许在Socket上再次调用recv()函数接收数据;当how被设置为SD_SEND时,不允许在Socket上再次调用send()函数发送数据;当how被设置为SD_BOTH时,在套接字s上禁止发送和接收数据。
如果没有发生错误,则shutdown()函数返回0;否则返回SOCKET_ERROR

猜你喜欢

转载自blog.csdn.net/SebastianStan/article/details/106817901