题型:选择题(单选和多选)+问答题+编程题(2道)
问答题:
1.简述一下TCP三次握手,为什么是三次握手?
(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
为什么是三次握手?
为什么连接建立需要三次握手,而不是两次握手?
防止失效的连接请求报文段被服务端接收,从而产生错误。
PS:失效的连接请求:若客户端向服务端发送的连接请求丢失,客户端等待应答超时后就会再次发送连接请求,此时,上一个连接请求就是『失效的』。
若建立连接只需两次握手,客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。
作者:大闲人柴毛毛
链接:https://www.zhihu.com/question/24853633/answer/254224088
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
SYN攻击:
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:
#netstat -nap | grep SYN_RECV
生产者消费者代码:
public class ShengchanzheXiaofeizhe {
private final int MAX_SIZE;
private int count;
public ShengchanzheXiaofeizhe(int n){
MAX_SIZE=n;
count=0;
}
public synchronized void add(){
while(count>=MAX_SIZE){
System.out.println("仓库已经满了");
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
count++;
System.out.println(Thread.currentThread().toString()+"put"+count);
this.notifyAll();
}
public synchronized void remove(){
while(count<=0){
System.out.println("仓库已经空了");
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().toString()+"get"+count);
count--;
this.notify();
}
public static void main(String []args){
ShengchanzheXiaofeizhe s=new ShengchanzheXiaofeizhe(5);
Thread pro=new Producer(s);
Thread con=new Consumer(s);
Thread pro2=new Producer(s);
Thread con2=new Consumer(s);
pro.setName("生产者1");
pro2.setName("生产者2");
con.setName("消费者1");
con2.setName("消费者2");
pro.start();
pro2.start();
con.start();
con2.start();
}
}
class Producer extends Thread{
private ShengchanzheXiaofeizhe s;
public Producer(ShengchanzheXiaofeizhe s){
this.s=s;
}
public void run(){
while(true){
s.add();
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
class Consumer extends Thread{
private ShengchanzheXiaofeizhe s;
public Consumer(ShengchanzheXiaofeizhe s){
this.s=s;
}
public void run(){
while(true){
s.remove();
try{
Thread.sleep(1500);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
随机产生10个数字比如(12,32,2323,...10),变成(21,23,3232,....1),再降序输出。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;
public class OPPO01 {
public static void main(String[] args) {
int[] s1=randomNumber(1,1000,10);
int[] s2=new int[10];
for(int i=0;i<10;i++){
int temp=0;
s2[i]=0;
while(s1[i]/10!=0){
temp=s1[i]%10;
s2[i]=(s2[i]+temp)*10;
s1[i]=s1[i]/10;
}
s2[i]=s2[i]+s1[i]%10;
}
sort(s2);
for(int i=0;i<10;i++){
System.out.println(s2[i]);
}
}
public static int[] sort(int[] s){
for(int i=1;i<s.length;i++){
int j;
int e=s[i];
for(j=i-1;j>=0&&s[j]<e;j--){
s[j+1]=s[j];
}
s[j+1]=e;
}
return s;
}
public static int[] randomNumber(int min,int max,int n){
//判断是否已经达到索要输出随机数的个数
if(n>(max-min+1) || max <min){
return null;
}
int[] result = new int[n]; //用于存放结果的数组
int count = 0;
while(count <n){
int num = (int)(Math.random()*(max-min))+min;
boolean flag = true;
for(int j=0;j<count;j++){
if(num == result[j]){
flag = false;
break;
}
}
if(flag){
result[count] = num;
count++;
}
}
return result;
}
}