C++ 服务器编程开发

Day 01

效率

开发效率
C++11 -> auto、lambda、move
同步、异步、多线程、多进程…
运行效率

预备知识

  1. TCP/IP (网络编程都是此种协议)
    • TCP -> 传输控制协议
    • IP -> 网际协议
  2. Socket
  3. C++
    • 指针、引用、地址、循环、函数、类…
    • std::shared_ptr、std::thread、std::aync、decltype、std::future、right、reference
  4. 第三方库
    • BOOST

问题

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
// 反转链表
#include<algorithm>

using namespace std;
struct Node{
int* next;
int value;
}

void pirntNodeList(Node* n){
while(n)
{
cout << n->value << " ";
n = n->next;
}
cout << "\n";
}

Node* reverseList(Node* node)
{

return 0;
}

void quizOne()
{
Node* n = 0;
for(int i = 0; i<3; i++)
{
n->next = Node(i);
}
}

IP网际协议详解

IP = Internet protocol suite

OSI模型(Open System Interconnection model)


注意: 七个层级,过于复杂。

IP模型

数据进入协议栈的封装


注意: Frame data 长度有要求,最小46字节,不足添0;最大1500个字节。

MTU(最大传输单元),当传输数据 > MTU时,则在IP层需要拆分成小包。

IP特点

  • 不可靠(unreliable)
  • 无连接(connectionless)

IP数据报格式首部字段


注意:【小小】 网络数据都是按照Big Endian【大端存储】传送的;家用操作系统都是Littele Endian数据存储。
Big_Small_Endian.jpg
思考题

1
2
3
static bool isLittleEndianSystem(){

}

TCP详解

TCP如何利用IP

  • IP的特点
  • TCP将应用程序的传输数据分割成合适的数据块
  • 定时器
  • 延迟确认
  • 检验和CRC
  • 流量控制

TCP首部


注意: IP首部20Byte,TCP首部20Byte,如需在网络传输,还需填充6Byte。

所谓的Socket , 即ip+port

ip 来自 IP首部
port 来自 TCP首部

  • Sequence Number 用于标记定时器对那一数据包计时。
  • Acknowlegment Number延迟确认有关 -> if ACK set
  • Checksum检验和 有关
  • Data offset – 数据长度
  • URG -> 数据紧急标志位
  • ACK -> 数据确认标识位
  • PSH -> push
  • RST -> reset
  • SYN -> 建立时需要
  • FIN -> 结束

TCP状态

TPC状态变迁

蓝线–server;棕线–client;虚线–异常

TCP的连接(三次握手)

TCP连接的断开(四次握手)

1.为什么断开要四次握手?

全双工,两边都需要确认
主动断开方,会进入TIME_WAIT状态

TCP数据相互传送

  • 交互式(小数据)与成块的数据两种
  • 时间延迟确认
  • Nagle算法[小数据积攒后再发送](游戏开发一般关闭这个算法)
  • 接收窗口大小

TCP内部使用的定时器

  • 重传定时器
  • 坚持定时器(Persist)
  • 保活定时器(KeepAlive)<建议不开启>
  • 2MSL定时器(TIME_WAIT)

实战

Wireshark使用

需安装WinCap方能使用

筛选器

ip.addr==115.239.210.27 && ip.port==80

Socket API (Berkeley sockets, BSD Socket)

头文件

API函数


服务器和客户端的例子

TCP Socket 基本流程图

Code:

Server_main.cpp

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
73
74
75
76
77
78
79
80
81
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main(int argc, char** argv)
{
char hello[] = "hello world";
struct sockaddr_in sa;
int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

if(-1 == SocketFD)
{
perror("cannot create socket");
exit(EXIT_FAILURE);
}

memset(&sa, 0, sizeof sa);

sa.sin_family = AF_INET;
sa.sin_port = htons(2222);
sa.sin_addr.s_addr = htonl(INADDR_ANY);

if(-1 == bind(SocketFD, (struct sockaddr*)&sa, sizeof sa))
{
perror("bind failed");
close(SocketFD);
exit(EXIT_FAILURE);
}

if(-1 == listen(SocketFD, 10))
{
perror("listen failed");
close(SocketFD);
exit(EXIT_FAILURE);
}

for(;;)
{
int ConnectFD = accept(SocketFD, NULL, NULL);

if(0 > ConnectFD)
{
perror("accept failed");
close(SocketFD);
exit(EXIT_FAILURE);
}

int writeSize = 0;
size_t totalWrite = 0;
while(totalWrite < sizeof(hello))
{
writeSize =
write(ConnectFD, hello + totalWrite, sizeof(hello) - totalWrite);
if(-1 == writeSize)
{
perror("write failed");
close(ConnectFD);
close(SocketFD);
exit(EXIT_FAILURE);
}
totalWrite += writeSize;
}

if(-1 == shutdown(ConnectFD, SHUT_RDWR))
{
perror("shutdown failed");
close(ConnectFD);
close(SocketFD);
exit(EXIT_FAILURE);
}
close(ConnectFD);
}
close(SocketFD);
return EXIT_SUCCESS;
}

Client_main.cpp

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
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main(int argc, char** argv)
{
struct sockaddr_in sa;
int res;
int SocketFD = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

if(-1 == SocketFD)
{
perror("cannot create socket");
exit(EXIT_FAILURE);
}

memset(&sa, 0, sizeof sa);

sa.sin_family = AF_INET;
sa.sin_port = htons(2222);
res = inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr);

if(-1 == connect(SocketFD, (struct sockaddr*)&sa, sizeof sa))
{
perror("connect failed");
close(SocketFD);
exit(EXIT_FAILURE);
}

char buffer[512];
int totalRead = 0;
for(;;)
{
int readSize = 0;
readSize = read(SocketFD, buffer + totalRead, sizeof(buffer)-totalRead);
if(readSize == 0)
{
break;
}
else if(readSize == -1)
{
perror("read failed");
close(SocketFD);
exit(EXIT_FAILURE);
}
totalRead += readSize;
}
buffer[totalRead] = 0;
printf("get from server: %s\n",buffer);

(void)shutdown(SocketFD, SHUT_RDWR);

close(SocketFD);
return EXIT_SUCCESS;
}
根本不存在什么打赏一说!