东风草堂博客

公众号:开发者来风

自旋锁

自旋锁是最基本的一种无锁技术,采用基于 CAS(Compare-and-Swap)的操作,尝试获取锁,失败时采用轮询的方式等待资源,避免线程被阻塞,不需要系统调用,从而减少上下文切换和性能损耗。
原子操作使用 CPU 提供的原子性指令,属于硬件层面的,实现数据的原子性访问和修改,避免使用锁导致的线程阻塞和竞争,同时也避免了死锁的发生。
加锁区执行速度快的情况下可以使用该锁。
atomic提供的常见方法:

  • store:原子写操作。
  • load:原子读操作。
  • compare_exchange_strong:传入[期望原值]和[设定值],当前值与期望值相等时,修改当前值为设定值,返回true;当前值与期望值不等时,将期望值修改为当前值,返回false。
  • compare_exchange_weak:同上,但允许偶然出乎意料的返回(比如在字段值和期待值一样的时候却返回了false),比strong性能更好些,但需要循环来保证逻辑的正确性。
  • exchange:交换2个数值,并保证整个过程是原子的。

C++11标准中提供了 std::atomic 模板类,用于实现原子操作,可用于实现 CAS 操作,从而实现无锁编程。
在非并发条件下,实现一个栈的Push操作,通常有如下操作:

  • 步骤1:新建一个节点,node* const new_node = new node(data);
  • 步骤2:将该节点的next指针指向现有栈顶,new_node->next = head;
  • 步骤3:更新栈顶,head = new_node
阅读全文 »

系统梳理软件开发中常用的设计模式,深入解析策略模式、观察者模式、责任链模式等核心模式的实现原理与应用场景,提供完整的C++代码示例,帮助开发者掌握设计模式在实际项目中的灵活运用。

阅读全文 »

目前还有不少朋友对留言功能开通不是很了解,已经很久没有更新文章了,还是决定将一些大家都存在的疑惑问题再集中整理一下,写成一篇文章,希望能对大家有帮助。

网上已经有各种资料已经说的很清楚了,18年3月后就关闭了留言功能,政策原因,理解一下。

下面介绍两种开通留言功能的方法。

公众号迁移

如果你是个人类型订阅号,建议直接看第二种开通留言的小程序留言方法或本方法的最后一点,如果你是企业类型订阅号,有条件的还是建议进行迁移,一步到位,只是迁移的过程麻烦一点。下面分几个方向对迁移进行说明。

阅读全文 »

网上已经有各种资料已经说的很清楚了,18年3月后就关闭了留言功能,政策原因,理解一下。

企业类型公众号的可以考虑通过公众号迁移来开通留言功能,简单来说就是将别人订阅号的留言功能迁移到你的企业类型订阅号上,这样你的号就有留言功能了,迁移的目标账户必须是组织类型的账户如企业类型订阅号。迁移的具体流程为:
公众号迁移流程)

那么到底什么是公众号迁移?

因为公众号不支持直接变更主体,所以公众平台推出帐号迁移功能,通过此功能可将A帐号的粉丝、违规记录、文章素材(可选)、微信号(可选)迁移至B帐号。

温馨提示:例如帐号A迁移至B,那迁移完成,A帐号被回收,B帐号获得A帐号的粉丝等信息继续使用,但主体还是B。

阅读全文 »

在这个函数中,会调用getsockopt函数,获取socket套接字是否有连接的异常,但是这个getsockopt函数有个连续调用的问题,如果你第二次调用这个函数,之前fd上的错误就被清除了,导致本来这个socket套接字是有问题的,但是检测不出来了,目前这个evhttp_connection_cb的bug就在这里,如果后端服务器根本就没有开启的情况下,该函数中的getsockopt也检测不到异常了,导致其误以为连接上了,流程会一直往下走,会继续去读取后端服务器的body,却发现读不到数据,导致连接503错误,原本应该是getsockopt检测到连接异常后,就响应500的错误的。

阅读全文 »

可以参考下典型的http会话,服务器响应由一系列文本指令组成, 并使用 CRLF 分隔,它们被划分为三个不同的块:

  • 第一行是 状态行,包括使用的HTTP协议版本,状态码和一个状态描述(可读描述文本)。
  • 接下来每一行都表示一个HTTP首部,为客户端提供关于所发送数据的一些信息(如类型,数据大小,使用的压缩算法,缓存指示)。与客户端请求的头部块类似,这些HTTP首部组成一个块,并以一个空行结束。
  • 最后一块是数据块,包含了响应的数据(如果有的话)。

具体实现如下代码,收到postman发过来的消息后,将收到的消息显示出来,并回复状态ok的响应,这就完成了一个最简单的http服务器模型。

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>

int main(void)
{
int fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd == -1) {
printf("create socket error %d\n", errno);
exit(1);
}
int on = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
struct sockaddr_in addr;
addr.sin_family = PF_INET;
addr.sin_port = htons(8081);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(fd, reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr)) != 0) {
printf("bind socket error %d\n", errno);
switch (errno)
{
case EADDRINUSE:
std::cout << "Address already in use" << std::endl;
break;

default:
break;
}
close(fd);
exit(1);
}
if (listen(fd, 1024) != 0) {
printf("listen socket error %d\n", errno);
close(fd);
exit(1);
}
while(1) {
struct sockaddr_in cli;
int len = sizeof(cli);
int nfd = accept(fd, reinterpret_cast<struct sockaddr *>(&cli),
reinterpret_cast<socklen_t *>(&len));
if(nfd == -1) {
printf("accept socket error %d\n", errno);
close(fd);
exit(1);
}

char buf[1024];
char content[] = "hello http!";
bzero(buf, 1024);
recv(nfd, buf, 1024, 0);
printf("----------------recv msg----------------\n%s\n", buf);
bzero(buf, 1024);
sprintf(buf, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n"
"Content-Type: text/html\r\n\r\n%s", static_cast<int>(strlen(content)), content);
printf("----------------ready to send buf----------------\n%s\n", buf);
if (send(nfd, buf, strlen(buf), 0) == -1) {
printf("write socket error %d\n", errno);
}
if (close(nfd) != 0)
printf("close nfd err: %d\n", errno);
}

if (close(fd) != 0)
printf("close fd err: %d\n", errno);
exit(0);
}

这里写法可以留意一下,将socket建立,绑定监听放在while循环之上,循环里面只要连续的accept就行了,完了之后把accept的fd给close掉,close就是代表主动关闭连接了。
发送http请求
服务器端接收情况
如果你没有下载postman的话,也可以使用curl命令发送请求。
curl发送请求
服务器端接收情况

阅读全文 »

参考链接:https://www.geeksforgeeks.org/tcp-server-client-implementation-in-c/

建立HTTP服务器的流程都是一样的,可以参考TCP Server的建立过程,因为Http是基于Tcp协议的,底层的原理都是相通的,参考了man手册大概介绍一下。

创建socket

创建socket,此时会返回一个描述符,头文件为#include <sys/socket.h>,函数原型为int socket(int domain, int type, int protocol);,domain参数指明通信将在哪个通信族发生,一般选PF_INET即Iternet版本4协议,socket参数指明通信的类型,可选择SOCK_STREAM/SOCK_DGRAM/SOCK_RAW,SOCK_STREAM类型提供基于序列的,可靠的,双向连接的字节流,可以支持带外数据传输机制,指的是TCP。 SOCK_DGRAM套接字支持数据报(无连接,不可靠的最大固定长度(通常很小)的消息),指的是UDP。 SOCK_RAW套接字提供对内部网络协议和接口的访问,仅超级用户可用。protocal协议指明与套接字使用的特定的协议,一般填0即可。
SOCK_STREAM类型的套接字是全双工字节流,类似于管道。 流套接字必须处于连接状态,然后才能在其上发送或接收任何数据。 通过connect或connectx调用创建与另一个套接字的连接。 连接后,可以使用read和write调用或send和recv调用的某些变型函数传输数据。 当会话已完成时,可以执行close。 带外数据也可以按照send中的说明进行发送,也可以按照recv中的说明进行接收。
如果发生错误,则返回-1,否则返回值是引用套接字的描述符。
所以可以这么写:

1
2
3
4
5
6
7
8
// socket create and verification 
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
printf("socket creation failed...\n");
exit(0);
}
else
printf("Socket successfully created..\n");
阅读全文 »

参考文章:
https://www.geeksforgeeks.org/tcp-3-way-handshake-process/
https://www.inetdaemon.com/tutorials/internet/tcp/3-way_handshake.shtml

传输控制协议中的TCP三向握手(也称为TCP握手;三消息握手和/或SYN-SYN-ACK)是TCP通过基于Internet协议的网络建立TCP / IP连接的方法。 TCP的三向握手技术通常称为“ SYN-SYN-ACK”(或更准确地说是SYN,SYN-ACK,ACK),因为TCP传输了三种消息来协商和启动两台计算机之间的TCP会话。 TCP握手机制经过设计,因此,尝试进行通信的两台计算机可以在传输SSH和HTTP Web浏览器请求之类的数据之前,协商网络TCP套接字连接的参数。

这也可以看作是建立TCP连接的一种方式。 在进入细节之前,让我们看一些基础知识。 TCP代表传输控制协议,该协议表示它做了一些事情以可靠的方式控制数据的传输。

互联网上设备之间的通信过程根据当前的TCP/IP套件模型(OSI参考模型的精简版)进行。 应用程序层是TCP/IP模型的顶层堆栈,在该层中,网络引用的应用程序(如客户端上的Web浏览器)与服务器建立连接。 信息从应用程序层传输到传输层,在这里我们的主题成为现实。 该层的两个重要协议是TCP,UDP(用户数据报协议),而TCP在其中很普遍(因为它为建立的连接提供了可靠性)。 但是,您可以在查询DNS服务器中找到UDP的应用程序,以获取与该网站使用的域名的二进制等效项。

TCP通过称为“带重传的肯定确认”(PAR)的方式提供可靠的通信。 传输层的协议数据单元(PDU)称为段。 现在,使用PAR的设备重新发送数据单元,直到它收到确认为止。 如果在接收器端接收的数据单元已损坏(它使用用于错误检测的传输层的校验和功能检查数据),则接收器将丢弃该段。 因此,发送方必须重新发送未收到肯定确认的数据单元。 通过上述机制,您可以实现在发送方(客户端)和接收方(服务器)之间交换三个段,以建立可靠的TCP连接。 让我们研究一下这种机制是如何工作的:
TCP-connection
3-way-handshake

阅读全文 »
0%