网络编程笔记

 

二种线程同步的方式---事件对象
件对象也属于内核对象,包含一个使用计数,一个用于指明该事件对象是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。
有两种不同类型的事件对象,一种是人工重置的事件,另一种是自动重置的事件。当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程(所谓可调度线程就是当CPU时间片到来时,线程可以执行)当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
当一个线程等待到一个人工重置的事件对象之后,该事件仍然处于有信号状态,所以别的线程也可以获得该事件的所有全,变为可调度线程。所以,人工重置的事件对象得到通知时,所有等待该事件对象的线程都变为可调度线程。
线程一旦获得了某个事件对象且使其处于了无信号状态,线程内的执行代码就变为受保护代码,在此期间别的线程不可以运行,即使别的线程获得了CPU的时间片。
如果一个事件始终处于无信号状态,则别的线程都无发请求获得该事件,别的线程都无法执行,只有次线程可以执行。
HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,
  BOOL bManualReset,
  BOOL bInitialState,
  LPCTSTR lpName
);创建或着打开一个命名的或者没有命名的事件对象。
BOOL bManualReset:TRUE--人工重置,FALSE--自动重置。
如果选择了TRUE(人工重置),则必须调用ResetEvent(handle)函数来重置这个事件为无信号状态。
如果为FALSE(自动重置事件),则当一个等待线程被释放以后,系统自动设定这个事件为无信号状态。
也就是说,如果我们创建的是人工重置的事件对象,当我们等待到事件对象之后,我们需要手动的调用ResetEvent()函数将这个事件设为无信号状态。
如果我们创建的是自动重置的事件对象,当一个线程等待到这个事件对象之后,系统将自动将这个事件对象设置为无信号状态。
BOOL bInitialState:设置事件创建时的初始状态。TRUE--有信号,FALSE--无信号。
如果在创建一个对象时将bInitialState设置为了无信号状态(FALSE),则别的线程就不能再申请到此事件对象直到该事
件对象被该线程释放。

BOOL SetEvent(
  HANDLE hEvent
);设定指定的对象为有信号状态。

在单CPU的系统中,同一时刻只能有一个线程运行。当一个线程已经进入到被保护的代码运行后,即使再把它的事件对象设置为非信号状态,他也不能再起作用了。人工重置的事件对象除非人工调用SetEvent将其设置为有信号状态,否则它将始终处于非信号状态。

对于线程同步间的同步来讲,我们不要采用人工重置的事件对象。


在线程同步时,一个线程获得了一个自动重置的事件对象后, 系统自动将这个事件对象设置为非信号状态,这个时候其他线程无法获取此事件对象,只能等待,这个线程也不能再一次获取这个事件对象。
所以,当一个线程获取了事件对象执行完受保护的代码后,应该用SetEvent()将这个事件对象设置为有信号状态。
在主线程退出之前要调用CloseHandle()--关闭事件对象。

通过创建一个命名的事件对象,我们也可以完成让一个程序只有一个实例运行,例如金山词霸;用创建命名的互斥对象也可以做到。
第三种线程同步的方式--关键代码段
关键代码段也叫做临界区,他工作在用户方式下。
关键代码段(临界区)是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权。
我们可以把能够访问同一种资源的代码段叫做关键代码段。关键代码段非常类似与公用电话厅,当我们进入公用电话亭之前要判断一下电话亭当中是否有人。如果已经有人在使用这种资源,那么我们就只能在外面等候,当别人用完了电话我们才能用。

void EnterCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);
等待指定的临界区对象的所有权,当调用线程被赋予所有权后,这个函数就返回。否则该函数将一直等待。从而导致我们线程的等待。
LPCRITICAL_SECTION lpCriticalSection:指向关键代码段结构体的一个指针。其功能类似于建立一个公用电话亭。

void InitializeCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);
void LeaveCriticalSection
( LPCRITICAL_SECTION
lpCriticalSection );
释放关键代码段的所有权。这样别的线程就可以获得关键代码段的所有权。

void DeleteCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);当关键的代码段不需要再存在的时候可以调用这个函数,该函数释放一个没有被拥有的临界区对象相关的所有资源。
线程死锁
哲学家进餐的问题:
有一群哲学家在一起共进晚餐, 但是每个哲学家手上都只有跟筷子,这个时候如果有一个哲学家把筷子交出来让其他的哲学家先吃,别人吃完之后,别人再将筷子交回来,那么这些问都可以完成进餐。但是所有的哲学家都这么想,都想让别人把筷子交出来自己先吃。最终,这些哲学家都只能看着满桌子的食物而都没法进餐。
这就是线程死锁的描述。
线程1拥有了临界区对象A,等待临界区对象B的所有权;线程2拥有了临界区对象B,等待临界区对象B的所有权,这样就造成了死锁。
所以在做多线程同步时,要避免发生线程的死锁。
三种线程同步方法--互斥对象,事件对象,关键代码段(临界区)的比较


1--互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内核对象可以在多个进程中的各个线程间进行同步。
2--关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。


我们在进行多线程同步时,首选的是关键代码段。如果是在MFC程序中使用,我们可以在一个类的构造函数中去调用InitializeCriticalSection(),在类的析构函数中去调用DeleteCriticalSection(),在我们所要保护的代码的前面调用EnterCriticalSection(),在访问完我们调用的资源之后调用LeaveCriticalSection()。使用关键代码段时一定要注意,在调用了EnterCriticalSection只有一定要调用LeaveCriticalSection,否则,其他线程都没有办法执行。
如果构造在多个临街区对象,要注意线程的死锁,

基于消息的异步套接字
1---Windows套接字在两种模式下执行I/O操作,阻塞和非阻塞。在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回程序(将控制权交还给程序)。而在非阻塞模式下,Winsock函数无论如何都会立即返回。譬如我们调用一个receive()函数,如果这个时候网路上没有数据到来,receive函数就会阻塞,导致我们的线程暂停运行,而不会影响主程序的运行。
对于非阻塞,Winsock函数无论如何都会立即返回,当函数执行完成之后,如果我们接收的数据操作完成了,系统会采用一种方式通知我们,我们就会知道我们的接收操作是正常完成了还是出错了。(这里还是不太明白)
2---Windows sockets为了支持Windows的消息机制,使应用程序开发者能够方便的处理网络通信,他对网络事件采用了基于消息的异步存取策略。在Windows环境下,因为阻塞套接字在很多应用环境当中,会影响我们程序的性能,所以在有些情况下我们需要采用非阻塞的套接字编程。工作在非阻塞模式下有多种机制,其中一种是异步选择机制。
3--Windows Sockets的异步选择函数WSAAsyncSelect()提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中指示了发生的网路事件,以及与事件相关的一些信息。譬如登记一个网络读取事件,那么一旦有数据到来的时候,就会触发这个事件,操作系统就会通过一个消息来通知我们,我们就可以在消息响应函数中去receive这个数据。采用异步选择机制能够有效的提高我们应用程序的性能。

int WSAAsyncSelect(
  SOCKET s,
  HWND hWnd,
  unsigned int wMsg,
  long lEvent
);
为一个套接字申请基于关于网络事件的通知。该函数自动设定socket为非阻塞模式。也就是说当我们调用WSAAsyncSelect去注册一个网路事件后,该套接字就为非阻塞模式。
s:要申请的套接字,
hWnd:事件发生时通知的窗口的句柄。
wMsg:事件发生时窗口接收到的消息。该消息为自定义消息。
lEvent:是我们感兴趣的网络事件的一个位掩码组合。
WSAAsyncSelect

When one of the nominated network events occurs on the specified socket s, the application's window hWnd receives message wMsg. The wParam parameter identifies the socket on which a network event has occurred. The low word of lParam specifies the network event that has occurred. The high word of lParam contains any error code. The error code be any error as defined in Winsock2.h.

int WSAEnumProtocols(
  LPINT lpiProtocols,
  LPWSAPROTOCOL_INFO lpProtocolBuffer,
  LPDWORD lpdwBufferLength
);//取得可用的传输协议
Win32平台支持多种不同的网络协议,采用Winsock32,就可以编写可以直接使用任何一种协议的网络应用程序了。通过WSAEnumProtocols函数可以获取到系统中安装的网络协议的相关信息。
lpiProtocols:一个以NULL结尾的信息。可选,如果为NULL将返回所有可用协议的信息;否则值返回数组中列的协议的信息。
lpProtocolBuffer:一个以WSAPROTOCOL_INFO结构填充的缓冲区。
lpdwBufferLength:输入时描述了lpProtocolBuffer的长度;输出时能够存放协议信息的最小缓冲区长度。该函数不能重复调用。

SOCKET WSASocket(
  int af,
  int type,
  int protocol,
  LPWSAPROTOCOL_INFO lpProtocolInfo,
  GROUP g,
  DWORD dwFlags
);//创建一个基于某种传输协议的socket。
前三个参数同于socket(  int af,
  int type,
  int protocol)函数的参数,
LPWSAPROTOCOL_INFO lpProtocolInfo:指向一个LPWSAPROTOCOL结构体的指针,该结构指定了所创建的套接字的特性。如果该参数为NULL,winsock2 DLL自己判断决定使用那一个服务体供者,他选择能够支持规定的地址族、套接字类型、和协议值的第一个传输提供者。如果不为NULL,则套接字绑定到指定的结构WSAPROTOCOL_INFO相关的提供者。
g:保留,
dwFlags:套接字属性的描述。

int WSARecvFrom(
  SOCKET s,//套接字描述符
  LPWSABUF lpBuffers,//WSABUF结构的指针,该结构包含windows socket buffer的信息
  DWORD dwBufferCount,//windows socket buffer 的个数
  LPDWORD lpNumberOfBytesRecvd,//实际接收到的字节数
  LPDWORD lpFlags,
  struct sockaddr* lpFrom,
  LPINT lpFromlen,
  LPWSAOVERLAPPED lpOverlapped,
  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);//接收数据报, 存储源地址

typedef struct __WSABUF {  u_long len;  char FAR* buf;
} WSABUF结构。

int WSASendTo(
  SOCKET s,
  LPWSABUF lpBuffers,//WSABUF 结构体指针
  DWORD dwBufferCount,//WSABUF结构体个数
  LPDWORD lpNumberOfBytesSent,//实际发送的字节数
  DWORD dwFlags,
  const struct sockaddr* lpTo,
  int iToLen,
  LPWSAOVERLAPPED lpOverlapped,
  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);


基于消息的异步套接字编程步骤:
1--创建套接字WSASocket()
2--绑定套接字调用bind
3--调用WSAAsyncSelect()为某个套接字请求某种事件的通知
4--等事件到来后在消息相应函数中进行处理,

阻塞模式下套接字相关函数VS基于消息的异步套接字

windows下的套接字函数包含了对原始套接字函数的扩充;
1--创建套接字   socket               WSASocket();
2--关闭套接字   closesocket        closesocket
3--绑定套接字    bind               bind
4--接收数据      recvfrom            WSARecvFrom()
5--发送数据      sendto               WSASendTo()

gethostbyname()将主机名转换为 IP地址
The gethostbyname function retrieves host information corresponding to a host name from a host database.
可以通过主机名获取主机的信息, ip,别名等
内存之间可以互相转换,只要内存的数据模型是兼容的就可以。
譬如dword 类型指针指向的数据占四个字节,ulong 类型数据占四个字节
HOSTENT *pHostent;
pHostent = gethostbyname("papa");
addrTo.sin_addr.S_un.S_addr = *((DWORD*)pHost->h_adrr_list[0]);
其中, pHost->h_adrr_list[0]为 char*的网络字节序,
可以将其转变为DWORD*类型, 因为dword类型的数据与ulong类型的数据内存模型相同,所以可以直接将其值付给S_addr。DOWRD*类型取值正好取出四个字节,与ulong类型相同。


DWORD *dwP;
char *cP;
(DWROD*)cp;
ulong 类型占四个字节。

struct HOSTENT* FAR gethostbyaddr(
  const char* addr,
  int len,
  int type
);
//通过地址获取主机名
pHost = gethostbyaddr((char *)&addrFrom.sin_addr_S_un.S_addr)...)

addrFrom.sin_addr_S_un.S_addr).为ulong类型地址, 取地址后为ulong类型指针, 然后再将其转换为char *类型指针即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 很抱歉,我是AI语言模型,无法提供自己的笔记。但是,我可以为您提供一些关于Linux网络编程的信息。Linux网络编程是指在Linux操作系统上进行网络编程的技术,主要包括socket编程网络协议网络编程模型等内容。在Linux网络编程中,常用的编程语言有C、C++、Python等。Linux网络编程的应用非常广泛,可以用于开发各种网络应用程序,如Web服务器、邮件服务器、FTP服务器等。同时,Linux网络编程也是网络安全领域的重要技术之一,可以用于网络攻防、数据加密等方面。 ### 回答2: Linux网络编程是一门高级语言编程,包括了网络以及套接字的知识,它的目的是让开发者能够在Linux系统下进行网络应用程序的开发与运行。Linux网络编程中,主要涉及到如下的知识: 1.网络协议网络协议是数据在计算机网络内传输时所必须遵循的规则和约定。网络协议的共同目标是确保数据的可靠传输。 2.套接字:套接字是一种在网络编程中广泛使用的编程接口,它允许从一个进程向另一个进程通信。通过套接字的编程,可以实现网络上的客户端和服务器端的通信。 3.套接字选项:在套接字编程中,选项提供了一些可选项来控制套接字的行为。例如,可以使用SO_REUSEADDR选项来允许相同的IP地址和端口被多个套接字所使用,或者使用SO_LINGER选项来控制套接字在关闭时的行为。 4.网络编程的主要函数:对于网络编程而言,大多数使用的函数都是socket编程或一些与之相关的函数。如socket、bind、listen、accept、connect、send、recv等。 5.多线程编程和select函数:在网络编程中,常常需要使用多线程编程来同时处理多个套接字,使程序具有高并发性。而select函数可以在一个线程中监听多个套接字的I/O事件,从而优化服务器的性能和响应速度。 6.网络编程的实际应用:网络编程在实际应用中,可以实现许多有趣的功能。例如,可以创建简单的聊天程序、实现网络文件传输、远程控制、实现P2P通信等。 总之,Linux网络编程是一种非常重要的技术,要学习并掌握这门技术,需要掌握网络协议、套接字、多线程编程等基础知识。掌握这些知识后,开发者可以根据实际需求,灵活地使用网络编程技术来实现各种基于网络的应用程序。 ### 回答3: Linux网络编程在现代软件开发中扮演着非常重要的角色,这是因为它是一种跨平台的操作系统,并且为开发人员提供了良好的网络编程接口。以下是一些重要的技术和笔记: 1. 套接字(socket)编程—— 在Linux环境中,套接字是网络编程的关键要素。它被用于实现客户端/服务器应用程序中的通信,例如Web服务器和聊天室应用程序。在编写套接字程序时,必须使用包括bind,listen和accept等操作来处理连接请求。 2. 网络协议—— Linux支持各种网络协议,例如TCP/IP,UDP,ICMP,ARP和RIP等。其中TCP/IP是最常用的网络协议,因为它可靠且易于使用,在开发网络应用程序时需要具备其相关知识,例如TCP连接管理和协议数据包的格式化。 3. 多线程编程—— 在Linux环境中,多线程编程是一种非常重要的技术,可以同时处理多个网络请求,以提高应用程序的性能。通常使用POSIX线程库(pthread)来实现多线程编程,并使用同步和互斥机制来管理线程访问共享变量的冲突。 4. 网络安全—— 网络安全是Linux网络编程的一个重要方面,因为网络应用程序通常需要保护敏感数据和隐私信息。开发人员必须学习诸如SSL和TLS等加密协议,以确保数据传输的安全性。 总结来说,在Linux环境下进行网络编程需要熟悉套接字编程网络协议、多线程编程网络安全等技术。这些技术的结合可以实现高效的网络应用程序,并提高用户体验。在掌握这些技术后,开发人员可以将网络编程应用于Web服务器、聊天室应用程序、数据存储器等各种应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值