好教程推荐系列:TCP面试常见题-张小方的知乎Live-轻松搞定技术面试中常见的网络通信问题(二)

本文涉及的产品
视频直播,500GB 1个月
简介: 好教程推荐系列:TCP面试常见题-张小方的知乎Live-轻松搞定技术面试中常见的网络通信问题

10.select函数可以检测网络异常吗?


答:不可以。当网络异常时,select函数可以检测到可读事件,这时候用read函数读取数据,会返回0.


详情见UNP 卷1 6.3.1 select描述符就绪条件 第131页和132页



12. epoll的水平模式LT和边缘模式ET


答:默认是LT。


水平模式指的是低电平到低电平,或者高电平到高电平。


边缘模式指的是低电平到高电平,或者高电平到低电平。


水平模式:如果epoll_wait检测到可读事件,可以一次性把数据读完,也可以分多次读完。


边缘模式:如果epoll_wait检测到可读事件,必须一次性把数据读完,否则如果分两次读,第一次读部分,第二次再读时,没有可读触发信号了,读不到了。只能使用非阻塞的网络模型。


参考博客:http://blog.csdn.net/analogous_love/article/details/60761528


用于windows或linux水平模式下收取数据,这种情况下收取的数据可以小于指定大小,总之一次能收到多少是多少:


bool TcpSession::Recv()  

{  

   //每次只收取256个字节  

   char buff[256];  

   //memset(buff, 0, sizeof(buff));  

   int nRecv = ::recv(clientfd_, buff, 256, 0);  

   if (nRecv == 0)  

       return false;  

 

   inputBuffer_.add(buff, (size_t)nRecv);  

 

   return true;  

}  


如果是linux epoll边缘模式(ET),则一定要一次性收完:


bool TcpSession::RecvEtMode()  

{  

   //每次只收取256个字节  

   char buff[256];  

   while (true)  

   {  

       //memset(buff, 0, sizeof(buff));  

       int nRecv = ::recv(clientfd_, buff, 256, 0);  

       if (nRecv == -1)  

       {  

           if (errno == EWOULDBLOCK || errno == EINTR)  

               return true;  

 

           return false;  

       }  

       //对端关闭了socket  

       else if (nRecv == 0)  

           return false;  

       

      inputBuffer_.add(buff, (size_t)nRecv);  

   }  

 

   return true;  

}  


13. 如何将socket设置成非阻塞的(创建时设置与创建完成后设置),非阻塞socket与阻塞的socket在收发数据上的区别


14. send/recv(read/write)返回值大于0、等于0、小于0的区别


答:


recv:

阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,

特别:非阻塞模式下返回 值 <0时并且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况 下认为连接是正常的,继续接收。

只是阻塞模式下recv会阻塞着接收数据,非阻塞模式下如果没有数据会返回,不会阻塞着读,因此需要 循环读取。

write:

阻塞与非阻塞write返回值没有区分,都是 <0:出错,=0:连接关闭,>0发送数据大小,

特别:非阻塞模式下返回值 <0时并且 (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认为连接是正常的, 继续发送。

只是阻塞模式下write会阻塞着发送数据,非阻塞模式下如果暂时无法发送数据会返回,不会阻塞着 write,因此需要循环发送。

read:

阻塞与非阻塞read返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,

特别:非阻塞模式下返回 值 <0时并且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况 下认为连接是正常的,继续接收。

只是阻塞模式下read会阻塞着接收数据,非阻塞模式下如果没有数据会返回,不会阻塞着读,因此需要 循环读取。

send:

阻塞与非阻塞send返回值没有区分,都是 <0:出错,=0:连接关闭,>0发送数据大小,

特别:非阻塞模式下返回值 <0时并且 (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认为连接是正常的, 继续发送。

只是阻塞模式下send会阻塞着发送数据,非阻塞模式下如果暂时无法发送数据会返回,不会阻塞着 send,因此需要循环发送。



15.如何编写正确的收数据代码与发数据代码


答:都需要考虑缓冲区的设计,发数据如果失败先缓存起来,等待下一次的机会再发。


参考博客:http://blog.csdn.net/analogous_love/article/details/60761528


bool TcpSession::Send()  

{  

   while (true)  

   {  

       int n = ::send(clientfd_, buffer_, buffer_.length(), 0);  

       if (n == -1)  

       {  

           //tcp窗口容量不够, 暂且发不出去,下次再发  

           if (errno == EWOULDBLOCK)  

               break;  

           //被信号中断,继续发送  

           else if (errno == EINTR)  

               continue;  

 

           return false;  

       }  

       //对端关闭了连接  

       else if (n == 0)  

           return false;  

 

       buffer_.erase(n);  

       //全部发送完毕  

       if (buffer_.length() == 0)  

           break;  

   }  

 

   return true;  

}  

tcp是流协议,应用层要自己来区分包的边界,就是加包头包尾。但是tcp有个滑动窗口,假如是10,我第一个数据包占了6,然后在服务器还没读取的时候,我发送第二个,也是6大小,此时会先只发4过去,剩下的6-4=2就得等服务端腾出空间后并提示后再发了.



16.发送数据缓冲区与接收数据缓冲区如何设计


答:参考学习muduo的buffer设计



17.socket选项SO_SNDTIMEO和SO_RCVTIMEO


答:阻塞模式时需要设置超时时间,否则会卡死。



18.socket选项TCP_NODELAY


答:一般来说,应用层send函数不会立刻把数据发出去,而是先给到网卡缓冲区。网卡缓冲区需要等待数据积累到一定量之后才会发送数据,这样会导致一定的延迟。


默认情况下,发送数据采用Nagle算法。这样虽然提高了网络吞吐量,但是实时性却降低了,在一些交互性很强的应用程序来说是不允许的,使用TCP_NODELAY选项可以禁止Nagle算法,避免连续发包出现延迟,这对低延迟网络服务很重要。 此时,应用程序向内核递交的每个数据包都会立即发送出去。需要注意的是,虽然禁止了Nagle 算法,但网络的传输仍然受到TCP确认延迟机制的影响。



19.socket选项SO_REUSEADDR和SO_REUSEPORT(Windows平台与linux平台的区别)


答:说白了当服务器进程关闭时,想立刻再复用原来的ip和端口需要等待2MSL的时间。举个例子,服务器监听了127.0.0.1和8001端口,如果此时结束掉进程,再立刻重启,是不可以再监听成功的。因为TCP四次挥手最后一步TIME_WAIT需要等待应答,如果等不到需要重连。


MSL的时间一般是1min~4min不等。MSL是数据包的最大存活时间,最后一步的ack需要考虑去和回,所以周期是2*MSL。


Linux是所有进程在2MSL的时间内不能复用刚才使用的ip和port,bind会失败;Windows是除了本进程可以,其他进程不可以。操作系统这么设计的。


结论:一般为了方便重启服务器或调试,会设置这两个选项,REUSE就是复用的意思,让进程立刻可以复用地址和端口。



20.socket选项SO_LINGER


答:当调用closesocket关闭套接字时,SO_LINGER将决定系统如何处理残存在套接字发送队列中的数据。处理方式无非两种:丢弃或者将数据继续发送至对端,优雅关闭连接。事实上,SO_LINGER并不被推荐使用,大多数情况下我们推荐使用默认的关闭方式(即下方表格中的第一种情况)。



21.shutdown与优雅关闭


答:socket 多进程中的shutdown, close使用

当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而停止在该socket上的任何数据操作:close(sockfd);

你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据,直至读入所有数据。

int shutdown(int sockfd,int how);

Sockfd是需要关闭的socket的描述符。参数 how允许为shutdown操作选择以下几种方式:

   SHUT_RD:关闭连接的读端。也就是该套接字不再接受数据,任何当前在套接字接受缓冲区的数据将被丢弃。进程将不能对该套接字发出任何读操作。对TCP套接字该调用之后接受到的任何数据将被确认然后无声的丢弃掉。

   SHUT_WR:关闭连接的写端,进程不能在对此套接字发出写操作。

   SHUT_RDWR:相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR。


服务器如果要主动关闭连接,可以这么执行:先关本地“写”端,等对方关闭后,再关本地“读”端。


服务器如果要被动关闭连接,可以这么执行:当read函数返回值是0时,先关本地“写”端,等对方关闭后,再关本地“读”端。



23.socket选项SO_KEEPALIVE


答:一般来说不推荐使用默认的心跳机制,默认是2小时。默认心跳有两个缺陷:


1、貌似设置之后会影响整个操作系统所有应用层的心跳时间;


2、每2小时发一次心跳,有时候会造成流量浪费。比如应用层如果有正常数据交互,不需要发心跳。


具体实现可以参考redis源码anet.c里的anetKeepAlive函数。



24.关于错误码EINTR


答:EINTR是linux中函数的返回状态,在不同的函数中意义不同。表示某种阻塞的操作,被接收到的信号中断,造成的一种错误返回值。

write

表示:由于信号中断,没写成功任何数据。

read

表示:由于信号中断,没读到任何数据。

sem_wait

函数调用被信号处理函数中断。

recv

由于信号中断返回,没有任何数据可用。



25.如何解决tcp粘包问题


答:通过应用层自定义协议来解决


1、固定长度的包


2、每个包以"\r\n"结尾


3、定义结构体,包含固定包头,包体长度等



26.信号SIGPIPE与EPIPE错误码


答:在linux下写socket的程序的时候,如果服务器尝试send到一个disconnected socket上,就会让底层抛出一个SIGPIPE信号。 这个信号的缺省处理方法是退出进程,大多数时候这都不是我们期望的。也就是说,当服务器繁忙,没有及时处理客户端断开连接的事件,就有可能出现在连接断开之后继续发送数据的情况,如果对方断开而本地继续写入的话,就会造成服务器进程意外退出。


根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。若不想客户端退出可以把 SIGPIPE设为SIG_IGN 如:signal(SIGPIPE, SIG_IGN); 这时SIGPIPE交给了系统处理。 服务器采用了fork的话,要收集垃圾进程,防止僵尸进程的产生,可以这样处理: signal(SIGCHLD,SIG_IGN); 交给系统init去回收。 这里子进程就不会产生僵尸进程了。



27.gethostbyname阻塞与错误码获取问题


答:Unix/Linux下的gethostbyname函数常用来向DNS查询一个域名的IP地址。 由于DNS的递归查询,常常会发生gethostbyname函数在查询一个域名时严重超时。而该函数又不能像connect和read等函数那样通过setsockopt或者select函数那样设置超时时间,因此常常成为程序的瓶颈。有人提出一种解决办法是用alarm设置定时信号,如果超时就用setjmp和longjmp跳过gethostbyname函数(这种方式我没有试过,不知道具体效果如何)。

gethostbyname确实是阻塞的,但应该可以设置一个time_out免得DNS Server出问题时老是执行,关于设置Time_out,参阅一下code:


int timeout = TIMEOUT_VALUE;

int err;

SOCKET s;

s = socket( ... );

err = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout).


在使用 gethostbyname() 的时候,你不能用perror() 打印错误信息 (因为 errno 没有使用),你应该调用 herror()。herror()函数签名如下:

void herror(const char *s);


举例如下:参考博客http://blog.csdn.net/analogous_love/article/details/53433994


bool Connect(const char* pszIp, int nPort)  

{  

   struct hostent* pHostent = NULL;  

   char* host = NULL;  

 

   struct sockaddr_in addrSrv;  

   memset(&addrSrv, 0, sizeof(addrSrv));  

   addrSrv.sin_addr.s_addr = ::inet_addr(pszIp);  

   if (addrSrv.sin_addr.s_addr == INADDR_NONE)  

   {  

       pHostent = ::gethostbyname(pszIp);  

       if (pHostent != NULL)  

       {  

           host = inet_ntoa(*((struct in_addr *)pHostent->h_addr));  

           std::cout << pszIp << "=" << host << std::endl;  

       }  

       else  

       {  

           herror("gethostbyname error");  

           std::cout << std::endl;  

           return false;  

       }  

   }  

   else  

       host = (char*)pszIp;  

 

   if (Socket.Connect(host, nPort))  

       return true;  

 

   std::cout << "Unable to connect to server " << pszIp << ":" << nPort << std::endl;  

   return false;  

}


28.心跳包的设计技巧(保活心跳包与业务心跳包)


答:http://blog.csdn.net/analogous_love/article/details/78388187



29.客户端断线重连机制如何设计


答:客户端先2s连接一次服务器,如果失败,再4s连接一次,如果失败,再8s连接一次,如果失败再16s连接一次。。。。。


补充一个情况,当网络状况突变时,立刻连接一次。例如,用户从地铁站出来,手机信号满格了,此时手机app立刻连接服务器。


《Linux多线程服务端编程:使用muduo C++网络库》P333有这么描述:


客户端连接断开后初次重试的延迟应该有随机性,比如说服务端奔溃,它所有的客户连接同时断开,然后0.5s之后再次发起连接,这样既可能造成SYN丢包,也可能给服务器带来短期大负载,影响其服务质量。因此每个客户端应该等待一段随机的时间(0.5~2s),再重试,避免拥塞。



30.如何检测对端已经关闭socket


答:根据ERRNO和recv结果进行判断

在UNIX/LINUX下,非阻塞模式SOCKET可以采用recv+MSG_PEEK的方式进行判断,其中MSG_PEEK保证了仅仅进行状态判断,而不影响数据接收

对于主动关闭的SOCKET, recv返回-1,而且errno被置为9(#define EBADF   9 /* Bad file number */)或104 (#define ECONNRESET 104 /* Connection reset by peer */)

对于被动关闭的SOCKET,recv返回0,而且errno被置为11(#define EWOULDBLOCK EAGAIN /* Operation would block */)

对正常的SOCKET, 如果有接收数据,则返回>0, 否则返回-1,而且errno被置为11(#define EWOULDBLOCK EAGAIN /* Operation would block */)

因此对于简单的状态判断(不过多考虑异常情况):

   recv返回>0,   正常



31.如何清除无效的死链(端与端之间的线路故障)


答:TCP四次挥手时产生的TIME_WAIT或CLOSE_WAIT,造成死链。或者服务器A<->路由器B<->路由器C<->客户端D,链路中的路由器发生了故障,造成死链。需要采取定时器/心跳检测来清理死链。



32.定时器的不同实现及优缺点


答:Windows可以使用OnTimer函数,Linux网络库定时器需要自己实现。


例如libevent的小根堆,libuv的红黑树,muduo的二叉搜索树,nginx的红黑树,redis的升序链表等


学习redis网络库,muduo,优先队列std:priority_queue



34.http协议的具体格式


35.http head、get与post方法的细节


答:


GET /index.php HTTP/1.1\r\n

Host: www.hootina.org\r\n

Connection: keep-alive\r\n

Cache-Control: max-age=0\r\n

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n

content-length: 8

User-Agent: Mozilla/5.0\r\n

\r\n

abcdefgh

POST /index.php HTTP/1.1\r\n

Host: www.hootina.org\r\n

Connection: keep-alive\r\n

Cache-Control: max-age=0\r\n

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n

content-length: 8

User-Agent: Mozilla/5.0\r\n

\r\n

abcdefgh



36.http代理、socks4代理与socks5代理如何编码实现


答:代码如下

BOOL UMySocket::Connect(PNETCONN_INFO pInfo,LPSTR lpMessage) //连接服务器
{
wsysplus_memory vMemory;
long noDelay(1),tmSend(1800*1000L),tmRecv(1800*1000L);
LPSTR lpBuffer=vMemory.GetBuf(8001);
//关闭连接先
UMySocket::Close();
//初始化
if(!lpBuffer)
{
if(lpMessage) strcpy(lpMessage,"MALLOC DATA");
return(FALSE);
}
_hSocket=socket(PF_INET,SOCK_STREAM,0);
if(_hSocket==INVALID_SOCKET)
{
if(lpMessage) strcpy(lpMessage,"INVALID_SOCKET");
return(FALSE);
}
//设置连接属性
setsockopt(_hSocket,IPPROTO_TCP,TCP_NODELAY,(LPSTR)&noDelay,sizeof(long));
setsockopt(_hSocket,SOL_SOCKET,SO_SNDTIMEO,(LPSTR)&tmSend,sizeof(long));
setsockopt(_hSocket,SOL_SOCKET,SO_RCVTIMEO,(LPSTR)&tmRecv,sizeof(long));
//连接服务器
WORD wPortConn=(WORD)pInfo->proxyport;
char *lpServer=pInfo->proxyurl;
sockaddr_in remote={0};
remote.sin_family = AF_INET;
if(!(pInfo->conntype&0x000F)) //未使用代理
{
lpServer = pInfo->srvurl;
wPortConn = (WORD)pInfo->srvport;
}
remote.sin_port = htons(wPortConn); 
LPHOSTENT lphost=NULL; 
if((remote.sin_addr.s_addr=inet_addr(lpServer))==INADDR_NONE) 
{ 
if(lphost=gethostbyname(lpServer)) 
remote.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr; 
} 
do 
{ 
if(connect(_hSocket,(sockaddr*)&remote,sizeof(remote)) 
==SOCKET_ERROR&&WSAGetLastError()!=WSAEWOULDBLOCK) 
{ 
if(lpMessage) sprintf(lpMessage,"服务器:%s:%d连接失败",lpServer,wPortConn); 
break; 
} 
if(pInfo->conntype&PROXY_HTTP)//http proxy 
{ 
sprintf(lpBuffer,"CONNECT %s:%d HTTP/1.0\r\nUser-Agent:rmtcmd/0.1\r\n\r\n", 
pInfo->srvurl,pInfo->srvport); 
send(_hSocket,lpBuffer,strlen(lpBuffer),0); 
if(recv(_hSocket,lpBuffer,8000,0)<1) 
{ 
if(lpMessage) sprintf(lpMessage,"代理服务器:%s:%d通讯失败",pInfo->srvurl,pInfo->srvport); 
break; 
} 
if(strstr(lpBuffer,"Connection established")==NULL) 
{ 
if(lpMessage) sprintf(lpMessage,"代理服务器:%s:%d通讯失败",pInfo->srvurl,pInfo->srvport); 
break; 
} 
}
else if(pInfo->conntype&PROXY_SOCK5) //sock5 proxy 
{ 
PSOCK5REQ req = (PSOCK5REQ)lpBuffer; 
PSOCK5ANS ans = (PSOCK5ANS)(lpBuffer+1024); 
req->ver = 5; 
req->lmethods = 2; 
req->methods[0] = 0; 
req->methods[1] = 2; 
send(_hSocket,(LPSTR)req,4,0); 
if(!Recv(ans,sizeof(SOCK5ANS))|| 
ans->ver!=5||(ans->method&&ans->method!=2)) 
{ 
if(lpMessage) sprintf(lpMessage,"代理服务器:%s:%d通讯失败",pInfo->srvurl,pInfo->srvport); 
break; 
} 
if(ans->method==2)  //need user & passwd 
{ 
PAUTHREQ reqa = (PAUTHREQ)lpBuffer; 
PAUTHANS ansa = (PAUTHANS)(lpBuffer+1024); 
memset(reqa,0,sizeof(AUTHREQ)); 
reqa->ver = 1; 
reqa->ulen = strlen(strcpy(reqa->user,pInfo->proxyuser)); 
reqa->plen = strlen(strcpy(reqa->passwd,pInfo->proxypasswd)); 
send(_hSocket,lpBuffer,sizeof(AUTHREQ),0); 
if(!Recv(ansa,sizeof(AUTHANS))||ansa->ver!=1||ansa->status) 
{ 
if(lpMessage) sprintf(lpMessage,"代理服务器:%s:%d通讯失败",pInfo->srvurl,pInfo->srvport); 
break; 
} 
}
PSOCK5REQEX reqex = (PSOCK5REQEX)lpBuffer; 
PSOCK5ANSEX ansex = (PSOCK5ANSEX)(lpBuffer+1024); 
memset(reqex,0,sizeof(SOCK5REQEX)); 
reqex->ver = 5; 
reqex->cmd = 1; 
reqex->rsv = 0; 
reqex->atyp = 1; 
if((reqex->addr=inet_addr(pInfo->srvurl))==INADDR_NONE) 
{ 
if(lphost=gethostbyname(pInfo->srvurl)) 
reqex->addr = ((LPIN_ADDR)lphost->h_addr)->s_addr; 
} 
reqex->port = ntohs((WORD)pInfo->srvport); 
send(_hSocket,(LPSTR)reqex,sizeof(SOCK5REQEX),0); 
if(!Recv(ansex,sizeof(SOCK5ANSEX))||ansex->ver!=5||ansex->rep) 
{ 
if(lpMessage) sprintf(lpMessage,"代理服务器:%s:%d通讯失败",pInfo->srvurl,pInfo->srvport); 
break; 
} 
} 
else if(pInfo->conntype&PROXY_SOCK4) //sock4 proxy 
{ 
PSOCK4REQ req=(PSOCK4REQ)lpBuffer; 
PSOCK4ANS ans=(PSOCK4ANS)(lpBuffer+1024); 
req->vn = 4; 
req->cd = 1; 
req->port = ntohs((WORD)pInfo->srvport); 
if((req->addr=inet_addr(pInfo->srvurl))==INADDR_NONE) 
{ 
if(lphost=gethostbyname(pInfo->srvurl)) 
req->addr = ((LPIN_ADDR)lphost->h_addr)->s_addr; 
}
send(_hSocket,lpBuffer,sizeof(SOCK4REQ),0); 
if(!Recv(ans,sizeof(SOCK4ANS))) 
{ 
if(lpMessage) sprintf(lpMessage,"代理服务器:%s:%d通讯失败",pInfo->srvurl,pInfo->srvport); 
break; 
} 
if(ans->vn||ans->cd!=90) 
{ 
if(lpMessage) sprintf(lpMessage,"代理服务器:%s:%d通讯失败",pInfo->srvurl,pInfo->srvport); 
break; 
} 
} 
return(TRUE); 
}while(0); 
UMySocket::Close(); 
return(FALSE); 
}
//代理服务器连接 
#pragma pack(push,1) 
//sock4 req & ans 
typedef struct  tagSock4Req 
{ 
char  vn; 
char  cd; 
WORD  port; 
DWORD addr; 
char  other[1]; 
}SOCK4REQ,*PSOCK4REQ; 
typedef struct  tagSock4Ans 
{ 
char  vn; 
char  cd; 
}SOCK4ANS,*PSOCK4ANS; 
//sock5 req & ans 
typedef struct  tagSock5Req 
{ 
char  ver; 
char  lmethods; 
char  methods[255]; 
}SOCK5REQ,*PSOCK5REQ; 
typedef struct  tagSock5Ans 
{ 
char  ver; 
char  method; 
}SOCK5ANS,*PSOCK5ANS; 
//sock5 check user 
typedef struct  tagAuthReq 
{ 
char  ver; 
char  ulen; 
char  user[255]; 
char  plen; 
char  passwd[255]; 
}AUTHREQ,*PAUTHREQ; 
typedef struct  tagAuthAns 
{ 
char  ver; 
char  status; 
}AUTHANS,*PAUTHANS; 
typedef struct  tagSock5ReqEx 
{ 
char  ver; 
char  cmd; 
char  rsv; 
char  atyp; 
long  addr; 
WORD  port; 
}SOCK5REQEX,*PSOCK5REQEX; 
typedef struct  tagSock5AnsEx 
{ 
char  ver; 
char  rep; 
char  rsv; 
char  atyp; 
char  other[1]; 
}SOCK5ANSEX,*PSOCK5ANSEX; 
#pragma pack(pop)

37.ping


38.telnet



39.close函数,fork


答:参考《UNP》卷1,第94页。close是引用计数-1,在没有到0的时候是不会关闭套接字的。fork调用会使父进程打开的socket引用计数+1,。所以一般多进程里面,父进程在fork子进程之后,父进程可以关闭accept套接字,子进程可以关闭listen套接字。这个时候两个套接字计数都从2减到1,所以不会关闭。所以父进程可以只做监听,子进程只做通信。



40.Linux终端调试命令


netstat -nalp|grep 8011 #查看8011端口的连接情况,观察TCP状态图

netstat -nalp|grep 8011|wc -l #查看8011端口的客户端连接数

ulimit -n 102400 #修改当前进程的最大文件数


tcpdump -i any 'tcp port 80'


lsof -i -Pn #lsof是list opened fd的单词缩写


netstat -anip



41.Windows cmd命令


netstat -ano|findstr "8011"#查看8011端口的连接情况,观察TCP状态图


-------


https://leetcode.com/


tcp/ip详解 第1卷,UNP,APUE,TCP/IP协议族


《编程珠玑第2版·修订版》


《编程珠玑(续)(修订版)》


《编程之美——微软技术面试心得》

《剑指OFFER:名企面试官精讲典型编程题(第2版)》


《程序员代码面试指南:IT名企算法与数据结构题目最优解》


《程序员面试宝典(第5版)》


相关文章
|
28天前
|
存储 监控 安全
单位网络监控软件:Java 技术驱动的高效网络监管体系构建
在数字化办公时代,构建基于Java技术的单位网络监控软件至关重要。该软件能精准监管单位网络活动,保障信息安全,提升工作效率。通过网络流量监测、访问控制及连接状态监控等模块,实现高效网络监管,确保网络稳定、安全、高效运行。
52 11
|
14天前
|
负载均衡 网络协议 网络性能优化
动态IP代理技术详解及网络性能优化
动态IP代理技术通过灵活更换IP地址,广泛应用于数据采集、网络安全测试等领域。本文详细解析其工作原理,涵盖HTTP、SOCKS代理及代理池的实现方法,并提供代码示例。同时探讨配置动态代理IP后如何通过智能调度、负载均衡、优化协议选择等方式提升网络性能,确保高效稳定的网络访问。
90 2
|
20天前
|
机器学习/深度学习 安全 网络安全
网络安全词云图与技术浅谈
### 网络安全词云图与技术浅谈 本文介绍了通过词云图展示网络安全关键术语的方法,并探讨了构建现代网络安全体系的关键要素。词云图利用字体大小和颜色突出高频词汇,如恶意软件、防火墙、入侵检测系统等。文中提供了生成词云图的Python代码示例,包括安装依赖库和调整参数。此外,文章详细讨论了恶意软件防护、加密技术、身份验证、DDoS防御、社会工程学防范及威胁情报等核心技术,强调了多层次、多维度的安全策略的重要性。
60 11
网络安全词云图与技术浅谈
|
25天前
|
负载均衡 网络协议 算法
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
这网络层就像搭积木一样,上层协议都是基于下层协议搭出来的。不管是ping(用了ICMP协议)还是tcp本质上都是基于网络层IP协议的数据包,而到了物理层,都是二进制01串,都走网卡发出去了。 如果网络环境没发生变化,目的地又一样,那按道理说他们走的网络路径应该是一样的,什么情况下会不同呢? 我们就从路由这个话题聊起吧。
56 4
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
|
21天前
|
网络协议
TCP报文格式全解析:网络小白变高手的必读指南
本文深入解析TCP报文格式,涵盖源端口、目的端口、序号、确认序号、首部长度、标志字段、窗口大小、检验和、紧急指针及选项字段。每个字段的作用和意义详尽说明,帮助理解TCP协议如何确保可靠的数据传输,是互联网通信的基石。通过学习这些内容,读者可以更好地掌握TCP的工作原理及其在网络中的应用。
|
1月前
|
存储 安全 网络安全
云计算与网络安全:技术融合的双刃剑
在数字化浪潮中,云计算如同一股不可阻挡的力量,推动着企业和个人用户步入一个高效、便捷的新时代。然而,随之而来的网络安全问题也如影随形,成为制约云计算发展的阿喀琉斯之踵。本文将探讨云计算服务中的网络安全挑战,揭示信息保护的重要性,并提供实用的安全策略,旨在为读者呈现一场技术与安全的较量,同时指出如何在享受云服务带来的便利的同时,确保数据的安全和隐私。
29 6
|
2月前
|
供应链 安全 物联网安全
NIST(美国国家标准与技术研究院)在网络安全领域进行了多项创新
NIST(美国国家标准与技术研究院)在网络安全领域进行了多项创新
58 10
|
1月前
|
存储 人工智能 安全
云计算与网络安全:技术融合与挑战
在数字化时代的浪潮中,云计算和网络安全已成为推动社会进步的两大关键技术。本文将探讨云计算服务的发展,网络安全的重要性,以及信息安全技术的演进。我们将通过实例分析,揭示云服务如何增强数据保护,网络安全措施如何应对新兴威胁,以及信息安全技术的创新如何为企业带来竞争优势。文章旨在为读者提供对云计算和网络安全领域的深入理解,并展示它们如何共同塑造我们的未来。
|
1月前
|
监控 安全 网络安全
云计算与网络安全:技术挑战与解决方案
随着云计算技术的飞速发展,其在各行各业的应用越来越广泛。然而,随之而来的网络安全问题也日益凸显。本文将从云服务、网络安全和信息安全等技术领域出发,探讨云计算面临的安全挑战及相应的解决方案。通过实例分析和代码示例,旨在帮助读者更好地理解云计算与网络安全的关系,提高网络安全防护意识。
|
1月前
|
存储 监控 安全
云计算与网络安全:云服务、网络安全、信息安全等技术领域的融合与挑战
本文将探讨云计算与网络安全之间的关系,以及它们在云服务、网络安全和信息安全等技术领域中的融合与挑战。我们将分析云计算的优势和风险,以及如何通过网络安全措施来保护数据和应用程序。我们还将讨论如何确保云服务的可用性和可靠性,以及如何处理网络攻击和数据泄露等问题。最后,我们将提供一些关于如何在云计算环境中实现网络安全的建议和最佳实践。