windows套接字(socket)编程(非MFC) 带你了解那些网络传输的细节内容

273 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

windows套接字(socket)编程是最简单的网络编程,首先先给大家看一个我用已经做好的套接字的小的程序,现在还不算很完善,炮弹打到对面的坦克并不会消失,不过本章博客重点介绍socket,这里就不要太纠结于这个内容了,本来打算做来和室友一起玩的,如果有兴趣的小伙伴可以继续完善。 还是,先看看效果吧,来放图:

首先我们来介绍本程序最基础的内容,socket通信。 //**************************************************************** //最最重要的东西,头文件和动态链接库的加载 //****************************************************************

#include <WinSock.h>
#include <errno.h>

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

一:socket的tcp通信 (a). 服务器和客户端的共有内容 1.要使用socket,首先需要初始化socket的内容,非常简单,代码内容如下:

// ====================初始化TCP协议=====================
	WSADATA wsaData;
	BOOL ret = WSAStartup(MAKEWORD(2,2),&wsaData);
	if(ret != 0)
	{
		MsgBox(TEXT("初始化套接字失败!")); //此处本函数对API进行了封装,具体如果没有我的框架可以用MessageBox函数来显示
		return;
	}

此处的WSAStartup函数原型如下(MSDN里面有): int WSAStartup ( WORD wVersionRequested,
LPWSADATA lpWSAData
); 函数说明: wVersionRequested 是一个16位的参数,用来指定动态链接库将要支持的版本内容,低八位指定主版本号,高八位用来指定低版本号,MAKEWORD(2,2)这里这么用到了MAKEWORD函数,这个函数可以将两个占有八个位的数字合并为一个新的数值,我这个看起来就很简单了,加载的就是2.2版本的了。 lpWSAData 指向一个WSADATA结构,用来返回动态链接库的详细信息,这个内容我们可以不管他。 (注意:!!在程序的结束的时候一定记得使用WSACleanup();函数来释放库,做扫尾工作。!!) 2.socket的创建 创建就比较简单了,这里我们要做的有两件事,其一,创建一个socket;其二,给这个socket绑定一个用于异步通信的windows用来通知的消息 (!!这个socket的返回值一定要好好的保存起来,后面的关闭包括发送或者接收全靠它了!!) NETWORK_EVENT(姓名随意,不过一定注意要在文件里面宏定义这个内容才能用 #define NETWORK_EVENT WM_USER+100 //定义网络事件 那个+100数值随意,不过可以适当的大一点,不会和其他的事件冲突)

m_sSocketLocal = socket(AF_INET/*Internet 协议*/, SOCK_STREAM/*套接字类型*/, 0/*与特定的地址族相关的协议,为0自动选择*/);
	if(m_sSocketLocal == INVALID_SOCKET)
	{
		MsgBox(StrAppend(TEXT ("创建套接字失败!!\r\n"),Str(WSAGetLastError ())));
		return 0;
	}
	//注册网络异步事件,m hWnd为应用程序的主对话框或主窗口的句柄
	//默认所有的网络事件都注册,m_sSocketLocal 是消息接收到的 socket 的句柄
	if(WSAAsyncSelect(m_sSocketLocal, hWnd, NETWORK_EVENT, FD_ACCEPT | FD_CLOSE | FD_READ | FD_WRITE)!=0)
	{
		MsgBox(StrAppend(TEXT ("消息注册失败!!\r\n"),Str(WSAGetLastError())));
		closesocket (m_sSocketLocal);
		m_sSocketLocal = INVALID_SOCKET;
		return 0;
	}

函数简要讲解: SOCKET socket ( int af,
int type,
int protocol
); //大多的内容注释有解释,此处第二个参数用的是SOCK_STREAM,使用的是tcp传输,如果使用udp传输的,参数应该是SOCK_DGRAM,此处我也没用,就不做过多的说明了。 //对了,这里的这个函数WSAGetLastError ()是能够返回出错代码,我用了Str函数是为了把返回的int数据转换为字符串来显示提示的。 int WSAAsyncSelect ( SOCKET s,
HWND hWnd,
unsigned int wMsg,
long lEvent
); 这个函数绝对是个大宝贝好吧,我当初为了用图形界面找了好长时间才知道要用这个函数来设置socket为非阻塞模式,消息驱动的方式,赞! //hWnd 参数表示如果有这个socket的消息应该发送到哪里的窗口来接受这个消息。 //wMsg 这个参数表示的是要向窗口发送的消息的内容,这里能了解了解,不能了解知道这么用就行啦,哈哈哈哈 //lEvent 表示的是在发生什么消息才向窗口发送此消息,可以是多个参数的组合,我为了方便就全部都设置了,具体消息代表什么后面具体介绍,不用慌 //这里如果注册失败就直接关闭了socket,这个函数后面介绍 3.socket的关闭 先不讲别的,此处讲完创建,先讲一下关闭吧,关闭调用的函数是closesocket; 函数介绍: int closesocket ( SOCKET s
); 这个函数看起来很简单。其实确实比较简单,但是对于服务器和客户端不太一样,对于客户端,参数直接为创建时候的那个socket;但是对于服务器的socket,分为两种情况,关闭服务器的socket和关闭客户端的连接但是不关闭服务器的socket。(具体的内容在accept函数介绍里面将详细介绍) (b). 服务器和客户端不同的内容 客户端和服务器主要的不同是一个是主动的,一个是被动的。(个人感觉,可能不准确),客户端是主动的,他可以连接不同的客户端,比较自由,但是对于服务器来说就属于那种绑定到一个地方,就开始等待连接(有种守株待兔的感觉,不过客户端不会在这里撞死)有socket要连接就连接一下的那种 1.客户端 客户端连接服务器的connect函数。 int connect ( SOCKET s,
const struct sockaddr FAR* name,
int namelen
); 先看看代码吧!

sockaddr_in m_sSocketAddr;
//设置新的端口号和 ip 地址
m_sSocketAddr.sin_port = htons(iPort);		//端口号
//此处设置连接的服务器的 ip 地址
//注意此处的 c_ip 需要是char*类型的数据,如果采用的是TCHAR*类型的内容,此处需要进行转换,将TCHAR*转换为char*类型的数据
m_sSocketAddr.sin_addr.s_addr=inet_addr(c_ip);
connect(m_sSocketLocal,(struct sockaddr *)&m_sSocketAddr, sizeof(struct sockaddr));
//判断连接状态,是否发送连接请求成功
//特别注意,此处由于是非阻塞模式,所以并不是连接成功了,只是发送连接请求成功,具体连接成功的话客户端会收到一个FD_WRITE消息,那时候才是真正的连接成功了
if (WSAEWOULDBLOCK != (iError = WSAGetLastError ()))
	{
		//如果失败了,自动关闭这个,然后重新创建一个新的,返回新的连接情况
		closesocket (m_sSocketLocal);
		return false;
	}
	else
		m_sckState = esckConnectionPending;	//连接挂起状态

函数介绍: s代表客户端要连接的客户端创建的socket句柄; name用来设置要连接的ip地址和对应的端口,具体操作见上面的代码,比较简单 2.服务器 服务器绑定ip地址的bind函数 int bind ( SOCKET s,
const struct sockaddr FAR* name,
int namelen
);

//绑定到本地一个端口上
	sockaddr_in localaddr;
	localaddr.sin_family = AF_INET;		//默认这么写
	localaddr.sin_port = htons(iPort);	//端口号
	localaddr.sin_addr.S_un.S_addr =INADDR_ANY;		//这个表示是检测本机的所有ip
	//如果计算机有多块网卡,不希望在全部的网卡上进行监听,那么就将该字段填写成内网的ip就行
	//套接字会在指定的ip地址监听
	if(bind(m_sSocketLocal,(const struct sockaddr*)&localaddr,sizeof(sockaddr))== SOCKET_ERROR)
	{
		closesocket (m_sSocketLocal);
		return false;
	}

绑定完毕,对于tcp套接字还需要调用listen