线程同步与异步套接字编程

事件对象

事件对象也属于内核对象,它包含以下三个成员:

  1. 使用计数。
  2. 用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值。 (为了实现线程间的同步,不应该使用人工重置事件对象,而应该使用自动重置对象。)
  3. 指明该事件出于已通知状态还是未通知状态的布尔值。 事件对象有两种不同的类型:人工重置的事件对象和自动重置的事件对象。当人工重置的事件对象得到通知时,在等待该事件对象的所有线程均可变为可调度线程。当一个自动重置的事件对象得到通知时,在等待该事件对象的线程中只有一个线程变为可调度线程。

创建事件对象CreateEvent函数

作用:
创建或打开一个命名的或匿名的事件对象。

函数原型:

HANDLE CreateEvent(LSPECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);

参数:
1.lpEventAttributes
指向SECURITY_ATTRIBUTES结构体的指针,如果为NULL,则使用默认的安全性。
2.bManualReset
为TRUE,表示函数创建一个人工重置事件对象;为FALSE,表示函数创建一个自动重置事件对象。如果是人工重置事件对象,当线程等待到该对象的所有权之后,则需要调用ResetEvent函数手动地将该事件对象设置为无信号状态;如果是自动重置对象,当线程等到该对象的所有权之后,则系统会自动将该对象设置为无信号状态。
3.bInitialState
指定事件对象的初始化状态。如果此参数值为真,那么该事件对象初始化是有信号状态的;否则是无信号状态。
4.lpName
指定事件对象的名称。如果为NULL,那么创建一个匿名的事件对象。

设置事件对象状态SetEvent函数

作用:
将把指定的事件对象设置为有信号状态。

函数原型:

BOOL SetEvent(HANDLE hEvent);

参数:
指定将要设置状态的事件对象的句柄。

重置事件对象状态ResetEvent函数

作用:
将把指定的事件对象设置为无信号状态。

函数原型:

BOOL ResetEvent(HANDLE hEvent);

参数:
指定将要设置状态的事件对象的句柄。

返回值:
成功返回非0值,失败返回0。

保证应用程序只有一个实例在运行

#include <Windows.h>
#include <iostream>

using namespace std;

HANDLE hEvent;

int main()
{
	hEvent = CreateEvent(NULL, FALSE, FALSE, "tickets");
	if (hEvent)
	{
		if (ERROR_ALREADY_EXISTS == GetLastError())
		{
			cout << "only one instance can run !" << endl;
			return -1;
		}
	}
	cout << "running" << endl;
	system("pause");
	return 0;
}

何为关键代码

关键代码也称临界区,工作在用户方式下。它是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权。

InitializeCriticalSection函数

作用:

在进入关键代码段之前,首先需要初始化一个关键代码段。

函数原型:

void InitializeCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

参数:
一个指向LPCRITICAL_SECTION结构体的指针,该参数是out类型,即作为返回值使用。声明LPCRITICAL_SECTION 结构体的一个对象,不需要对它的成员进行赋值。

EnterCriticalSection函数

作用:
以获得指定临界区对象的所有权。该函数等待指定临界区的所有权,如果该所有权赋予了调用线程,则该函数返回;否则该函数一直等待,从而导致线程等待。

函数原型:

void EnterCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

参数:
一个指向LPCRITICAL_SECTION结构体的指针,该参数是out类型,即作为返回值使用。声明LPCRITICAL_SECTION 结构体的一个对象,不需要对它的成员进行赋值。

LeaveCriticalSection函数

作用:
在线程使用完保护资源后,释放指定的临界区对象的所有权。

函数原型:

void LeaveCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

参数:
一个指向LPCRITICAL_SECTION结构体的指针,该参数是out类型,即作为返回值使用。声明LPCRITICAL_SECTION 结构体的一个对象,不需要对它的成员进行赋值。

DeleteCriticalSection函数

作用:
该函数将释放一个没有被任何线程所拥有的临界区对象的所有资源。

函数原型:

void DeleteCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

参数:
一个指向LPCRITICAL_SECTION结构体的指针,该参数是out类型,即作为返回值使用。声明LPCRITICAL_SECTION 结构体的一个对象,不需要对它的成员进行赋值。

异步套接字(非阻塞)----------------------------------

WSAAsyncSelect函数

函数原型:

int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);

作用:
为指定的套接字申请基于Windows消息的网络事件通知,并且自动将该套接字设置为非阻塞模式。

参数:

  1. s标识请求网络事件通知的套接字描述符。
  2. hWnd 标识一个网络事件发生时接收消息的窗口的句柄。
  3. wMsg 指定网络事件发生时窗口将接收到的消息。
  4. 指定应用程序感兴趣的网络事件,并且可以使用位操作来构造多个事件。

FD_READ 应用程序想要接收有关是否可读的通知,以便数据的读取。
FD_WRITE 应用程序想要接收有关是否可写的通知,以便发送数据。
FD_OOB 应用程序想要接收是否带外(OOB)数据抵达的通知。
FD_ACCEPT 应用程序想要接收与进入连接有关的通知。
FD_CONNECT 应用程序想要接收连接操作已完成的通知。
FD_CLOSE 应用程序想要接收与套接字关闭有关的通知。
FD_QOS 应用程序想要接收套接字“服务质量”发生更改的通知。
FD_GROUP_QOS 应用程序想要接收套接字组“服务质量”发生更改的通知。
FD_ROUTING_INTERFACE_CHANGE 应用程序想要接收指定方向上,与路由接口发生变化有关的通知。
FD_ADDRESS_LIST_CHANGE 应用程序想要接收针对套接字的协议家族,本地地址列表发生变化的通知。

WSAEnumProtocols函数

函数原型:

Int WSAEnumProtocols(LPINT lpiProtocols, LPWSAAPROTOCOL_INFO lpProtocolBuffer, ILPDWORD lpdwBufferLength);

作用:
通过该函数获得系统中安装的网络协议的相关信息。

参数:

  1. lpiProtocols一个以NULL结尾的协议标识号数组。这个参数是可选的,如果lpiProtocols为NULL,则 WSAEnumProtocols函数将返回所有可用协议的信息,否则,只返回数组列出的协议信息。
  2. LpProtocolsBuffer,out类型的参数,作为返回值使用,一个用WSAPROTOCOL_INFO结构体填充的缓冲区。WSAPROTOCOL_INFO结构体用来存放或得到一个指定协议的完整信息。
  3. lpdwBufferLength,in/out类型的参数,在输入时,指定传递给WSAEnumProtocols函数的lpProtocolBuffer缓冲区的长度;在输出时,可以传递给WSAEnumProtocols以检索所有请求信息的最小缓冲区大小。

WSAStartup函数

功能:
1.加载套接字库。
2.进行套接字库的版本协议,也就是确认使用socket版本。
Int WSAAPI WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

参数:
1.wVersionRequested

用来指定准备加载的Winsock库的版本。高位字节指定所需的Winsock库的副版本,而低位字节则是主版本。如,版本号2.1,其中2是主版本号,1就是副版本号。Windows Socket规范的当前版本是2.2,当前的WinSock库支持的版本包括:1.0、1.1、2.0、2.1和2.2。可以利用MAKEWORD(x,y)宏(其中x是高位字节,y是低位字节)方便地获取wVersionRequested的正确值。
2.lpWSAData
这是一个返回值,指向WSADATA结构体指针,WSAStartup函数用其加载的库版本有关的信息填在这个结构体中。

typedef struct WSAData {
        WORD                    wVersion;//设置为打算使用的Winsock版本
        WORD                    wHighVersion;//现有的Winsock库的最高版本
#ifdef _WIN64
        unsigned short          iMaxSockets;
        unsigned short          iMaxUdpDg;
        char FAR *              lpVendorInfo;
        char                    szDescription[WSADESCRIPTION_LEN+1];
        char                    szSystemStatus[WSASYS_STATUS_LEN+1];
#else
        char                    szDescription[WSADESCRIPTION_LEN+1];
        char                    szSystemStatus[WSASYS_STATUS_LEN+1];
        unsigned short          iMaxSockets;
        unsigned short          iMaxUdpDg;
        char FAR *              lpVendorInfo;
#endif
} WSADATA, FAR * LPWSADATA;

返回值:

如果Ws2_32.dll或底层网络子系统没有被正确地初始化或没有被找到,WSAStarup函数返回WSASYSNOTREADY。如果请求的版本低于WinSock动态库所支持的最低版本,则WSAStarup函数将返回WSAVERNOTSUPPORTED。
对于每一个WSACleanUp函数调用成功后,在最后都应该对应一个WSACleanUp调用,以便释放为该应用程序分配的资源。

例子:

 WSADATA wsa;  //存放 windows socket初始化信息的结构体
    if (WSAStartup(MAKEWORD(2,2), &wsa)) {
         printf("init winsock failed !"); 
         return 0;
		
    }

WSACleanup函数

作用:
释放加载的套接字库。

函数的原型:

int WSAAPI WSACleanup(void);

WSASocket函数

作用:
Winsock库中的扩展函数WSASocket将创建套接字。

函数原型:

SOCKET WSASocket(int af,  int type,  int protocol,  LPWSAPROTOCOL_INFO lpProtocolInfo,  GROUP g,  DWORD dwFlags);

参数:

  1. af 指定地址族,对于TCP/IP协议的套接字,它只能是AF_INET(也可以写成PF_INET,IPv4)或AF_INET6(IPv6);
  2. type指定socket类型,对于1.1版本的socket,它只支持两种类型的套接字,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据包套接字。
  3. protocol与特定的地址族相关的协议,如果指定为0,那么系统会根据地址格式和套接字类别,自动选择一个适合的协议。
  4. lpProtocoInfo 一个纸箱WSAPROTOCOL_INFO结构体的指针,该结构体定义了所创建的套接字的特性。如果lpProtocolInfo为NULL,则WinSock2DLL使用前三个参数来决定使用哪一个服务提供者,它选择能够支持规定的地址族、套接字类型和协议值的第一个传输提供者。如果lpProtocolInfo不为NULL,则套接字绑定到与指定的结构WSAPROTOCOL_INFO相关的提供者。
  5. g保留
  6. dwFlags,指定套接字属性的描述。如果该参数的取值为WSA_FLAG_OVERLAPPED,那么将创建一个重叠套接字,这种类型的套接字后续的重叠操作与文件的重叠操作是类似的。

WSARecvFrom函数

函数原型:

int WSARecvFrom(	SOCKET s,  
LPWSABUF lpBuffers,  
DWORD dwBufferCount,  
LPDWORD lpNumberOfBytesRecvd, 
LPDWORD lpFlags,
Sockaddr *lpFrom,
LPINT lpFromlen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );

作用:
接收数据报类型的数据,并保存数据发送方地址。

参数:

  1. 1.s表示套接字描述符。
  2. 2.lpBuffer,in/out类型的参数,一个指向WSABUF结构体数组的指针,定义如下:
typedef struct _WSABUF {
    ULONG len;     /* the length of the buffer */ //缓冲区长度
    _Field_size_bytes_(len) CHAR FAR *buf; /* the pointer to the buffer */ //指向缓冲区的指针
} WSABUF, FAR * LPWSABUF;
  1. dwBufferCount,lpBuffers数组中WSABUF结构体的数目。
  2. lpNumberOfBytesRecvd, out类型的参数,如果接收操作立即完成,那么该参数是一个指向本次调用所接收的字节数的指针。
  3. lpFlags, in/out类型的参数,一个指向标志位的指针,这些标志位影响函数的行为,可以利用位操作组合起来使用。
    MSG_PEEK 浏览到来的数据。这些数据被复制到缓冲区,但并不从输入队列中移除。此标志仅对非重叠套接字有效。
    MSG_OOB 处理带外(OOB)数据
    MSG_PARTIAL 此标志仅用于面向消息的套接字。作为输出参数时,此标志表明数据是发送方传送的消息的一部分。消息的剩余部分将在随后的接收操作中被传送。如果随后的某个接收操作没有此标志,就表明这是发送方发送的消息的尾部。作为输入参数时,此标志表明接收操作应该是完成的,即使只是一条消息的部分数据,也已被服务提供者所接收。
  4. lpFrom, out类型的参数,是一个可选的指针,指向重叠操作完成后存放源地址的缓冲区。
  5. lpFromlen, in/out类型的参数,一个指向lpFrom指定的缓冲区大小的指针,仅当指定了lpFrom参数时才需要使用这个参数。
  6. lpOverlapped一个指向WSAOVERLAPPED结构体指针(对于非重叠套接字则忽略)。
  7. lpCompletionRoutine 一个指向接收操作完成时调用的完成例程的指针(对于非重叠套接字则忽略)。 注意:如果创建的是重叠套接字,那么在使用WSARecvFrom函数时,一定要注意最后两个参数的值。因为这时采用重叠IO操作,WSARecvFrom函数立即返回。当接收到数据这个操作完成后,操作系统会调用lpCompletionRoutine参数指定的例程来通知调用线程,这个例程实际上就是一个回调函数,函数原型为:
void CALLBACK CompletionROUTINE(
IN DWORD dwError,
IN DWORD cbTransferred,
IN LPWSAOVERLAPPED lpOverlapped,
IN DWORD dwFlags);

WSASendTo函数

函数原型:

int WSASendTo(
SOCKET s, 
LPWSABUF lpBuffers, 
DWORD dwBufferCount, 
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
const struct sockaddr FAR *lpTo,
int iToLen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPlETION_ROUTINE lpCompletionRoutine);

参数:

  1. s标识一个套接字(可能已连接)的描述符 。
  2. lpBuffers 一个指向WSABUF结构体的指针。每一个WSABUF结构体包含一个缓冲区的指针和缓冲区长度。
  3. dwBufferCount lpBuffers数组中WSABUF结构体的数目。
  4. lpNumberOfBytesSent out类型的参数,如果发送操作位立即完成,则为一个指向本次调用所发送的字节数的指针。
  5. dwFlags 指示影响操作行为的标志位。一般设置为0。
  6. lpTo 可选指针,指向目标套接字的地址。
  7. iToLen lpTo中地址的长度。
  8. lpOverlapped 一个指向WSAOVERLAPPED结构的指针(对于非重叠套接字则忽略)。
  9. lpCompletionRoutine 一个指向接收操作完成时调用的完成例程的指针(对于非重叠套接字则忽略)。

gethostbyname函数

作用:
利用主机名实现网络访问。gethostbyname函数从主机的数据库中获取主机名相对于的IP地址。

函数原型:

struct hostent FAR* gethostbyname(const char FAR* name);

参数:
指向空终止的字符串。

返回值:
是一个指向hostent结构体类型的指针。该结构体类型的定义如下:

struct hostent{
char FAR*  		h_name;
char FAR* FAR* 	h_aliases;
short			h_addrtype;
short			h_length;
char FAR* FAR* h_addr_list;  
};

h_addr_list是指针数组,每一个元素存放一个网络字节序表示的主机IP地址。其中h_addr_list[0]被定义为h_addr宏,这主要是为了兼容以前的软件。因为一台主机可能会有多个IP地址,利用主机名查询该主机的IP时,可能可以返回多个IP地址,所以需要一个指针数组来存放这些地址。

gethostname():

作用:
返回本地主机的标准主机名

原型如下:

int gethostname(char *name, size_t len);

参数:
接收缓冲区name,其长度必须为len字节或是更长,存获得的主机名。
接收缓冲区name的最大长度。

返回值:
如果函数成功,则返回0。如果发生错误则返回-1.错误号存放在外部变量errno中。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

困了就喝白茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值