1.socket长连接与短连接

长连接:在服务端的socket的超时时间内如果没有收到客户端的请求包,那么服务端就会关闭此次连接,因此为了保持长连接的话,客户端需要在服务端的超时时间内发送心跳信息以保持跟服务端的连接,防止服务端关闭连接。

注意:在使用socket进行通信信息交互的时候,双方需要约定好消息包的格式(从程序员的角度来看就是定义好消息的结构体),如果是C语言的话则直接将结构体赋值后放到send函数的参数内即可,如果是java的话则需要转为字节流后利用outpurtstream发送出去。除此之外,还需要注意的是网络字节序的问题,在X86和Linux下都是采用的小端字节序,在Mac下采用的是大端字节序,为了在网络传输的统一期,规定了网络字节序都采用大端字节序。所以,如果通信双方都是小端字节序的话,那么接收一方需要将网络传输过来的大端字节序进行转换。

大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;

小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。

这两种模式,方便记忆:“小端低低”。这样就知道小端的模式,反之大端的模式。 

2.http长连接与短连接

众所周知http1.1(http1.0不是标准,依服务器而定)默认是支持长连接的,长连接能够保证服务器和客户端的socket能够高效利用,减少握手等额外的开销。在客户端的请求头中添加"Connection" , "Keep-Alive" or "close"来控制是否保持连接。

正常情况下,服务器在返回内容的头中会带上如下信息:

http/1.1  200 OK\r\n
server: Apache-Coyote/1.1 \r\n
Content-Length:0 \r\n
Date: Tue,28 Jan 2014 GMT \r\n
\r\n

但是如果服务器端连接次数计数达到指定值时,则会在返回内容中添加Connection: close信息,表示该连接将被关闭。

http/1.1  200 OK\r\n

server: Apache-Coyote/1.1 \r\n
Content-Length:0 \r\n
Date: Tue,28 Jan 2014 GMT \r\n
Connection:close \r\n
\r\n

这个时候该connection就失效了,客户端如果下次请求服务器的话,需要重新创建一个新的连接。但是有个问题,是否连接保持是由服务器端决定的,一旦连接超时导致connection close,服务器是不会专门通知客户端的,所以我们不能依赖Connection: close信息来决定是否关闭socket。我们先来看看tomcat对长连接的配置。下面是一个典型的配置长连接的方式:

其中:

keepAliveTimeout:表示在下次请求过来之前,tomcat保持该连接多久。这就是说假如客户端不断有请求过来,且为超过过期时间,则该连接将一直保持。

maxKeepAliveRequests:表示该连接最大支持的请求数。超过该请求数的连接也将被关闭(此时就会返回一个Connection: close头给客户端)。 

3.一个最简单的长连接与心跳保持的示例程序

1. #include <stdio.h>
2. #include <string.h>
3. #include <errno.h>
4. #include <sys/socket.h>
5. #include <resolv.h>
6. #include <stdlib.h>
7. #include <netinet/in.h>
8. #include <arpa/inet.h>
9. #include <arpa/inet.h>
10. #include <unistd.h>
11. #include <sys/time.h>
12. #include <sys/types.h>
 
#define MAXBUF 1024 
int main(int argc, char **argv) 
{ 
  int sockfd, len; 
  struct sockaddr_in dest; 
  char buffer[MAXBUF]; 
  char heartbeat[20] = "hello server"; 
  fd_set rfds; 
  struct timeval tv; 
  int retval, maxfd = -1; 
  if (argc != 3) 
  { 
    printf("error! the right format should be : \ 
          \n\t\t%s IP port\n\t eg:\t%s127.0.0.1 80\n", 
          argv[0], argv[0]); 
    exit(0); 
  } 
  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
  { 
    perror("Socket"); 
    exit(errno); 
  } 
  bzero(&dest, sizeof(dest)); 
  dest.sin_family = AF_INET; 
  dest.sin_port = htons(atoi(argv[2])); 
  memset(&(dest.sin_zero), 0, 8); 
  if (inet_aton(argv[1], (struct in_addr*)&dest.sin_addr.s_addr) == 0) 
  { 
    perror(argv[1]); 
    exit(errno); 
  } 
  if (connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0) 
  { 
    perror("Connect"); 
    exit(errno); 
  } 
  printf("\nReady to start chatting.\n\ 
        Direct input messages and \n\ 
        enter to send messages to the server\n"); 
  while (1) 
  { 
    FD_ZERO(&rfds); 
    FD_SET(0, &rfds); 
    maxfd = 0; 
    FD_SET(sockfd, &rfds); 
    if (sockfd > maxfd) 
      maxfd = sockfd;
    tv.tv_sec = 2; 
    tv.tv_usec = 0; 
    retval = select(maxfd+1, &rfds, NULL, NULL, &tv); 
    if (retval == -1) 
    { 
      printf("Will exit and the select is error! %s", strerror(errno)); 
      break; 
    } 
else if (retval == 0)//外层的if,对应于select的返回值 
    { 
      //printf("No message comes, no buttons, continue to wait ...\n");
      len = send(sockfd, heartbeat, strlen(heartbeat), 0);
      if (len < 0)
      {
        printf("Message '%s' failed to send ! \
              The error code is %d, error message '%s'\n",
              heartbeat, errno, strerror(errno));
        break;
      } else
      {
        printf("News: %s \t send, sent a total of %d bytes!\n", heartbeat, len);
      }
      continue;
    },对应于select的返回值
    {      if (FD_ISSET(sockfd, &rfds))
      {
        bzero(buffer, MAXBUF+1);
        len = recv(sockfd, buffer, MAXBUF, 0);

        if (len > 0)
        {
          printf("Successfully received the message: '%s',%d bytes of data\n",
                  buffer, len);
        } else {
          if (len < 0)
              printf("Failed to receive the message! \
                    The error code is %d, error message is '%s'\n",
                    errno, strerror(errno));
          else
              printf("Chat to terminate!\n");

          break;
,对应于select的返回值
      }//while


      if (FD_ISSET(0, &rfds))
      {
        bzero(buffer, MAXBUF+1);
        fgets(buffer, MAXBUF, stdin);


        if (!strncasecmp(buffer, "quit", 4))
        {
          printf("Own request to terminate the chat!\n");
          break;
        }


        len = send(sockfd, buffer, strlen(buffer)-1, 0);
        if (len < 0)
        {
          printf("Message '%s' failed to send ! \
                The error code is %d, error message '%s'\n",
                buffer, errno, strerror(errno));
          break;
        }
        else
        {
          printf("News: %s \t send, sent a total of %d bytes!\n",
                buffer, len);
        }
      }
    }
  }


  close(sockfd);
  return 0;
}