先看TCP IP的10种状态,如下所示:

  三次握手:

    客户端A端发送SYN,然后进入SYN_SENT状态,服务器B端接收到SYN后,返回一个响应ACK,同时也发送一个SYN,然后B端进入SYN_RCVD状态,A端收到ACK后进入ESTABLISHED状态,然后发送一个ACK,服务器B端收到ACK后进入ESTABLISHED状态。

  四次分手:

  先关闭的一端A端发送FIN然后进入FIN_WAIT_1状态,另一端B端会返回一个响应,然后A端进入FIN_WAIT_2状态。当服务器端B端检测到对端已经关闭(read到0)时,也会调用close,发送FIN给A端,然后B端进入LAST_ACK状态,A端收到FIN后回复一个响应然后进入TIME_WAIT状态,服务器B端接收到响应后进入CLOSED状态。

  三次握手双方都进入ESTABLISHED状态后,表示双方可以通信了。查看TCP的连接状态可以使用netstat -na。

TCP IP协议为什么要做成三次握手和四次断开?

因为TCP IP是全双工协议,需要互相确认对方的身份,A要确认B收到自己发的包了,B也要确认A收到自己发的包了。

  两个银行之间的连接和上述握手过程类似,A银行先发送加密包,B端收到后解密,然后B发送一个加密包,A端收到后解密,在这个过程中,协商出一个秘钥,从此这个秘钥就用来在两个会话之间进行加密。

  四次分手中,任何一端都可以先关闭连接,先调用close的那一端,最终状态要推进到TIME_WAIT,TIME_WAIT状态的含义就是等一会再最终关闭。套接字处于TIME_WAIT状态时,再次在这个套接字上启动服务器是起不来的,除非使用套接字的IO复用技术(SO_REUSEADDR)。

  调用close相当于发送了一个'\0',另一端会读取到0,读取到0就会知道对方已经关闭了(至少对方的写数据断了)。当A端关闭了socket,不代表B端不能往自己的socket中写数据,最多是写失败,毕竟socket是存在缓冲区的。B端得知A端已关闭后,可以根据自己的业务需要,来关闭自己的socket,也可以不关闭。TCP IP是全双工的,两端都执行关闭,才能将这条通路关闭,如果只有一端关闭,那么这条通路是没有彻底关闭的,只是关闭了一个方向的数据流。

  主动关闭的一方进入FIN_WAIT_2就是半连接状态,只要对等方不调用close,这个连接就一直处于半连接状态。服务器端接收到FIN后,是TCP IP内核回复的ACK,对应用透明。

我们用一个实验查看半连接状态。

服务器程序如下:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ void handler(int num)
{
int mypid = ;
while((mypid = waitpid(-, NULL, WNOHANG)) > )
{
printf("child %d die\n", mypid);
}
} int main()
{
int sockfd = ;
signal(SIGCHLD, handler);
sockfd = socket(AF_INET, SOCK_STREAM, ); if(sockfd == -)
{
perror("socket error");
exit();
} struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.6.249");
//addr.sin_addr.s_addr = INADDR_ANY; int optval = ;
if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < )
{
perror("setsockopt error");
exit();
} if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < )
{
perror("bind error");
exit();
} if(listen(sockfd, SOMAXCONN) < )
{
perror("listen error");
exit();
} struct sockaddr_in peeraddr;
socklen_t peerlen; int conn = ; while()
{
conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
if(conn == -)
{
perror("accept error");
exit();
} char *p = NULL;
int peerport = ;
p = inet_ntoa(peeraddr.sin_addr);
peerport = ntohs(peeraddr.sin_port);
printf("peeraddr = %s\n peerport = %d\n", p, peerport); pid_t pid = fork(); if(pid == -)
{
perror("fork error");
exit();
} if(pid == )
{
char recvbuf[] = {};
int ret = ;
while()
{
ret = read(conn, recvbuf, sizeof(recvbuf)); if(ret == )
{
printf("peer closed \n");
exit();
}
else if(ret < )
{
perror("read error");
exit();
} fputs(recvbuf, stdout); write(conn, recvbuf, ret);
}
}
} close(conn);
close(sockfd); return ;
}

客户端程序如下:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ int main()
{
int sockfd[]; int i = ; for(i = ; i < ; i++)
{
sockfd[i] = socket(AF_INET, SOCK_STREAM, ); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.31.128"); if( connect(sockfd[i], (struct sockaddr *)&addr, sizeof(addr)) == - )
{
perror("connect error");
exit();
} struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
if(getsockname(sockfd[i], (struct sockaddr*)&localaddr, &addrlen) < )
{
perror("getsockname error");
exit();
} printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); } char recvbuf[] = {};
char sendbuf[] = {};
int ret = ; while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
write(sockfd[], sendbuf, strlen(sendbuf)); ret = read(sockfd[], recvbuf, sizeof(recvbuf)); fputs(recvbuf, stdout);
memset(recvbuf, , sizeof(recvbuf));
memset(sendbuf, , sizeof(sendbuf)); } return ;
}

我们首先启动服务器,然后启动客户端,然后关掉服务器(执行主动关闭),并查看套接字状态,如下所示:

服务器执行主动关闭,由程序可以得知,服务器关闭后会发送FIN给客户端,客户端的TCP IP协议栈接收到FIN后恢复ACK给服务器,这时候服务器进入了FIN_WAIT2状态,但是客户端一直阻塞在fgets函数那里,所以没有执行close函数,因此,客户端也就不会主动发送FIN,因此,服务器处于半连接状态(由于我们将服务器进程关闭了,所以这个半连接状态过一段时间也会消失,如果不关闭服务器,则会一直处于半连接状态)。客户端套接字一直处于CLOSE_WAIT状态。

  首先关闭的一端存在TIME_WAIT是因为要确保最后一个确认包能发送到对端,保证对端能正常关闭,具体原因见另一篇博客。

  以上我们只说了10种状态,现在来看第11种状态,如下所示:

当通信双方同时close时,会出现第11种状态,叫CLOSIING。

发送FIN后进入FIN_WAIT_1状态,收到对端的FIN后,回复一个ACK并进入CLOSIING状态,收到对端的ACK后进入TIME_WAIT状态。

细节知识:

如果发送端向缓冲区写入数据,然后调用close,这样接收端还能收到数据吗?例如调用如下的程序片段:

send(fd,  "abcd");

close(fd);

接收端是可以收到数据的,而且可以可靠的收到。发送端会先将abcd顺序写入缓冲区,调用close时,会将'\0'也写入缓冲区,然后开始像流水一样顺序发送出去。接收端的TCP IP协议栈会逐个分析,当发现FIN时就知道发送端关闭了。

  服务器向客户端发送了FIN,但是客户端的应用程序没有处理读到的0,而是继续向socket套接字写数据,向服务器发送报文。因为TCP IP是双工的,服务器关闭socket,不等于客户端不能写数据,在这种场景下,如果客户端向服务器发送数据,会引起TCP IP协议RST段重置,会使客户端接收到一个SIGPIPE信号,如果客户端不处理,则默认动作是让进程退出。

管道破裂示例程序如下:

服务器:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ void handler(int num)
{
int mypid = ;
while((mypid = waitpid(-, NULL, WNOHANG)) > )
{
printf("child %d die\n", mypid);
}
} int main()
{
int sockfd = ;
signal(SIGCHLD, handler);
sockfd = socket(AF_INET, SOCK_STREAM, ); if(sockfd == -)
{
perror("socket error");
exit();
} struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.6.249");
//addr.sin_addr.s_addr = INADDR_ANY; int optval = ;
if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < )
{
perror("setsockopt error");
exit();
} if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < )
{
perror("bind error");
exit();
} if(listen(sockfd, SOMAXCONN) < )
{
perror("listen error");
exit();
} struct sockaddr_in peeraddr;
socklen_t peerlen; int conn = ; while()
{
conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
if(conn == -)
{
perror("accept error");
exit();
} char *p = NULL;
int peerport = ;
p = inet_ntoa(peeraddr.sin_addr);
peerport = ntohs(peeraddr.sin_port);
printf("peeraddr = %s\n peerport = %d\n", p, peerport); pid_t pid = fork(); if(pid == -)
{
perror("fork error");
exit();
} if(pid == )
{
printf("child pid=%d\n", getpid());
char recvbuf[] = {};
int ret = ;
while()
{
ret = read(conn, recvbuf, sizeof(recvbuf)); if(ret == )
{
printf("peer closed \n");
exit();
}
else if(ret < )
{
perror("read error");
exit();
} fputs(recvbuf, stdout); write(conn, recvbuf, ret);
}
} close(conn);
} close(conn);
close(sockfd); return ;
}

客户端:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ int main()
{
int sockfd[]; int i = ; for(i = ; i < ; i++)
{
sockfd[i] = socket(AF_INET, SOCK_STREAM, ); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.31.128"); if( connect(sockfd[i], (struct sockaddr *)&addr, sizeof(addr)) == - )
{
perror("connect error");
exit();
} struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
if(getsockname(sockfd[i], (struct sockaddr*)&localaddr, &addrlen) < )
{
perror("getsockname error");
exit();
} printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); } char recvbuf[] = {};
char sendbuf[] = {};
int ret = ; while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
//write(sockfd[0], sendbuf, strlen(sendbuf));
write(sockfd[], "aaaaaaaaaa", );
write(sockfd[], "aaaaaaaaaa", );
write(sockfd[], "aaaaaaaaaa", );
write(sockfd[], "aaaaaaaaaa", );
//ret = read(sockfd[0], recvbuf, sizeof(recvbuf)); //fputs(recvbuf, stdout);
memset(recvbuf, , sizeof(recvbuf));
memset(sendbuf, , sizeof(sendbuf)); } return ;
}

我们的实验步骤是这样的,启动服务器,然后启动客户端,使用kill命令杀死服务器中的业务进程(子进程),然后查看套接字状态,这时可以看到客户端进程处于CLOSE_WAIT状态,因为客户端没有调用close,而是阻塞在fgets函数了。这时,我们在终端上随便输入几个字符,让客户端往套接字写数据。这时客户端会退出(这就是管道破裂了),ps -u已经看不到客户端进程了。

结果如下:

网络服务程序中有些进程莫名退出可能就是管道破裂导致的。

当管道破裂时,我们不想让进程退出,而是捕捉信号做其他处理,因此,需要注册信号处理函数,修改后的客户端程序如下:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ void handler(int num)
{
printf("signal num : %d\n", num);
} int main()
{
int sockfd[]; signal(SIGPIPE, handler); int i = ; for(i = ; i < ; i++)
{
sockfd[i] = socket(AF_INET, SOCK_STREAM, ); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.31.128"); if( connect(sockfd[i], (struct sockaddr *)&addr, sizeof(addr)) == - )
{
perror("connect error");
exit();
} struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
if(getsockname(sockfd[i], (struct sockaddr*)&localaddr, &addrlen) < )
{
perror("getsockname error");
exit();
} printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); } char recvbuf[] = {};
char sendbuf[] = {};
int ret = ; while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
//write(sockfd[0], sendbuf, strlen(sendbuf));
write(sockfd[], "aaaaaaaaaa", );
write(sockfd[], "aaaaaaaaaa", );
write(sockfd[], "aaaaaaaaaa", );
write(sockfd[], "aaaaaaaaaa", );
//ret = read(sockfd[0], recvbuf, sizeof(recvbuf)); //fputs(recvbuf, stdout);
memset(recvbuf, , sizeof(recvbuf));
memset(sendbuf, , sizeof(sendbuf)); } return ;
}

管道破裂时,进程不会退出了,执行结果如下:

实际应用中,我们注册SIGPIPE的信号处理函数,然后对write的返回值做异常处理就好了。

  调用close相当于将读和写全部关闭,shutdown函数可以有选择的终止某个方向的数据的传送或者终止数据传送的两个方向。当我们只想关闭某一个方向的写,不想关闭收时可以调用这个函数。比如我们发送ABC,然后调用shutdown(发送FIN),则关闭了写,ABC和FIN会传送到对端,我们还可以在这一端读取对端的回信。对端可能会回复DEF,然后关闭对端的套接字。

  shutdown  how=1就可以保证对等方接收到一个EOF(\0)字符,而不管其他进程是否已经打开了套接字。而close不能保证,直到套接字引用计数减为0时才发送FIN。也就是说直到所有的进程都关闭了套接字。不管文件描述符的引用计数为2,3,5或者其他,但是我们还想关闭这个文件描述符,这时就可以用shutdown了。

  how参数可以选择关闭读或者写,下面我们进行实验。

服务器端程序:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ void handler(int num)
{
int mypid = ;
while((mypid = waitpid(-, NULL, WNOHANG)) > )
{
printf("child %d die\n", mypid);
}
} int main()
{
int sockfd = ;
signal(SIGCHLD, handler);
sockfd = socket(AF_INET, SOCK_STREAM, ); if(sockfd == -)
{
perror("socket error");
exit();
} struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.6.249");
//addr.sin_addr.s_addr = INADDR_ANY; int optval = ;
if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < )
{
perror("setsockopt error");
exit();
} if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < )
{
perror("bind error");
exit();
} if(listen(sockfd, SOMAXCONN) < )
{
perror("listen error");
exit();
} struct sockaddr_in peeraddr;
socklen_t peerlen; int conn = ; while()
{
conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
if(conn == -)
{
perror("accept error");
exit();
} char *p = NULL;
int peerport = ;
p = inet_ntoa(peeraddr.sin_addr);
peerport = ntohs(peeraddr.sin_port);
printf("peeraddr = %s\n peerport = %d\n", p, peerport); pid_t pid = fork(); if(pid == -)
{
perror("fork error");
exit();
} if(pid == )
{
close(sockfd);
printf("child pid=%d\n", getpid());
char recvbuf[] = {};
int ret = ;
while()
{
ret = read(conn, recvbuf, sizeof(recvbuf)); if(ret == )
{
printf("peer closed \n");
exit();
}
else if(ret < )
{
perror("read error");
exit();
} fputs(recvbuf, stdout);
write(conn, recvbuf, ret); if(recvbuf[] == '')
{
close(conn);
//shutdown(conn, SHUT_WR);
} }
} //close(conn);
} close(conn);
close(sockfd); return ;
}

客户端程序如下:

 #include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */ void handler(int num)
{
printf("signal num : %d\n", num);
} int main()
{
int sockfd[]; signal(SIGPIPE, handler); int i = ; for(i = ; i < ; i++)
{
sockfd[i] = socket(AF_INET, SOCK_STREAM, ); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons();
inet_aton("192.168.31.128", &addr.sin_addr);
//addr.sin_addr.s_addr = inet_addr("192.168.31.128"); if( connect(sockfd[i], (struct sockaddr *)&addr, sizeof(addr)) == - )
{
perror("connect error");
exit();
} struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
if(getsockname(sockfd[i], (struct sockaddr*)&localaddr, &addrlen) < )
{
perror("getsockname error");
exit();
} printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); } char recvbuf[] = {};
char sendbuf[] = {};
int ret = ; while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
write(sockfd[], sendbuf, strlen(sendbuf)); ret = read(sockfd[], recvbuf, sizeof(recvbuf)); if(ret == )
{
perror("server closed");
exit();
} fputs(recvbuf, stdout);
memset(recvbuf, , sizeof(recvbuf));
memset(sendbuf, , sizeof(sendbuf)); } return ;
}

在服务器端程序中,我们将122行的close(conn)注释掉,这时候conn的引用计数为2,第113-117行中,当服务器接收到第一个字符时'2'时,子进程关闭conn,但是这时候conn

的引用计数是1,不会发送FIN,因为调用close时,只有引用计数变为0时才会发送FIN。当客户端接收到FIN时,会进入到67行打印server closed,但是执行时却没有打印,也就是说客户端没有接收到FIN,执行结果如下所示:

  下面我们将服务器程序的第115行注释掉,换成116行的shutdown函数,当服务器接收到第一个字符是‘2’时,会执行shutdown,这时即使conn的引用计数是2,也还是会发送FIN给客户端,客户端接收到FIN时,会打印出server closed,执行结果如下:

7.2 TCP IP的11种状态的更多相关文章

  1. TCP/IP协议11种状态

    1.l  SYN_SENT :这个状态与SYN_RCVD 状态相呼应,当客户端SOCKET执行connect()进行连接时,它首先发送SYN报文,然后随即进入到SYN_SENT 状态,并等待服务端的发 ...

  2. (转)TCP连接的11种状态变迁

    自:http://blog.csdn.net/engrossment/article/details/8104482 http://blog.csdn.net/xiaofei0859/article/ ...

  3. [linux] C语言Linux系统编程-TCP通信的11种状态

    三次握手由client主动发出SYN请求, 此时client处于SYN_SENT状态(第一次握手)当server收到之后会由LISTEN转变为SYN_REVD状态, 并回复client, client ...

  4. TCP协议的11种状态及其变化过程?传输的内容又是什么?

    在TCP的11种状态变迁中,我们需要用到TCP头部的三个标志位: 1.SYN,SYN=1表示这是一个连接请求报文或者连接接受报文 2.ACK,ACK=1,表示确认号生效 3.FIN,FIN=1表示发送 ...

  5. TCP连接的11种状态

    传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的.可靠的.基于字节流的传输层通信协议.TCP协议主要针对三次握手建立连接和四次挥手断开连接,其中包括了 ...

  6. TCP连接的11种状态,三次握手四次挥手原因

    1).LISTEN:首先服务端需要打开一个socket进行监听,状态为LISTEN. /* The socket is listening for incoming connections. 侦听来自 ...

  7. 【Linux网络基础】TCP/IP协议簇的详细介绍(三次握手四次断开,11种状态)

    一.TCP/IP协议簇(DoD参考模型) 用于简化OSI层次,以及相关的标准. 传输控制协议(tcp/ip)簇是相关国防部DoD所创建的,主要用来确保数据的完整性以及在毁灭性战争中维持通信 是由一组不 ...

  8. python TCP协议详解 三次握手四次挥手和11种状态

    11种状态解析 LISTEN  --------------------  等待从任何远端TCP 和端口的连接请求. SYN_SENT  ---------------  发送完一个连接请求后等待一个 ...

  9. TCP之11种状态变迁

    1. TCP 之11种状态变迁 TCP 为一个连接定义了 11 种状态,并且 TCP 规则规定如何基于当前状态及在该状态下所接收的分节从一个状态转换到另一个状态.如,当某个应用进程在 CLOSED 状 ...

随机推荐

  1. hdu 5525 Product 数论算贡献

    Product Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Proble ...

  2. Java 面向对象之继承和重写OverWrite,重写和重载的区别,抽象类

    01继承的概述 A:继承的概念 a:继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系 b:在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作 ...

  3. 如何上传本地文件到github又如何删除自己的github仓库

    首先自己在https://github.com/网站要注册一个账户 自己上传工程到jithub,没有付费的用户只能选用public,意味着你的项目在全网是可以被看到和下载的: 所以涉及私密信息的,需要 ...

  4. 手把手教你开发jquery插件

    I have said that i dislike jQuery UI’s unified API, so i want to get the instance of the component a ...

  5. 通过IIS寄宿WCF服务

    WCF全面解析一书中的例子S104,直接将Service目录部署到iis是无法得到服务相应的,需要在项目中新建一个web项目,删除掉自动生成的所有文件之后,把Service目录下的Calculator ...

  6. codeforces 536a//Tavas and Karafs// Codeforces Round #299(Div. 1)

    题意:一个等差数列,首项为a,公差为b,无限长.操作cz是区间里选择最多m个不同的非0元素减1,最多操作t次,现给出区间左端ll,在t次操作能使区间全为0的情况下,问右端最大为多少. 这么一个简单题吞 ...

  7. android沉浸状态栏和顶部状态栏背景色的设置

    法一: 现在很多应用都引用了沉浸式状态栏,如QQ,效果下图: 这样的效果很酷炫,其实设置很简单. 不过要说明的是,这种效果只能在API19以及以上版本中才能够做到. 如果想让界面Activity中实现 ...

  8. Confluence 6 导入 Active Directory 服务器证书 - UNIX

    为了让你的应用服务器能够信任你的目录服务器.你目录服务器上导出的证书需要导入到你应用服务器的 Java 运行环境中.JDK 存储了信任的证书,这个存储信任证书的文件称为一个 keystore.默认的 ...

  9. Confluence 6 为 Active Directory 配置一个 SSL 连接

    如果你希望配置 Microsoft Active Directory 的读写权限,你需要在你的 Confluence 服务器和JVM keystore 上安装 Active Directory 服务器 ...

  10. 斐波拉契数列(用JavaScript和Python实现)

    1.用JavaScript 判断斐波拉契数列第n个数是多少 //需求:封装一个函数,求斐波那契数列的第n项 //斐波拉契数列 var n=parseInt(prompt("输入你想知道的斐波 ...