1.TCP三次握手四次挥手的原因
为什么要进行三次握手呢(两次确认)?
建立三次握手主要是因为A发送了再一次的确认,那么A为什么会再确认一次呢,主要是为了防止已失效的连接请求报文段又突然传送给B,从而产生了错误。
所谓“已失效的连接请求报文”是这样产生的,正常情况下,A发出连接请求,但是因为连接报文请求丢失而未收到确认,于是A再重传一次连接请求,后来收到了请求,并收到了确认,建立了连接,数据传输完毕后,就释放链接,A共发送了两次连接请求报文段,其中第一个丢失,第二个到达了B,没有“已失效的连接请求报文段”,但是还有异常情况下,A发送的请求报文连接段并没有丢失,而是在某个网络节点滞留较长时间,以致延误到请求释放后的某个时间到达B,本来是一个早已失效的报文段,但是B收到了此失效连接请求报文段后,就误以为A又重新发送的连接请求报文段,并发送确认报文段给A,同意建立连接,如果没有三次握手,那么B发送确认后,连接就建立了,而此时A没有发送建立连接的请求报文段,于是不理会B的确认,也不会给B发送数据,而B却一直等待A发送数据,因此B的许多资源就浪费了,采用三次握手的方式就可以防止这种事情发生,例如刚刚,A不理会B,就不会给B发送确认,B收不到A的确认,就知道A不要求建立连接,就不会白白浪费资源。
为什么要进行四次挥手呢?
由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
在建立连接时,当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是在断开连接时,当Server端收到FIN报文时,很可能并不会立即想关闭连接,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
四次挥手的过程如下所述:
-
数据传输结束后,客户端的应用进程发出连接释放报文段,并停止发送数据,客户端进入FIN_WAIT_1状态,此时客户端依然可以接收服务器发送来的数据。
-
服务器接收到FIN后,发送一个ACK给客户端,确认序号为收到的序号+1,服务器进入CLOSE_WAIT状态。客户端收到后进入FIN_WAIT_2状态。
-
当服务器没有数据要发送时,服务器发送一个FIN报文,此时服务器进入LAST_ACK状态,等待客户端的确认。
-
客户端收到服务器的FIN报文后,给服务器发送一个ACK报文,确认序列号为收到的序号+1。此时客户端进入TIME_WAIT状态,等待2MSL(MSL:报文段最大生存时间),然后关闭连接。
2.父类和子类构造函数析构函数调用顺序
定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;
析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数。
3.cookie和session区别
Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
简单的说,当你登录一个网站的时候,如果web服务器端使用的是session,那么所有的数据都保存在服务器上面,
客户端每次请求服务器的时候会发送 当前会话的session_id,服务器根据当前session_id判断相应的用户数据标志,以确定用户是否登录,或具有某种权限。
由于数据是存储在服务器 上面,所以你不能伪造,但是如果你能够获取某个登录用户的session_id,用特殊的浏览器伪造该用户的请求也是能够成功的。
session_id是服务器和客户端链接时候随机分配的,一般来说是不会有重复,但如果有大量的并发请求,也不是没有重复的可能性。
Session是由应用服务器维持的一个服务器端的存储空间,用户在连接服务器时,会由服务器生成一个唯一的SessionID,用该SessionID 为标识符来存取服务器端的Session存储空间。而SessionID这一数据则是保存到客户端,用Cookie保存的,用户提交页面时,会将这一 SessionID提交到服务器端,来存取Session数据。这一过程,是不用开发人员干预的。所以一旦客户端禁用Cookie,那么Session也会失效。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
3、设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。
4、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用cookie。
5、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型)
注意:
session很容易失效,用户体验很差;
虽然cookie不安全,但是可以加密 ;
cookie也分为永久和暂时存在的;
浏览器 有禁止cookie功能 ,但一般用户都不会设置;
一定要设置失效时间,要不然浏览器关闭就消失了;
例如:
记住密码功能就是使用永久cookie写在客户端电脑,下次登录时,自动将cookie信息附加发送给服务端。
application是全局性信息,是所有用户共享的信息,如可以记录有多少用户现在登录过本网站,并把该信息展示个所有用户。
两者最大的区别在于生存周期,一个是IE启动到IE关闭.(浏览器页面一关 ,session就消失了),一个是预先设置的生存周期,或永久的保存于本地的文件。(cookie)
Session信息是存放在server端,但session id是存放在client cookie的,当然php的session存放方法是多样化的,这样就算禁用cookie一样可以跟踪
Cookie是完全保持在客户端的如:IE firefox 当客户端禁止cookie时将不能再使用
4.介绍下flask
Flask是一个使用 Python 编写的轻量级 Web 应用框架。
Flask的socket是基于Werkzeug 实现的,模板语言依赖jinja2模板
5.介绍下gin
Gin 是一个 Go 写的 web 框架,具有高性能的优点。
Gin 是 Go语言写的一个 web 框架,它具有运行速度快,分组的路由器,良好的崩溃捕获和错误处理,非常好的支持中间件和 json。
6.Redis能存储的数据类型
- 字符串string
- 列表list
- 散列hash
- 集合set
- 有序集合sorted set
7.Redis常用命令
Redis 常用命令
登录 redis-cli -p 5566 -a password
检查key是否存在 EXISTS key
搜索某关键字 KSYS *4
返回一个Key所影响的vsl的类型 TYPE key
1 String
设置一个键的值 SET key value
获取一个建的值 GET key
删除键对 DEL key
同时获取多个 mget key1 key2
2 Hash
设置一个hash HMSET key valueKey value --<key,<valueKey,value>>
获取hash所有key&value HGETALL key
获取hash所有key HKEYS key
获取hash所有key的vslue HVALS key
获取hash内键值对的长度 HLEN key
给一个hash的某个键值对赋值 HSET key valueKey value
当hash中valueKey不存在时赋值 HSETNX key valueKey value
3 List
给list赋值 LPUSH listName value
按照索引取值 LINDEX listName 1
8.c++模板编程
template <模板形参表>
函数返回类型 函数(形参表){
函数体;
};
例如:
template <typename T>
inline const T& Max(const T& a, const T& b){
return a>b?a:b;
}
Max(2,10);
9.中序遍历递归,非递归
递归:
void dfs(TreeNode* root, vector<int> &data){
if(!root)
return;
dfs(root->left,data);
data.emplace_back(root->val);
dfs(root->right,data);
}
非递归:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode head) {
List<Integer> list=new ArrayList<Integer>();
Stack<TreeNode> stack=new Stack<TreeNode>();
if (head!=null) {
while(head!=null||!stack.empty()) {
if(head!=null) {
stack.push(head);
head=head.left;
}else {
head=stack.pop();
list.add(head.val);
head=head.right;
}
}
}
return list;
}
}
10.两数之和
vector<int> twoSum(vector<int>& numbers, int target) {
unordered_map<int,int> mp;
for(int i=0;i<numbers.size();i++){
if(mp.count(target-numbers[i])){
return {mp[target-numbers[i]],i+1};
}else{
mp[numbers[i]]=i+1;
}
}
return {};
}
11.实现strcat,strcpy
实现strcat:把src所指字符串添加到dest结尾处(覆盖dest结尾处的’\0’)
char* myStrcat(char* pre, const char* next)
{
if (pre == nullptr || next == nullptr) // 如果有一个为空指针,直接返回pre
return pre;
char* tmp_ptr = pre + strlen(pre); //strlen计算字符数,需要包含都文件string.h,当然也可以自己实现
while ( (*tmp_ptr++ = *next++) != '\0'); // 依次接着赋值
return pre;
}
实现strcpy:复制字符串
char* myStrcpy(char* pre, const char* next)
{
if (pre == nullptr || next == nullptr) //空指针直接返回
{
return nullptr;
}
if (pre == next) // 两者相等也无需拷贝了
return pre;
while ((*pre++ = *next++) != '\0'); // 依次赋值给主字符数组
return pre;
}
12.手写非重复最长公共子序列
class Solution {
public:
int lengthOfLongestSubstring(string s) {
map<char, int> mp;
int res=0,max_res=0;
for(int i=0;i<s.length();i++){
int last_pos=mp.count(s[i])?mp[s[i]]:-1;
mp[s[i]]=i;
if(res>=i-last_pos)
res=i-last_pos;
else
res+=1;
max_res=max(max_res,res);
}
return max_res;
}
};
13.C++什么时候只能用指针?什么时候只能用引用?
何时使用引用参数
- 程序员能够修改调用函数中的数据对象。
- 通过传递引用而不是整个数据对象,可以提高程序的运行速度。
14.const成员函数的作用?
程序通常不直接修改类对象。在必须修改类的对象时,应调用公有成员函数集来完成。为尊重类对象的常量性,编译器必须区分不安全与安全的成员函数(即区分试图修改类对象与不试图修改类对象的函数)。
类的设计者通过把成员函数声明为const,以表明它们不修改类对象。例如:
class Screen {
public:
char get() const {return _screen[_cursor];}
//...
};
只有被声明为const的成员函数才能被一个const类对象调用。关键字const被放在成员函数的参数表和函数体之间。对于在类体之外定义的const成员函数,我们必须在它的定义和声明中同时指定关键字const。
注意,把一个修改类数据成员的函数声明为const是非法的。
15.为什么要提出右值引用?
右值引用允许从程序中其他地方无法引用的临时对象转移资源
16.对象指针如何找到对应的虚函数表?
类:有虚函数,这个类会产生一个虚函数表。
类对象:有一个指针,指针(vptr)会指向这个虚函数表的开始地址。类对象的虚函数表指针位置取决于编译器。