一、TCP 特性

尽管 TCP 和 UDP 都是用 IP 协议作为网络层,但 TCP 却提供和 UDP 完全不同的网络服务。TCP 是面向连接的稳定可靠字节流服务。TCP 首部的很多字段都是为了实现这 2 大特性而设计的。

阅读全文 »

消息队列

首先我们要明确一个观点:窗口是和线程相关联的,消息队列也是和线程相关联的,这个线程无论是主线程还是子线程

当一个线程被创建时,系统假定该线程不会被用于任何与用户界面相关的任务,所以不会为它分配相应的资源(如消息队列等),因为这样可以减少线程对系统资源的占用。

但是,一旦这个线程调用一个与图形用户界面有关的函数(例如检查它的消息队列或建立一个窗口),系统就会为该线程分配一些额外的资源,以便它能够执行与用户界面有关的任务。特别是,系统会分配一个THREADINFO结构,并将这个数据结构与线程关联起来。

THREADINFO结构是微软内部的、没有被公开的数据结构,我们无法找到这个结构体的准确的定义。但从其他文档中可以得知,THREADINFO结构包含:

  • 一组成员变量,利用这组成员,线程可以认为它是在自己独占的环境中运行。
  • 登记消息队列(posted-message queue)
  • 发送消息队列( send-message queue)
  • 应答消息队列( reply -message queue)
  • 虚拟输入队列(virtualized-input queue)
  • 唤醒标志(wake flag)
  • 用来描述线程局部输入状态的若干变量。

上图描述了THREADINFO结构中的各个成员。线程拥有了THREADINFO结构也就有了各种消息队列。

窗口消息处理函数

下面是一个完整的、简单的创建 Windows 窗体的 C++代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}

return DefWindowProc(hwnd, message, wParam, lParam);
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpfnWndProc = WndProc; //设置窗体消息处理函数
wndclass.lpszClassName = TEXT("SimpleWindow");
if (!RegisterClass(&wndclass)) { //注册窗体类
return 0;
};

HWND hwnd = CreateWindow(TEXT("SimpleWindow"), // window class name
TEXT("SimpleWindow"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT,// initial x position
CW_USEDEFAULT,// initial y position
CW_USEDEFAULT,// initial x size
CW_USEDEFAULT,// initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL);

ShowWindow(hwnd, SW_SHOW);

MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return msg.wParam;
}

上面代码中的WndProc函数就是窗口消息处理函数。消息循环中的DispatchMessage函数派发消息时,系统就会调用这个函数对消息进行处理。

消息循环

消息循环是程序员自己编写的从线程消息队列中循环获取(Get 或 Peek)消息的循环体,代码大致如下:

1
2
3
4
5
while(GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

循环何时结束?

GetMessage函数的返回值如下:

1
2
3
收到WM_QUIT消息,返回0
收到非WM_QUIT消息,返回非0
错误,返回-1

利用GetMessage函数返回值的特性,在收到WM_QUIT消息之后,消息循环就会结束。

TranslateMessage

TranslateMessage函数的作用就是将虚拟键值信息转换为字符信息。这一步并不是必须的。

DispatchMessage

将消息派发到窗口的消息处理函数(如第 2 节中的WndProc函数)。

PostMessage

PostMessage 原理

1
2
3
4
5
BOOL PostMessage(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);

当一个线程调用这个函数时,系统要确定是哪一个线程建立了用hwnd参数标识的窗口。然后系统分配一块内存,将这个消息参数存储在这块内存中,并将这块内存增加到相应线程的登记消息队列中。并且,这个函数还设置QS_POSTMESSAGE唤醒位。PostMessage函数在登记了消息之后立即返回,调用该函数的线程不知道登记的消息是否被指定窗口的窗口过程所处理。实际上,有可能这个指定的窗口永远不会收到登记的消息。

PostThreadMessage

还可以通过调用PostThreadMessage将消息放置在线程的登记消息队列中。

1
2
3
4
5
BOOL PostThreadMessage(
DWORD dwThreadId,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);

PostQuitMessage

为了终止线程的消息循环,可以调用PostQuitMessage函数。PostQuitMessage函数类似于:
PostThreadMessage(GetCurrentThreadId(), WM_QUIT, nExitCode, 0);
但是,PostQuitMessage并不实际登记一个消息到任何一个THREADINFO结构的消息队列。只是在内部,PostQuitMessage会设定QS_QUIT唤醒标志,并对THREADINFO结构的nExitCode成员进行设置。因为这些操作永远不会失败,所以PostQuitMessage的原型被定义成VOID返回类型。

SendMessage

SendMessage的实现分为 2 种情况:向本线程的窗口发送消息、向其他线程的窗口发送消息。

向本线程的窗口发送消息

如果调用SendMessage的线程向本线程所建立的一个窗口发送一个消息,SendMessage的做法很简单:直接调用指定窗口的“窗口消息处理函数”,将其作为一个子例程。当“窗口消息处理函数”完成对消息的处理后,该函数会返回一个值给SendMessageSendMessage再将这个值返回给调用线程。

向其他线程的窗口发送消息

但是,当调用SendMessage的线程向其他线程所建立的窗口发送消息时,SendMessage的内部工作就复杂得多(即使两个线程在同一进程中也是如此)。

Windows 要求建立窗口的线程来处理该窗口的消息。所以当一个线程调用SendMessage向一个由其他进程所建立的窗口发送一个消息,也就是向其他的线程发送消息,发送线程不可能处理窗口消息,因为发送线程不是运行在接收进程的地址空间中,因此不能访问相应窗口过程的代码和数据。实际上,发送线程要挂起,而由另外的线程处理消息。所以为了向其他线程建立的窗口发送一个窗口消息,系统必须执行下面的动作:

首先,发送的消息要追加到接收线程的发送消息队列,同时还为这个线程设定QS_SENDMESSAGE标志(后面将讨论)。
其次,如果接收线程已经在执行代码并且没有等待消息(等待消息是指:如调用GetMessagePeekMessageWaitMessage等),发送的消息不会被处理,系统不能中断线程来立即处理消息。当接收进程在等待消息时,系统首先检查线程的QS_SENDMESSAGE唤醒标志是否被设定,如果是,系统扫描发送消息队列中消息的列表,并找到第一个发送的消息。有可能在这个队列中有几个发送的消息。例如,几个线程可以同时向一个窗口分别发送消息。当发生这样的事时,系统只是将这些消息追加到接收线程的发送消息队列中。

当接收线程等待消息时,系统从发送消息队列中取出第一个消息并调用适当的窗口过程来处理消息。如果在发送消息队列中再没有消息了,则QS_SENDMESSAGE唤醒标志被关闭。当接收线程处理消息的时候,调用SendMessage的线程被设置成空闲状态(idle),等待一个消息出现在它的应答消息队列中。在发送的消息处理之后,窗口过程的返回值被登记到发送线程的应答消息队列中。发送线程现在被唤醒,取出包含在应答消息队列中的返回值。这个返回值就是SendMessage的返回值。这时,发送线程继续正常执行。

当一个线程等待SendMessage返回时,它基本上是处于空闲状态。但它可以执行一个任务:如果系统中另外一个线程向一个窗口发送消息,这个窗口是由这个等待SendMessage返回的线程所建立的,则系统要立即处理发送的消息。在这种情况下,系统不必等待线程去调用GetMessagePeekMessageWaitMessage

由于 Windows 使用上述方法处理线程之间发送的消息,所以有可能造成线程挂起。例如,当处理发送消息的线程含有错误时,会导致进入死循环。那么对于调用SendMessage的线程会发生什么事呢?它会恢复执行吗?这是否意味着一个程序中的 bug 会导致另一个程序挂起?答案是确实有这种可能。

利用 4 个函数—— SendMessageTimeOutSendMessageCallbackSendNotifyMessageReplayMessage,可以编写保护性代码防止出现这种情况。

一、什么是 UDP 协议?

UDP 是 User Datagram Protocol 的简称,中文名是用户数据报协议,是 OSI 参考模型中的传输层协议,它是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。

阅读全文 »

互联网控制消息协议(英文:Internet Control Message Protocol,ICMP)是互联网协议族的核心协议之一。定义在 RFC 792 文档中。
ICMP 的消息大致可以分为两类:一类是差错报文,即通知出错原因的错误消息(如 traceroute),另一类是查询报文,即用于诊断的查询消息(如 ping)。
使用 ICMP 协议的典型应用有 ping 和 traceroute(windows 上叫 tracert)。

阅读全文 »

IP 协议是 TCP/IP 协议族中最核心的协议。所有的 TCP、UDP、ICMP、IGMP 数据都以 IP 数据报的格式传输。

阅读全文 »

ARP (Address Resolution Protocol) 地址解析协议,位于 TCP/IP 协议栈中的链路层。

当局域网内主机间(或者是主机与网关间)需要通信时,通过使用 ARP 协议获取目标 IP 地址所对应的硬件 MAC 地址,然后在主机间通过 MAC 地址来完成数据包的发送和接收。

阅读全文 »

功能

DECLARE...HANDLER语法如下:

1
2
3
DECLARE handler_action HANDLER
FOR condition_value [, condition_value] ...
statement

功能概括起来就是:
在一个或者多个condition_value条件满足时,先执行statement语句,然后执行handler_action动作

阅读全文 »

  1. 成功的唯一秘诀就是钻空子。钻职场人际关系的空子、钻市场监管的空子、钻信息不对称的空子,钻你发现的一切空子的空子。当然,坚持、努力、选择这些因素,对于成功也很重要,但算不上秘诀。只有钻空子才是唯一的秘诀。

  2. 不要做永远正确的穷人。少跟人争辩,你觉得对就去做,做成了自然别人就闭嘴了。说服别人并没有实际的意义,浪费口舌和时间。当然,利益相关的事情,有人冤枉你的情况除外。

  3. 脸皮变厚一倍,成功的可能性要翻倍。不要怕丢脸,你觉得丢脸的事情,大家其实压根不在乎,丢脸多半是你个人的臆想。

  4. 项目不做调研,就像嫖娼不戴套,非常危险。任何项目启动前一定要做适当的调研,哪怕很粗糙,也远胜于无。

  5. 创业做生意就是要抄,抄产品、抄模式、抄架构,这是规避风险,减少试错成本的最好方法。但是也不能全抄。大原则是:拿得准的环节创新,拿不准的地方抄。

  6. 每个月花半天时间,扫描和梳理自己所在领域和区域的行业新机会。

  7. 有枣没枣,打一竿子。多去做一些尝试。不一定每一件事都要努力去做,但那些舒适区外的事情,可以试着去做。

  8. 大家都去找金矿的时候,给淘金的人卖铲子就是最好的生意,多考虑快速增长行业的周边生意。

  9. 卖手机不如修手机,修手机不如做二手回收。一个行业越是信息不对称,越是不标准化,它的利润就越高,赚钱的可能性就越大。

  10. 要投资一个领域,下面两个条件至少要一个条件成立:1. 你自己懂行;2. 你能找到懂行又靠谱的人来帮你把关。

  11. 不要用个人的好恶代替商业逻辑。如果你觉得罗辑思维、咪蒙、比特币都是骗子,那你永远看不到背后的需求、机会和行业红利。

  12. 做营销就和做小姐一样,越low的方式反倒越有效。与其扭扭捏捏、故作逼格,不如大大方方简单粗暴、赤裸相待。

  13. 遇到自己不懂或者没听过的新名词,用手机记下来。有空可以研究一下,说不定里面有新的机会和思路。

  14. 对于赚钱的事,多琢磨,多尝试。当你第一次尝到甜头,你就走在一条快车道上了。

  15. 让你觉得听了不舒服的话,往往是真话。别人对你的有价值的批评,往往是你成长和进步的机会。

  16. 减少无效思考。80%的人都是无能的,即使是有能力的人当中,80%的思考也是无效的。尽可能减少无效的思考时间,多把精力和时间投入到行动中。

  17. 别被那些一夜暴富的事情忽悠了,相信时间,相信积累,顺着大趋势慢慢做,总会起来。

  18. 选择比努力更重要。思考方式的转变比能力提升更重要,这方面的学习和研究很有价值。

  19. 如果你无法释怀过去的痛苦,不要责怪你自己,尽可能安排一下未来生活的兴奋点。

  20. 忧虑的时候尽可能保持忙碌,把身体的能量用完了,就没时间、精力忧虑了。

  21. 每周至少约一个对自己事业、生活有帮助的朋友吃饭。

  22. 人与人之间很难真正理解,你也不需要这么多人理解你,也不要为了让别人理解你做太多尝试。专注于提升自己的实力,多做一些让别人敬佩你的事情。

  23. 相比于认清世界,更难的是认清自己。对自己有一个清晰的认识后,需要做取舍做选择,才会有判断依据。

  24. 靠谱不仅是一种人格,也是一种营销策略。因为这个世界大部分人都不靠谱,你要是相对靠谱,好的资源、机会都会首先想到你、找到你。能在混沌一片的世界里快速把自己的品牌营销出去。

  25. 思想深度和快乐是成反比的,除了赚钱相关的事情,其他事情没必要深究,徒增烦恼。不要觉得减少就变成一个快乐的笨蛋了,你思考太多、忧虑太多,你只是变成一个忧心忡忡的笨蛋罢了。

  26. 学习最重要的,不是把10分的精力都花在吸收新知识上,而是3分学习,7分练习和实践。

  27. 简单的事情反复做,就能成长;复杂的事情简单做,就能成功。同一个技能反复练习100次,那个技能就是你的;同一个领域的事情做对100次,那个领域的钱就是你的。

  28. 迷茫的时候,就把能做好的事情踏实做好就行。多关注自己力所能及的小事,小成功积累多了,就有大成功的机会。。

  29. 人生常常就像堵车,心里别添堵。很多时候堵在里面,你也不要心急。能走几步走几步,走不动的时候歇着,等车流慢慢恢复了,又继续走。

  30. 有责任感有担当的人,往往都活得比较累,因为要操心的事儿太多。你如果是这样的人,接受这样的事实,尽可能坦然面对事业、生活、家庭中的苦与乐。

  31. 做人不要太容易多愁善感,不然容易成为loser。

  32. 生活的常态就是平淡,但是我们在规划未来的时候,尽可能还是为生活安排一些兴奋点。

  33. 每个人的生活都需要有一个支点,不管是让你充满成就感的事业、还是你疼爱的爱人,还是你的温暖家庭,还是你的知心挚友、老师、贵人,或者是一个爱好。至少要有一个这样的支点,能够支撑起你的生命。即使在你迷茫、无助、悲伤的时候,这个支点能支撑你走过最艰难的时刻。能让你的精神随时有地方可以倚靠。

转载自:

http://mp.weixin.qq.com/s/PjlLbikJHSM4PKwpmTkL9g

0%