Windows上的Socket编程(阻塞模型)

Socket协议的工作原理

Socket又称套接字,它是TCP/IP网络环境下应用程序与底层通信驱动程序之间运行的开发接口,它可以将应用程序与具体的TCP/IP隔离开,使得应用程序不需要了解TCP/IP的细节,就能够实现传输。

TCP协议使用的send、recv、WSASend和WSARecv,
UDP协议使用的sendto、recvfrom、WSASendTo 和WSARecvFrom。

什么是WSASend?
作用:在一个已连接的套接口上发送数据。

需要包括的库

#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib ")

用到的函数

1. WSAStartup   初始化Ws2_32.dll的函数

int WSAStartup(
   __in          WORD wVersionRequested,
  __out         LPWSADATA lpWSAData
);

wVersionRequested

标识了用户调用的Winsock的版本号。高字节指明辅版本编号,低字节指明主版本编号。通常使用MAKEWORD来生成一个版本号。 当前Winsock sockets的版本号为2.2,用到的dll是 Ws2_32.dll。

lpWSAData

指向WSADATA结构体的指针,lpWSAData返回了系统对Windows Sockets 的描述

Return Value

如果调用成功,WSAStartup 函数返回0。否则,将返回五种错误代码之一

2. WSACleanup   释放Ws2_32.dl的l函数

int WSACleanup(void);

返回值0表示正常退出,返回值SOCKET_ERROR表示异常。返回值是SOCKET_ERROR,可以调用 WSAGetLastError.查看错误代码。需要注意的是,在多线程环境下,WSACleanup 函数将终止所有线程的socket操作。

3. socket   创建socket的函数

SOCKET WSAAPI socket(
   __in          int af,
   __in          int type,
   __in          int protocol
 );

af   ( address family)

  指明地址簇类型,常用的地址簇如下,其余地址簇在Winsock2.h中定义。

AF_UNSPEC(未指明)、

AF_INET(IPv4)、

AF_NETBIOS(NETBIOS地址簇)、

AF_INET6(IPv6)、

AF_IRDA(Infrared Data Association (IrDA)地址簇)、

AF_BTM(Bluetooth)。

type

  指明socket的类型,Windows Sockets 2常见类型如下:

SOCK_STREAM(流套接字,使用TCP协议)、

SOCK_DGRAM(数据报套接字,使用UDP协议)、

SOCK_RAW(原始套接字)、

SOCK_RDM(提供可靠的消息数据报文,reliable message datagram)、

SOCK_SEQPACKET(Provides a pseudo-stream packet based on datagrams,在UDP的基础上提供了伪流数据包)。

protocol

指明数据传输协议,该参数取决于af和type参数的类型。protocol参数在Winsock2.h and Wsrm.h定义。通常使用如下3中协议:

IPPROTO_TCP(TCP协议,使用条件,af是AF_INET or AF_INET6、type是SOCK_STREAM )

IPPROTO_UDP(UDP协议,使用条件,af是AF_INET or AF_INET6、type是SOCK_DGRAM)

IPPROTO_RM(PGM(Pragmatic General Multicast,实际通用组播协议)协议,使用条件,af是AF_INET 、type是SOCK_RDM)

4. bind    服务端将socket与地址关联

5. listen    服务端网络监听

6. accept   服务端connect接收

7. connect   客户端请求服务端连接

8. send   发送数据

TCP连接流程

TCP服务端代码

// WinServer.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

int main()
{
	WORD wVersion = MAKEWORD(2, 2);
	WSADATA wsaData = { 0 };
	//WinSocket初始化
	if (WSAStartup(wVersion, &wsaData))   
	{
		std::cout << "WSAStartup Error" << std::endl;
		return 1;
	}

	//创建socket
	SOCKET nServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (nServerSocket == INVALID_SOCKET)
	{
		std::cout << "create socket Error" << std::endl;
		WSACleanup();//释放WinSocket
		return 1;
	}

	sockaddr_in serverAddr = { 0 };
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(8888);    //绑定端口
	serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);	//绑定ip

	//绑定ip和端口
	if (SOCKET_ERROR == bind(nServerSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)))
	{
		std::cout << "bind Error" << std::endl;
		closesocket(nServerSocket);
		WSACleanup();//释放WinSocket
		return 1;
	}

	//监听  最大连接数为5
	if (SOCKET_ERROR == listen(nServerSocket, 5))
	{
		std::cout << "listen Error" << std::endl;
		closesocket(nServerSocket);
		WSACleanup();//释放WinSocket
		return 1;
	}

	SOCKET nClientSocket = { 0 };
	sockaddr_in clientAddr = { 0 };
	int nLen = sizeof(clientAddr);
	//接受客户端连接请求
	nClientSocket = accept(nServerSocket, (sockaddr*)&clientAddr, &nLen);
	if (INVALID_SOCKET == nClientSocket)
	{
		std::cout << "accept Error" << std::endl;
		closesocket(nServerSocket);
		WSACleanup();//释放WinSocket
	}

	char szBuffer[255] = { 0 };
	int nRet = 0;
	//接收客户端数据
	while (true)
	{
		memset(szBuffer, 0, 255);
		//接受客户端发来的数据
		nRet = recv(nClientSocket, szBuffer, 255, 0);
		if (nRet == SOCKET_ERROR)
		{
			std::cout << "recv Error" << std::endl;
			closesocket(nClientSocket);
			closesocket(nServerSocket);
			WSACleanup();//释放WinSocket
			return 1;
		}
		std::cout << szBuffer << std::endl;

		nRet = send(nClientSocket, szBuffer, 255, 0);
		if (nRet == SOCKET_ERROR)
		{
			std::cout << "send Error" << std::endl;
			closesocket(nClientSocket);
			closesocket(nServerSocket);
			WSACleanup();//释放WinSocket
			return 1;
		}
	}

	closesocket(nClientSocket);
	closesocket(nServerSocket);
	WSACleanup();//释放WinSocket
	return 0;
}

TCP客户端代码

// WinClient.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <WS2tcpip.h>
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

int main()
{
	WSADATA		wsaData = { 0 };
	SOCKET		nClientSocket = { 0 };
	int			nRet = 0;
	char		szBuffer[255];

	//WinSocket初始化
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		std::cout << "WSAStartup failed!\n";
		return 1;
	}

	//创建socket
	nClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (nClientSocket == INVALID_SOCKET)
	{
		std::cout << "socket failed!\n";
		//释放
		WSACleanup();
		return 1;
	}

	SOCKADDR_IN addrServ = { 0 };
	addrServ.sin_family = AF_INET;
	addrServ.sin_port = htons(8888);   //绑定端口
	inet_pton(AF_INET, "127.0.0.1", &addrServ.sin_addr);  //服务器ip

	//连接服务器
	nRet = connect(nClientSocket, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
	if (nRet == SOCKET_ERROR)
	{
		std::cout << "connect failed!\n";
		closesocket(nClientSocket);
		WSACleanup();
		return 1;
	}

	while (true)
	{
		memset(szBuffer, 0, 255);
		std::cout << "Please input a string:";
		std::cin >> szBuffer;
		//发送数据
		nRet = send(nClientSocket, szBuffer, strlen(szBuffer), 0);
		if (nRet == SOCKET_ERROR)
		{
			std::cout << "send failed!\n";
			closesocket(nClientSocket);
			WSACleanup();
			return -1;
		}
		memset(szBuffer, 0, sizeof(szBuffer));
		//接收数据
		nRet = recv(nClientSocket, szBuffer, sizeof(szBuffer), 0);
		if (nRet == SOCKET_ERROR)
		{
			std::cout << "recv failed!\n";
			closesocket(nClientSocket);
			WSACleanup();
			return 1;
		}
		std::cout << szBuffer << std::endl;
	}

	closesocket(nClientSocket);
	WSACleanup();

	return 0;
}

UDP

sendto

int sendto(

  __in  SOCKET s, //指定套接字(可能已连接)

  __in  const char *buf, //指向将要发送数据的缓冲区

  __in  int len, //缓冲区大小

  __in  int flags, //指定数据传输方式

  __in  const struct sockaddr *to, //可选的指针,指向存储目标套接字地址信息的sockaddr结构

  __in  int tolen //to参数指向结构的字节大小

);

返回值:

成功时,返回发送的字节数;

失败时,返回SOCKET_ERROR,调用WSAGetLastError函数查看进一步错误信息。

 recvfrom

recvfrom函数用于接收数据报并存储发送方源地址信息:

int recvfrom(

  __in         SOCKET s, //指定一个绑定的socket

  __out        char *buf, //存放接到的数据的缓冲区

  __in         int len, //缓冲区字节大小

  __in         int flags, //指定传输控制方式

  __out        struct sockaddr *from, //可选的指针,函数成功返回后,

//指向存储源套接字地址信息的sockaddr结构

  __inout_opt  int *fromlen //from的字节大小

);

返回值:

成功时,返回接收的字节数;

失败时,返回SOCKET_ERROR,调用WSAGetLastError函数查看进一步错误信息。

UDP服务端

// WinServer.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

int main()
{
	WORD wVersion = MAKEWORD(2, 2);
	WSADATA wsaData = { 0 };
	//WinSocket初始化
	if (WSAStartup(wVersion, &wsaData))   
	{
		std::cout << "WSAStartup Error" << std::endl;
		return 1;
	}

	//创建socket
	SOCKET nServerSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (nServerSocket == INVALID_SOCKET)
	{
		std::cout << "create socket Error" << std::endl;
		WSACleanup();//释放WinSocket
		return 1;
	}

	sockaddr_in serverAddr = { 0 };
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(8888);    //绑定端口
	serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);	//绑定ip

	//绑定ip和端口
	if (SOCKET_ERROR == bind(nServerSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)))
	{
		std::cout << "bind Error" << std::endl;
		closesocket(nServerSocket);
		WSACleanup();//释放WinSocket
		return 1;
	}

	sockaddr clientAddr = { 0 };
	int nLen = sizeof(clientAddr);
	char szBuffer[255] = { 0 };
	int nRet = recvfrom(nServerSocket, szBuffer, 255, 0, &clientAddr, &nLen);
	if (nRet)
	{
		std::cout << szBuffer << std::endl;
	}

	closesocket(nServerSocket);
	WSACleanup();//释放WinSocket

	getchar();
	return 0;
}

UDP客户端

// WinClient.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <WS2tcpip.h>
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

int main()
{
	WSADATA		wsaData = { 0 };
	SOCKET		nClientSocket = { 0 };

	//WinSocket初始化
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		std::cout << "WSAStartup failed!\n";
		return 1;
	}

	//创建socket
	nClientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (nClientSocket == INVALID_SOCKET)
	{
		std::cout << "socket failed!\n";
		//释放
		WSACleanup();
		return 1;
	}

	sockaddr_in addrServ = { 0 };
	addrServ.sin_family = AF_INET;
	addrServ.sin_port = htons(8888);   //服务器端口
	inet_pton(AF_INET, "127.0.0.1", &addrServ.sin_addr);  //服务器ip

	char szBuffer[255] = "123456";
	int nRet = sendto(nClientSocket, szBuffer, 255, 0, (sockaddr*)&addrServ, sizeof(addrServ));
	if (nRet)
	{
		std::cout << "send success" << std::endl;
	}

	closesocket(nClientSocket);
	WSACleanup();

	getchar();
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值