c++ 重点基础知识

  1. 指针和引用的区别

    	**指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用仅是个别名**;
    
    	引用使用时无需解引用(*),指针需要解引用;
    
    	**引用只能在定义时被初始化一次,之后不可变;指针可变;**
    
    	引用没有 const,指针有 const;
    
    	**引用不能为空,指针可以为空;**
    
    	“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
    
    	指针和引用的自增(++)运算意义不一样;
    
    	 **指针可以有多级,但是引用只能是一级**(int **p;合法 而 int &&a是不合法的)
    
    	**从内存分配上看:****程序为指针变量分配内存区域,而引用不需要分配内存区域
    

2 const 有什么用途

主要有三点:
  **1:定义只读变量,即常量** 
  2:修饰函数的参数和函数的返回值 
  3: 修饰函数的定义体,这里的函数为类的成员函数,被const修饰的成员函数代表不修改成员变量的值
  1. C++中有了malloc / free , 为什么还需要 new / delete

    1,malloc与free是C++/C语言的**标准库函数**,new/delete是C++的**运算符。**它们都可用于申请动态内存和释放内存。
    2,对于**非内部数据类型**的对象而言,光用maloc/free无法满足动态对象的要求。
    对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
    由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
    3,因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
    
  2. 堆和栈的区别

    一个由c/C++编译的程序占用的内存分为以下几个部分 
    1、栈区(stack)―   **由编译器自动分配释放 ,存放函数的参数值,局部变量的值等**。其操作方式类似于数据结构中的栈。 
    2、堆区(heap) ―   一般由程序员分配释放, **若程序员不释放,程序结束时可能由OS回收 。
    注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,**呵呵。 
    3、全局区(静态区)(static)―,全局变量和静态变量的存储是放在一块的,
    初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后**有系统释放** 
    4、文字常量区  ―常量字符串就是放在这里的。 程序结束后由系统释放 
    5、程序代码区―存放函数体的二进制代码。
    

5 关键字static的作用

1.  函数体内 static 变量的作用范围为该函数体,不同于 auto 变量, **该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值**

2.  在模块内的 static 全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问

3.  在模块内的static 函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内

4.  在类的static 成员变量**属于整个类所拥有,对类的所有对象只有一份拷贝**

5.  在类中的 static 成员函数属**于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量**

6 在c++程序中调用被C编译器编译后的函数,为什么要加extern“C”

extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。
     这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了更好的支持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。
C++语言支持函数重载,C语言不支持函数重载,函数被C++编译器编译后在库中的名字与C语言的不同,
假设某个函数原型为:
void foo(int x, inty);

该函数被C编译器编译后在库中的名字为:  _foo

而C++编译器则会产生像: _foo_int_int   之类的名字。

为了解决此类名字匹配的问题,C++提供了C链接交换指定符号 extern "C"。

7 头文件种的ifndef/define/endif 是干什么用的

  防止头文件被重复包含

8 线程和进程的联系和区别

线程,有时称为轻量级进程,是CPU使用的基本单元;
它由线程ID、程序计数器、寄存器集合和堆栈组成。
它与属于同一进程的其他线程共享其代码段、数据段和其他操作系统资源(如打开文件和信号)
线程有四种状态:新生状态、可运行状态、被阻塞状态、死亡状态。
进程是系统所有资源分配时候的一个基本单位,拥有一个完整的虚拟空间地址,并不依赖线程而独立存在。
![在这里插入图片描述](C:%5CUsers%5CAdministrator%5CDesktop%5C22.png)

~~~~~~~~~~~~~~~~~~~~多线程同步和互斥有几种实现方法,都是什么?

线程间的同步方法大体可分为两类:用户模式和内核模式。
内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,
而用户模式就是不需要切换到内核态,只在用户态完成操作。
用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。
内核模式下的方法有:事件,信号量,互斥量。

~~~~~~~~~~~~~~~~~~~~多线程同步和互斥有何异同,在什么情况下分别使用他们?

线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。

9 进程间的通信方式

 管道、有名管道、信号、共享内存、消息队列、信号量、套接字、文件.

10线程同步的方式

 Linux:   互斥锁、条件变量和信号量

  一、互斥锁(mutex)
通过锁机制实现线程间的同步。

初始化锁。在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化。
静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
加锁。对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
销毁锁。锁在是使用完成后,需要进行销毁以释放资源。
int pthread_mutex_destroy(pthread_mutex *mutex)

#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>
#include "iostream"
using namespace std;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int tmp;
void* thread(void *arg)
{
	cout << "thread id is " << pthread_self() << endl;
	pthread_mutex_lock(&mutex);
	tmp = 12;
	cout << "Now a is " << tmp << endl;
	pthread_mutex_unlock(&mutex);
	return NULL;
}
int main()
{
	pthread_t id;
	cout << "main thread id is " << pthread_self() << endl;
	tmp = 3;
	cout << "In main func tmp = " << tmp << endl;
	if (!pthread_create(&id, NULL, thread, NULL))
	{
		cout << "Create thread success!" << endl;
	}
	else
	{
		cout << "Create thread failed!" << endl;
	}
	pthread_join(id, NULL);
	pthread_mutex_destroy(&mutex);
	return 0;
}
//编译:g++ -o thread testthread.cpp -lpthread

二、条件变量(cond)

互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量分为两部分: 条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

初始化条件变量。
静态态初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER;
动态初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
等待条件成立。释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
激活条件变量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
清除条件变量。无线程等待,否则返回EBUSY
int pthread_cond_destroy(pthread_cond_t *cond);

#include <stdio.h>
#include <pthread.h>
#include "stdlib.h"
#include "unistd.h"
pthread_mutex_t mutex;
pthread_cond_t cond;
void hander(void *arg)
{
	free(arg);
	(void)pthread_mutex_unlock(&mutex);
}
void *thread1(void *arg)
{
	pthread_cleanup_push(hander, &mutex);
	while(1)
	{
		printf("thread1 is running\n");
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond, &mutex);
		printf("thread1 applied the condition\n");
		pthread_mutex_unlock(&mutex);
		sleep(4);
	}
	pthread_cleanup_pop(0);
}
void *thread2(void *arg)
{
	while(1)
	{
		printf("thread2 is running\n");
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond, &mutex);
		printf("thread2 applied the condition\n");
		pthread_mutex_unlock(&mutex);
		sleep(1);
	}
}
int main()
{
	pthread_t thid1,thid2;
	printf("condition variable study!\n");
	pthread_mutex_init(&mutex, NULL);
	pthread_cond_init(&cond, NULL);
	pthread_create(&thid1, NULL, thread1, NULL);
	pthread_create(&thid2, NULL, thread2, NULL);
	sleep(1);
	do
	{
		pthread_cond_signal(&cond);
	}while(1);
	sleep(20);
	pthread_exit(0);
	return 0;
}
	#include <pthread.h>
#include <unistd.h>
#include "stdio.h"
#include "stdlib.h"
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
struct node
{
	int n_number;
	struct node *n_next;
}*head = NULL;
 
static void cleanup_handler(void *arg)
{
	printf("Cleanup handler of second thread./n");
	free(arg);
	(void)pthread_mutex_unlock(&mtx);
}
static void *thread_func(void *arg)
{
	struct node *p = NULL;
	pthread_cleanup_push(cleanup_handler, p);
	while (1)
	{
		//这个mutex主要是用来保证pthread_cond_wait的并发性
		pthread_mutex_lock(&mtx);
		while (head == NULL)
		{
			//这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何
			//这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线
			//程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。
			//这个时候,应该让线程继续进入pthread_cond_wait
			// pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,
			//然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立
			//而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源
			//用这个流程是比较清楚的
			pthread_cond_wait(&cond, &mtx);
			p = head;
			head = head->n_next;
			printf("Got %d from front of queue/n", p->n_number);
			free(p);
		}
		pthread_mutex_unlock(&mtx); //临界区数据操作完毕,释放互斥锁
	}
	pthread_cleanup_pop(0);
	return 0;
}
int main(void)
{
	pthread_t tid;
	int i;
	struct node *p;
	//子线程会一直等待资源,类似生产者和消费者,但是这里的消费者可以是多个消费者,而
	//不仅仅支持普通的单个消费者,这个模型虽然简单,但是很强大
	pthread_create(&tid, NULL, thread_func, NULL);
	sleep(1);
	for (i = 0; i < 10; i++)
	{
		p = (struct node*)malloc(sizeof(struct node));
		p->n_number = i;
		pthread_mutex_lock(&mtx); //需要操作head这个临界资源,先加锁,
		p->n_next = head;
		head = p;
		pthread_cond_signal(&cond);
		pthread_mutex_unlock(&mtx); //解锁
		sleep(1);
	}
	printf("thread 1 wanna end the line.So cancel thread 2./n");
	//关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,子线程会在最近的取消点,退出
	//线程,而在我们的代码里,最近的取消点肯定就是pthread_cond_wait()了。
	pthread_cancel(tid);
	pthread_join(tid, NULL);
	printf("All done -- exiting/n");
	return 0;
}

三、信号量(sem)

如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。信号量函数的名字都以"sem_"打头。线程使用的基本信号量函数有四个。

信号量初始化。
int sem_init (sem_t *sem , int pshared, unsigned int value);
这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。
等待信号量。给信号量减1,然后等待直到信号量的值大于0。
int sem_wait(sem_t *sem);
释放信号量。信号量值加1。并通知其他等待线程。
int sem_post(sem_t *sem);
销毁信号量。我们用完信号量后都它进行清理。归还占有的一切资源。
int sem_destroy(sem_t *sem);

  #include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#define return_if_fail(p) if((p) == 0){printf ("[%s]:func error!/n", __func__);return;}
typedef struct _PrivInfo
{
	sem_t s1;
	sem_t s2;
	time_t end_time;
}PrivInfo;
 
static void info_init (PrivInfo* thiz);
static void info_destroy (PrivInfo* thiz);
static void* pthread_func_1 (PrivInfo* thiz);
static void* pthread_func_2 (PrivInfo* thiz);
 
int main (int argc, char** argv)
{
	pthread_t pt_1 = 0;
	pthread_t pt_2 = 0;
	int ret = 0;
	PrivInfo* thiz = NULL;
	thiz = (PrivInfo* )malloc (sizeof (PrivInfo));
	if (thiz == NULL)
	{
		printf ("[%s]: Failed to malloc priv./n");
		return -1;
	}
	info_init (thiz);
	ret = pthread_create (&pt_1, NULL, (void*)pthread_func_1, thiz);
	if (ret != 0)
	{
		perror ("pthread_1_create:");
	}
	ret = pthread_create (&pt_2, NULL, (void*)pthread_func_2, thiz);
	if (ret != 0)
	{
		perror ("pthread_2_create:");
	}
	pthread_join (pt_1, NULL);
	pthread_join (pt_2, NULL);
	info_destroy (thiz);
	return 0;
}
static void info_init (PrivInfo* thiz)
{
	return_if_fail (thiz != NULL);
	thiz->end_time = time(NULL) + 10;
	sem_init (&thiz->s1, 0, 1);
	sem_init (&thiz->s2, 0, 0);
	return;
}
static void info_destroy (PrivInfo* thiz)
{
	return_if_fail (thiz != NULL);
	sem_destroy (&thiz->s1);
	sem_destroy (&thiz->s2);
	free (thiz);
	thiz = NULL;
	return;
}
static void* pthread_func_1 (PrivInfo* thiz)
{
	return_if_fail(thiz != NULL);
	while (time(NULL) < thiz->end_time)
	{
		sem_wait (&thiz->s2);
		printf ("pthread1: pthread1 get the lock./n");
		sem_post (&thiz->s1);
		printf ("pthread1: pthread1 unlock/n");
		sleep (1);
	}
	return;
}
static void* pthread_func_2 (PrivInfo* thiz)
{
	return_if_fail (thiz != NULL);
	while (time (NULL) < thiz->end_time)
	{
		sem_wait (&thiz->s1);
		printf ("pthread2: pthread2 get the unlock./n");
		sem_post (&thiz->s2);
		printf ("pthread2: pthread2 unlock./n");
		sleep (1);
	}
	return;
}

11 网络七层

网络七层协议的通俗理解
需求1:
需求2:
需求3:
需求4:
需求5:
需求6:
需求7:
Socket:
这里写图片描述

OSI七层模式简单通俗理解

这个模型学了好多次,总是记不住。今天又看了一遍,发现用历史推演的角度去看问题会更有逻辑,更好记。本文不一定严谨,可能有错漏,主要是抛砖引玉,帮助记性不好的人。总体来说,OSI模型是从底层往上层发展出来的。

这个模型推出的最开始,是是因为美国人有两台机器之间进行通信的需求。

需求1:
科学家要解决的第一个问题是,两个硬件之间怎么通信。具体就是一台发些比特流,然后另一台能收到。

于是,科学家发明了物理层:

主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。

需求2:
现在通过电线我能发数据流了,但是,我还希望通过无线电波,通过其它介质来传输。然后我还要保证传输过去的比特流是正确的,要有纠错功能。

于是,发明了数据链路层:

定义了如何让格式化数据以进行传输,以及如何让控制对物理介质的访问。这一层通常还提供错误检测和纠正,以确保数据的可靠传输。 
在该层中,数据以帧为单位传输。在帧中,有收信地址(Source, SRC)和送信地址(Destination, DST),还有能够探测错误的校验序列(Frame Check Sequence)。当然,帧中最重要的最重要是所要传输的数据 (payload)。

以太网(Ethernet)和WiFi是现在最常见的连接层协议。通过连接层协议,我们可以建立局域的以太网或者WiFi局域网,并让位于同一局域网络中的两台计算机通信。

需求3:
现在我能发正确的发比特流数据到另一台计算机了,但是当我发大量数据时候,可能需要好长时间,例如一个视频格式的,网络会中断好多次(事实上,即使有了物理层和数据链路层,网络还是经常中断,只是中断的时间是毫秒级别的)。

那么,我还须要保证传输大量文件时的准确性。于是,我要对发出去的数据进行封装。就像发快递一样,一个个地发。

于是,先发明了传输层(传输层在OSI模型中,是在网络层上面)

例如TCP,是用于发大量数据的,我发了1万个包出去,另一台电脑就要告诉我是否接受到了1万个包,如果缺了3个包,就告诉我是第1001,234,8888个包丢了,那我再发一次。这样,就能保证对方把这个视频完整接收了。

例如UDP,是用于发送少量数据的。我发20个包出去,一般不会丢包,所以,我不管你收到多少个。在多人互动游戏,也经常用UDP协议,因为一般都是简单的信息,而且有广播的需求。如果用TCP,效率就很低,因为它会不停地告诉主机我收到了20个包,或者我收到了18个包,再发我两个!如果同时有1万台计算机都这样做,那么用TCP反而会降低效率,还不如用UDP,主机发出去就算了,丢几个包你就卡一下,算了,下次再发包你再更新。

TCP协议是会绑定IP和端口的协议,下面会介绍IP协议。

需求4:
传输层只是解决了打包的问题。但是如果我有多台计算机,怎么找到我要发的那台?或者,A要给F发信息,中间要经过B,C,D,E,但是中间还有好多节点如K.J.Z.Y。我怎么选择最佳路径?这就是路由要做的事。

于是,发明了网络层。即路由器,交换价那些具有寻址功能的设备所实现的功能。这一层定义的是IP地址,通过IP地址寻址。所以产生了IP协议。 
通过路由实现不同局域网间的通信。

需求5:
现在我们已经保证给正确的计算机,发送正确的封装过后的信息了。但是用户级别的体验好不好?难道我每次都要调用TCP去打包,然后调用IP协议去找路由,自己去发?当然不行,所以我们要建立一个自动收发包,自动寻址的功能。

于是,发明了会话层。会话层的作用就是建立和管理应用程序之间的通信。

需求6:
现在我能保证应用程序自动收发包和寻址了。但是我要用Linux给window发包,两个系统语法不一致,就像安装包一样,exe是不能在linux下用的,shell在window下也是不能直接运行的。于是需要表示层(presentation),帮我们解决不同系统之间的通信语法问题。

需求7:
OK,现在所有必要条件都准备好了,我们可以写个android程序,web程序去实现需求把。

Socket:
这不是一个协议,而是一个通信模型。其实它最初是伯克利加州分校软件研究所,简称BSD发明的,主要用来一台电脑的两个进程间通信,然后把它用到了两台电脑的进程间通信。所以,可以把它简单理解为进程间通信,不是什么高级的东西。主要做的事情不就是:

A发包:发请求包给某个已经绑定的端口(所以我们经常会访问这样的地址182.13.15.16:1235,1235就是端口);收到B的允许;然后正式发送;发送完了,告诉B要断开链接;收到断开允许,马上断开,然后发送已经断开信息给B。

B收包:绑定端口和IP;然后在这个端口监听;接收到A的请求,发允许给A,并做好接收准备,主要就是清理缓存等待接收新数据;然后正式接收;接受到断开请求,允许断开;确认断开后,继续监听其它请求。

可见,Socket其实就是I/O操作。Socket并不仅限于网络通信。在网络通信中,它涵盖了网络层、传输层、会话层、表示层、应用层——其实这都不需要记,因为Socket通信时候用到了IP和端口,仅这两个就表明了它用到了网络层和传输层;而且它无视多台电脑通信的系统差别,所以它涉及了表示层;一般Socket都是基于一个应用程序的,所以会涉及到会话层和应用层。

12 tcp三次握手

TCP(Transmission Control Protocol) 传输控制协议
TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:
位码即tcp标志位,有6种标示:
SYN(synchronous建立联机) 
ACK(acknowledgement 确认)
PSH(push传送)
FIN(finish结束)
RST(reset重置) 
URG(urgent紧急)
Sequence number(顺序号码)
Acknowledge number(确认号码)

第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;

第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包

第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,
若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,
主机B收到后确认seq值与ack=1则连接建立成功。
完成三次握手,主机A与主机B开始传送数据。

实例:

IP 192.168.1.116.3337 > 192.168.1.123.7788: S 3626544836:3626544836
 IP 192.168.1.123.7788 > 192.168.1.116.3337: S 1739326486:1739326486 ack 3626544837
 IP 192.168.1.116.3337 > 192.168.1.123.7788: ack 1739326487,ack 1

第一次握手:192.168.1.116发送位码syn=1,随机产生seq number=3626544836的数据包到192.168.1.123,192.168.1.123由SYN=1知道192.168.1.116要求建立联机;

第二次握手:192.168.1.123收到请求后要确认联机信息,向192.168.1.116发送ack number=3626544837,syn=1,ack=1,随机产生seq=1739326486的包;

第三次握手:192.168.1.116收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,192.168.1.116会再发送ack number=1739326487,ack=1,192.168.1.123收到后确认seq=seq+1,ack=1则连接建立成功。
一个完整的三次握手也就是 请求---应答---再次确认

13 TCP和UDP有什么区别

TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。

   当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。

   TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。

   UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。

   由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快

14 HTTP协议

http(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,

HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。

TCP 和 HTTP区别: http://blog.csdn.net/lemonxuexue/article/details/4485877

15 常见算法

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值