编程实现与时间戳服务器的通信

在上一篇博文《介绍一个法国的时间戳服务器》中,介绍了获取时间戳响应的方法,分为两步:

1. 使用 OpenSSL 生成一个时间戳请求文件;

2. 使用 Linux 下的 curl 命令,发送时间戳请求、接收时间戳响应。

要想在 Windows 下执行第 2 步,有两种途径:

1) 下载为 Windows 平台实现的 curl 程序,可以到 http://curl.haxx.se/download.html 下载;

2)不用 curl 命令,自己编程实现向那个法国的时间戳服务器发送时间戳请求、接收时间戳响应。

为了更好地了解与时间戳服务器通信的过程细节,这里介绍一下第 2 种途径,即编程实现,先介绍 C 语言实现:

a) 为了读取时间戳请求文件,要实现获取文件大小的函数,实现过程如下:

文件1

get_small_file_size.h

#ifndef GET_SMALL_FILE_SIZE_H
  #define GET_SMALL_FILE_SIZE_H

#ifdef  __cplusplus
extern "C" {
#endif

/**************************************************
*函数名称:GetSmallFileSize
*功能: 获取文件的大小,结果以字节为单位
*参数:
    file_name[in]        文件名
    file_byte_size[out]  文件大小
*返回值:
    0   成功
	-1  失败
*备注:
  该函数对实际文件大小有限制,文件大小不能超过 (2G-1) Bytes,
  因为调用了 ftell() 函数,该函数的返回值是 long 类型,能表示的
  整数最大不会超过 (2G-1) Bytes
**************************************************/
int GetSmallFileSize(char *file_name, long *file_byte_size);

#ifdef  __cplusplus
}
#endif

#endif /* end of GET_SMALL_FILE_SIZE_H */

文件2

get_small_file_size.c

#include "get_small_file_size.h"
#include <stdio.h>

int GetSmallFileSize(char *file_name, long *file_byte_size)
{
  FILE * fp;

  if ( !(fp=fopen(file_name, "rb")) )
  {
    printf("Open file %s failed!\n", file_name);
    return (-1);
  }
  fseek(fp, 0L, SEEK_END);
  *file_byte_size=ftell(fp);
  fclose(fp);
  return 0;
}

b) 为了与时间戳服务器通信,要实现用 Windows socket 方式通信的函数,实现过程如下:

文件3

socket_communication.h

#ifndef SOCKET_COMMUNICATION_H
  #define SOCKET_COMMUNICATION_H

#ifdef  __cplusplus
extern "C" {
#endif

/**************************************************
*函数名称:socket_communication
*功能: 以 socket 通信方式向指定 IP 地址和端口号的主机发送请求数据,接收响应数据
*参数:
    ip[in]                   对方主机的 IP 地址
    port[in]                 对方主机接收数据时使用的端口号
    request[in]              缓冲区首地址,该缓冲区用来存放要发送的请求数据
    request_len[in]          要发送的请求数据的长度,以字节为单位
    response[out]            缓冲区首地址,该缓冲区用来存放接收到的响应数据
    response_buffer_len[in]  存放接收到的响应数据的缓冲区大小,以字节为单位
    response_len[out]        实际接收到的响应数据的长度,以字节为单位
*返回值:
    0   成功
    -1  失败
**************************************************/
int socket_communication(unsigned char *ip,
			 unsigned int port,
			 unsigned char *request,
			 unsigned int request_len,
			 unsigned char *response,
			 unsigned int response_buffer_len,
			 unsigned int *response_len);

#ifdef  __cplusplus
}
#endif

#endif /* end of SOCKET_COMMUNICATION_H */

文件4

socket_communication.c

#include "socket_communication.h"
#include <stdio.h>
#include <Winsock2.h>

#pragma comment(lib,"WS2_32.lib")

int socket_communication(unsigned char *ip,
			 unsigned int port,
			 unsigned char *request,
		         unsigned int request_len,
			 unsigned char *response,
			 unsigned int response_buffer_len,
			 unsigned int *response_len)
{
  int result, received_data_len;
  char *p;
  WSADATA wsaData;
  SOCKET sockClient;
  SOCKADDR_IN addrSrv;

  result = WSAStartup(MAKEWORD(2, 2), &wsaData);
  if (result != NO_ERROR)
  {
    printf("WSAStartup function failed with error: %d at %s, line %d!\n", result, __FILE__, __LINE__);
    return (-1);
  }

  sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if( sockClient == INVALID_SOCKET )
  {
    printf("Create socket failed with error: %ld at %s, line %d!\n", WSAGetLastError(), __FILE__, __LINE__);
    WSACleanup();
    return (-1);
  }
  addrSrv.sin_addr.s_addr = inet_addr((char *)(ip));
  addrSrv.sin_family = AF_INET;
  addrSrv.sin_port = htons(port);
  result = connect(sockClient, (SOCKADDR*)(&addrSrv), sizeof(SOCKADDR));
  if (result == SOCKET_ERROR)
  {
    printf("Invoke connect() function failed with error: %ld at %s, line %d!\n", WSAGetLastError(),  __FILE__, __LINE__);
    closesocket(sockClient);
    WSACleanup( );
    return (-1);
  }

/* 发送时间戳请求 */
  printf("Sending data...");
  result = send(sockClient, (char *)request, (int)request_len, 0);
  if (result == -1)
  {
    printf("Send data failed at %s, line %d!\n", __FILE__, __LINE__);
    closesocket(sockClient);
    WSACleanup( );
    return (-1);
  }
  printf("%d bytes have been sent.\n", result);

/* 接收时间戳请求 */
  printf("Receiving data...");
  p = (char *)response;
  received_data_len = 0;
  while (1)
  {
    result = recv(sockClient, p, (response_buffer_len - received_data_len), 0);
    if (result == -1)
    {
      printf("Receive data failed at %s, line %d!\n", __FILE__, __LINE__);
      closesocket(sockClient);
      WSACleanup();
      return (-1);
    }
   if (result == 0)
     break;
   p += result;
   received_data_len += result;
  }
  *response_len = received_data_len;
  printf("Receiving data complete.\n");
  printf("%d bytes have been received.\n", received_data_len);

  result = closesocket(sockClient);
  if (result == SOCKET_ERROR)
  {
    printf("Invoke closesocket() function failed with error: %ld at %s, line %d!\n", WSAGetLastError(), __FILE__, __LINE__);
    WSACleanup();
    return (-1);
  }

  WSACleanup();
  return 0;
}

c) 实现发送 HTTP POST 请求、接收时间戳服务器 HTTP 响应消息的函数,其功能包括:读取时间戳请求文件,生成一个 HTTP POST 请求,发给时间戳服务器,接收时间戳服务器返回的 HTTP 消息,并从 HTTP 消息中提取出 Content (这部分内容就是 ASN.1 编码格式的时间戳响应),输出或将其保存到文件中。实现过程如下:

文件5

TS_communication.h

/**************************************************
* File name: TS_communication.h
* Author: HAN Wei
* Author's blog: http://blog.csdn.net/henter/
* Date: Aug 21st, 2014
**************************************************/ 

#ifndef TIME_STAMP_COMMUNICATION_H
  #define TIME_STAMP_COMMUNICATION_H

#ifdef  __cplusplus
extern "C" {
#endif

/**************************************************
*函数名称:TS_Communication
*功能: 将时间戳请求文件发送到时间戳服务器,接收时间戳响应
*参数:
    TS_request_file_name[in]   时间戳请求文件名
    TS_server_ip[in]           时间戳服务器的 IP 地址
    TS_server_port[in]         时间戳服务器接收 HTTP POST 请求的端口号
    save_file_flag[in]         表示是否要将时间戳服务器返回的时间戳响应保存到文件的标志变量
    TS_response_file_name[in]  时间戳响应文件名
*返回值:
    0   成功
    -1  失败
*备注:
1. 与本函数通信的时间戳服务器的网址是 http://timestamping.edelweb.fr/ ,
   其 IP 地址是:92.103.215.77。
2. 对于输入参数 TS_request_file_name 指定的时间戳请求文件,其大小必须
   小于 2GB,内容是时间戳请求的 ASN.1 编码,编码方式在 RFC 3161 中规定。
3. 输入参数 save_file_flag 用来表示是否要将时间戳服务器返回的时间戳响应
   保存到文件,当其取值为 0 时,表示不需要保存到文件,此时输入参数
   TS_response_file_name 将被忽略;当其取值为任意非零的无符号整数时,
   表示要将时间戳响应保存到文件,文件名由 TS_response_file_name 指定。
**************************************************/
int TS_Communication(char *TS_request_file_name,
                     unsigned char *TS_server_ip,
		     unsigned int TS_server_port,
		     unsigned int save_file_flag,
		     char *TS_response_file_name);

#ifdef  __cplusplus
}
#endif

#endif /* end of TIME_STAMP_COMMUNICATION_H */

文件6

TS_communication.c

/**************************************************
* File name: TS_communication.c
* Author: HAN Wei
* Author's blog: http://blog.csdn.net/henter/
* Date: Aug 21st, 2014
**************************************************/ 

#include "get_small_file_size.h"
#include "socket_communication.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_IP_PACKET_LEN (int)(65535)

int TS_Communication(char *TS_request_file_name,
                     unsigned char *TS_server_ip,
		     unsigned int TS_server_port,
		     unsigned int save_file_flag,
		     char *TS_response_file_name)
{
  int error_code, i;
  FILE *input_fp, *output_fp;
  long input_file_length;
  unsigned char *request;
  unsigned char response[MAX_IP_PACKET_LEN];
  unsigned int request_len, response_buffer_len, response_len, message_len=0;
  unsigned char content_len[32], message[MAX_IP_PACKET_LEN];
  unsigned char *p, *q, *content_start;

  if ( !(input_fp=fopen(TS_request_file_name, "rb")) )
  {
    printf("Open file %s failed at %s, line %d!\n", TS_request_file_name, __FILE__, __LINE__);
    return (-1);
  }
  if ( error_code=GetSmallFileSize(TS_request_file_name, &input_file_length) )
  {
    printf("Get file %s length failed at %s, line %d!\n", TS_request_file_name, __FILE__, __LINE__);
    return (-1);
  }
  request_len = (unsigned int)input_file_length;
  printf("Time stamp request ASN.1 encode length is %d bytes\n", request_len);
  if ( !(request=(unsigned char *)malloc(request_len)) )
  {
    printf("Invoke malloc() function failed at %s, line %d!\n", __FILE__, __LINE__);
    fclose(input_fp);
    return (-1);
  }
  fread(request, request_len, 1, input_fp);
  fclose(input_fp);
  printf("Time stamp request:\n");
  for (i=0; i<(int)(request_len); i++)
    printf("0x%x  ", request[i]);
  printf("\n");

  memset(content_len, 0, sizeof(content_len));
  sprintf((char *)(content_len), "%d", request_len);
  memset(message, 0, sizeof(message));
  strcat((char *)(message), "POST /service/tsp HTTP/1.1\n");
  strcat((char *)(message), "Host: timestamping.edelweb.fr\n");
  strcat((char *)(message), "Content-Type: application/timestamp-query\n");
  strcat((char *)(message), "Content-Length: ");
  strcat((char *)(message), (char *)(content_len));
  strcat((char *)(message), "\n\n");
  p = message + strlen((char *)(message));
  message_len = strlen((char *)(message));
  memcpy(p, request, request_len);
  p = p + request_len;
  message_len += request_len;
  *p='\n';
  message_len++;
  printf("HTTP POST message length is %d bytes.\n", message_len);
  printf("HTTP POST message:\n");
  for (i=0; i<(int)(message_len); i++)
    printf("%c", message[i]);
  printf("\n");
  free(request);

  response_buffer_len = sizeof(response);
  if ( error_code=socket_communication(TS_server_ip,
				       TS_server_port,
				       message,
				       message_len,
				       response,
				       response_buffer_len,
				       &response_len) )
  {
    printf("Communicate with time stamp server failed at %s, line %d!\n", __FILE__, __LINE__);
    return (-1);
  }

  printf("HTTP Time stamp response is %d bytes.\n", response_len);
  printf("HTTP time stamp response:\n");
  for (i=0; i<(int)(response_len); i++)
    printf("%c", response[i]);
  printf("\n");

  q = (unsigned char *)strstr((char *)response, "Content-Type: application/timestamp-reply");
  if (q!=NULL)
  {
   q += (strlen("Content-Type: application/timestamp-reply")+4);
/* 注意在时间戳服务器返回的 HTTP 响应中,
   Content-Type: application/timestamp-reply 这一行之后有一个空行,
   所以这一行内容之后的换行符总共有四个字符,即: 0x0d 0x0a 0x0d 0x0a。
   将指针 q 移动到这四个字符之后,指针才指向 HTTP Content 的起始位置 */
   content_start = q;
  }
  else
  {
    printf("Error at %s, line %d!\n", __FILE__, __LINE__);
    printf("Can't find string 'Content-Type: application/timestamp-reply' in HTTP response!\n");
    return (-1);
  }

  i=0;
  printf("\nContent:\n");
  while ( q < (response+response_len) )
  {
    printf("0x%x  ", *q);
    q++;
    i++;
  }
  printf("\n");

  printf("Content length is %d bytes.\n", i);

  if (save_file_flag != 0)
  {
    if ( !(output_fp=fopen(TS_response_file_name, "wb")) )
    {
      printf("Create file %s failed at %s, line %d!\n", TS_response_file_name, __FILE__, __LINE__);
      return (-1);
    }
    fwrite(content_start, i, 1, output_fp);
    fclose(output_fp);
    printf("Save time stamp response ASN.1 encode into file %s succeeded!\n", TS_response_file_name);
  }
  return 0;
}

d) 调用已有的子函数,实现向法国时间戳服务器发送请求、接收响应。实现过程如下:

文件7

test.c

/**************************************************
* File name: test.c
* Author: HAN Wei
* Author's blog: http://blog.csdn.net/henter/
* Date: Aug 21st, 2014
**************************************************/ 

#include "TS_communication.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
  int error_code;
  unsigned char ip[64]="92.103.215.77";  /* 法国时间戳服务器的 IP 地址 */
  unsigned int port=80;
  unsigned int wrtie_file_flag=1;

  if (argc != 3)
  {
    printf("Invalid input!\n");
    system("pause");
    return (-1);
  }
  if ( error_code=TS_Communication(argv[1],
                                   ip,
			           port,
			           wrtie_file_flag,
			           argv[2]) )
  {
    printf("Send time stamp request or acquire time stamp response failed at %s, line %d!\n", __FILE__, __LINE__);
    return (-1);
  }
  printf("Test sending time stamp request and receiving time stamp response succeeded!\n");
  system("pause");
  return 0;
}

一共有 7 个文件,假定编译后得到的 exe 文件名是 TS_communication.exe,可在 Windows 的命令提示符界面下执行命令:

TS_communication  时间戳请求文件名  时间戳响应文件名

例如:已经用 OpenSSL 生成了一个时间戳请求文件,名字是 TS_request.tsq,希望将从时间戳服务器返回的时间戳响应(ASN.1编码格式)保存到文件 TS_response.tsr 中,执行的命令是

TS_communication TS_request.tsq TS_response.tsr

以上介绍了 C 语言的实现过程。如果用 Python 语言实现,由于有一个名为 httplib2 的库,实现过程会简单得太多!

下面给出一个很粗糙的(未做异常检查和处理)示例程序:

import httplib2

in_file=open("TS_request.tsq", "rb")
request=in_file.read()
in_file.close()

h=httplib2.Http(".cache")
resp, content=h.request("http://timestamping.edelweb.fr/service/tsp", "POST", body=request, headers={'Content-Type':'application/timestamp-query'})

out_file=open("TS_response.tsr","wb")
out_file.write(content)
out_file.close()

要在 Python 中添加 httplib2 库,这里介绍两种方法:

1. 可以到  https://pypi.python.org/pypi/httplib2 下载,当前最新版本号是 0.9,下载文件 httplib2-0.9.zip 后解压缩,到解压缩后的目录中,执行命令

python setup.py install

2. 如果 Python 中已经安装了 pip 工具,执行命令

pip install httplib2

编程实现与时间戳服务器的通信

时间: 2024-08-05 11:22:16

编程实现与时间戳服务器的通信的相关文章

Socket编程总结—Android手机服务器与多个Android手机客户端之间的通信(非阻塞)

根据前两周写的关于Socket编程的网络通信的代码,现在对有关知识和注意事项进行总结如下: 1.首先说下Android NIO中有关Socket编程的类: 1)ServerSocketChannel类:服务器套接字通道相当于传统IO下的ServerSocket,通过ServerSocketChannel的socket()可以获得传统的ServerSocket,反过来使用ServerSocket的getChannel()可以获得ServerSocketChannel对象:实例化ServerSock

编程实现获取linux服务器cpu、内存和磁盘使用率

proc文件系统介绍 /proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为内核与进程提供通信的接口.用户和应用程序可以通过/proc得到系统的信息,并可以改变内核的某些参数.由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取/proc目录中的文件时,proc文件系统是动态从系统内核读出所需信息并提交的. /proc目录中有一些以数字命名的目录,它们是进程目录.系统中当前运行的每一个进程在/proc下都对应一个以进程号为目录名的目录/proc/pi

Android开发之简单的聊天室(客户端与服务器进行通信)

1.使用ServerSocket创建TCP服务器端 Java中能接收其他通信实体连接请求的类是ServerSocket, ServerSocket对象用于监听来 自客户端的Socket连接,如果没有连接,它将一直处于等待状态.ServerSocket包含一个监听来自客户端连接请求的方法. 1) Socket accept():如果接收到一个客户端Socket的连接请求,该方法将返回一个与连接客户端Socket对应的Socket;否则该方法将一直处于等待状态,线程也被阻塞. 创建ServerSoc

Java网络编程(tcp在服务器上应用多线程)

package org.tcp; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; public class EchoThread implements Runnable { private Socket client = null; public EchoThread(Socket client){ this.c

介绍一个法国的时间戳服务器

在学习RFC 3161 <Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)>时,发现该文档中没有提供具体的示例,如果能有具体的例子对照标准学习,效果会好得多.在网上找到了一个法国的时间戳服务器,网址是: http://timestamping.edelweb.fr/ 该时间戳服务器是一个实验性的服务器,提供免费的数字时间戳加盖服务.要申请时间戳,可以使用以下方法: 1)  如果是在 Windows 平台上,先编

iOS从零开始学习socket编程——高并发多线程服务器

在上一篇文章<iOS从零开始学习socket编程--HTTP1.0服务器端>中我们已经简单的接触了OC搭建的HTTP服务器. (地址http://blog.csdn.net/abc649395594/article/details/45131373) 出于用户体验和鲁棒性考虑,这里把这个HTTP服务器改进成多线程的. 首先,AnsycSocket这个类是基于OC的Runloop实现的,Runloop实现了方法的异步调用但并不支持多线程. 在这里首先简单区分一下多线程和方法异步调用的区别.他们都

【Android笔记】Android与服务器数据库通信的方法

1.Android平台下与服务器数据库通信的方法 在Android平台下,连接电脑服务器的MySQL.PostgreSQL.Oracle.Sybase.Microsoft SQLServer等数据库管理系统DBMS(database management system),主要有以下两种方法: 方法1.直接连接 在Android工程中引入JDBC驱动,直接连接.(本文主要介绍此种方法) 方法2.间接连接 在服务器上用PHP+DBMS做服务器端,PHP将DBMS中的数据用json或者xml进行封装.

linux编程实例--简单多进程服务器

主要利用fork事先创建若干个进程,并发处理多个客户端的连接,返回当前系统时间.具体代码如下: server.c # include <sys/types.h> # include <sys/socket.h> # include <netinet/in.h> # include <time.h> # include <string.h> # include <stdio.h> # include <signal.h> #

Android客户端与本地服务器Socket通信

Android客户端与本地服务器Socket通信 Socket服务器运行结果图?? 一.客户端和服务器端的选择: 客户端是我们手机端,关于服务器端,只要安装了JDK,自然就拥有通讯的功能,我们只需要在Eclipse或者MyEclipse中写好文章中服务器端的代码,运行起来即可,用accept()方法启动服务器端,等待客户端的连接,在未连接的情况下,服务器端处于堵塞的状态. 二.客户端注意事项 andriod客户端添加网络访问权限 <uses-permission android:name="