网络相关面试题总结(二)

网络编程实例代码 : 手写

客户端:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd != -1);
    struct sockaddr_in saddr;

    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("192.168.31.96");

    int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res != -1);

    char buff[128] = {0};
    printf("input : \n");
    fgets(buff,128,stdin);
    send(sockfd,buff,strlen(buff),0);
    memset(buff,0,128);
    recv(sockfd,buff,127,0);
    printf("buff=%s\n",buff);

    close(sockfd);
}

服务器端:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd != -1);
    struct sockaddr_in saddr,caddr;

    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr( "127.0.0.1");
   
    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res != -1);

    listen(sockfd,5);//5是监听队列的长度,经验值.

    while(1)
    {
         int len = sizeof(caddr);
         int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//c:链接套接字
         if( c < 0 )
         {
             continue;
         }
        
         char buff[128] = {0};
         int n = recv(c,buff,127,0);
         printf("n = %d ,buff = %s\n",n,buff);
         send(c,"ok",2,0);

         close(c);
    }
    exit(0);
}

 

三次握手可以改为两次吗,可以改为4次吗?哪个阶段可能受到攻击?

TCP面向连接的,所以在通信前,先用“三次握手”来建立连接 。如果connect 返回成功,则三次握手完成。要思考能不能改为两次,首先要知道每次的握手都干了什么?

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;[SYN:同步序列编号(Synchronize Sequence Numbers)]

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

如果采取两次握手,相当于第二次握手结束便建立连接,如果发送SYN的一方不想连接了,也不会有反馈,另一方却一直在等待,浪费了时间,这样就会产生僵尸连接,浪费了网络资源。当然也可以采取4次甚至N次握手,但是没有必要,建立连接的时间太长,双方通信效果也会大打折扣。所以3次只是折中方案,保证了可靠性,又节俭了建立连接的时间。所以,三次握手两次不行

可能受到的攻击:SYN溢出攻击 / SYN泛洪

在三次握手的时候,容易受到的攻击是syn溢出攻击,也叫syn泛洪,攻击者通过发送TCP 的SYN,而当服务器返回ACK后,该攻击者就不对其进行再确认,那这个TCP连接就处于挂起状态,也就是所谓的半连接状态,服务器收不到再确认的话,还会重复发送ACK给攻击者。这样更加会浪费服务器的资源。攻击者就对服务器发送非常大量的这种TCP连接,由于每一个都没法完成三次握手,所以在服务器上,这些TCP连接会因为挂起状态而消耗CPU和内存,最后服务器可能死机,就无法为正常用户提供服务了

四次挥手可以改为三次吗?

因为断开连接可以是任意一方,所以用A、B来代表通信双方。

(1)A的应用进程先向其TCP发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN-WAIT-1(终止等待1)状态,等待B的确认。

(2)B收到连接释放报文段后即发出确认报文段,(ACK=1,确认号ack=u+1,序号seq=v),B进入CLOSE-WAIT(关闭等待)状态,此时的TCP处于半关闭状态,A到B的连接释放。

(3)A收到B的确认后,进入FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段。

(4)B没有要向A发出的数据,B发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),B进入LAST-ACK(最后确认)状态,等待A的确认。

(5)A收到B的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),A进入TIME-WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL(最长报文段寿命Maximum Segment Lifetime),A才进入CLOSED状态

如果B发出的确认报文段和释放报文段同时发出,就可以看作是三次。

断开连接时,可能受到什么攻击?

FIN 重复发送

time_wait 状态存在的意义

可靠的终止tcp连接。

让迟来的报文被识别,并丢弃

在连接出错如何处理

回复RST报文(复位报文) 序号发送 ,确认号+1

对于异常事件,采用带MSG_OOB标志的recv函数读取带外数据,表示紧急指针,将数据放到最前面进行读取

画出IP的报头和TCP的报头

IPV4报头(图源《Linux高性能服务器编程》2.2)

TCP报头(图源《Linux高性能服务器编程》。3.2)

如何防止connect长久阻塞:

(1)套接字设置超时时间

(2)将套接字设置成非阻塞的模式 fcntl

如何解决粘包问题:

TCP的可靠机制:应答确认,超时重传 ,通过滑动窗口进行流量控制 ,TCP是一个流式服务,没有一个明确的起始和结尾,多次发送的数据可以被对方一次接收,就会产生粘包问题。

1、 send /recv /send : 将每次的数据分开发送,只有收到对方发送的确认信息,才能继续发送。

2、为数据设置起始和结束的标记。比如可以使用’#’作为数据包的结束。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值