Delphi TXMLDocument 慎用 doNodeAutoIndent

有时候,我们用jQuery使用的jsonp的形式获取跨域数据,但其返回值是utf8的原始编码,如果直接渲染到页面的话会显示
uXXXXuYYYY
这样的字符而非中文。其实我们只需要做一个简单的转换既能解决此问题。

在使用TXMLDocument解析XML文档的时候,若希望保存之后的文档看起来漂亮点,在Options属性中增加doNodeAutoIndent即可。

  1 //利用readline函数解决粘包问题,每次读取一行。n换行
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<sys/socket.h>
  5 #include<string.h>
  6 #include<stdlib.h>
  7 #include<stdio.h>
  8 #include<errno.h>
  9 #include<netinet/in.h>
 10 #include<arpa/inet.h>
 11 #include<signal.h>
 12 #define ERR_EXIT(m)
 13     do
 14     {
 15         perror(m);
 16         exit(EXIT_FAILURE);
 17     }while(0)
 18 ssize_t readn(int fd,void *buf,size_t count)
 19 {
 20     size_t nleft=count;
 21     ssize_t nread;
 22     char *bufp=(char*)buf;
 23     while(nleft>0)
 24     {
 25         if((nread=read(fd,bufp,nleft))<0)
 26         {
 27             if(errno==EINTR)
 28                 continue;
 29             else
 30                 return -1;
 31         }
 32         else if(nread==0)
 33             return (count-nleft);
 34         bufp+=nread;
 35         nleft-=nread;
 36     }
 37     return count;
 38 }
 39 ssize_t writen(int fd, const void *buf, size_t count)
 40 {
 41     size_t nleft=count;
 42     ssize_t nwritten;
 43     char *bufp=(char*)buf;
 44     while(nleft>0)
 45     {
 46         if((nwritten=write(fd,bufp,nleft))<=0)
 47         {
 48             if(errno==EINTR)
 49                 continue;
 50             return -1;
 51         }else if(nwritten==0)
 52             continue;
 53         bufp+=nwritten;
 54         nleft-=nwritten;
 55     }
 56     return count;
 57 
 58 }
 59 ssize_t recv_peek(int sockfd,void *buf,size_t len)
 60 {
 61     while(1)
 62     {
 63         int ret=recv(sockfd,buf,len,MSG_PEEK);//从sockfd读取内容到buf,但不去清空sockfd,偷窥
 64         if(ret==-1&&errno==EINTR)
 65             continue;
 66         return ret;
 67     }
 68 }
 69 //偷窥方案实现readline避免一次读取一个字符
 70 ssize_t readline(int sockfd,void * buf,size_t maxline)
 71 {
 72     int ret;
 73     int nread;
 74     size_t nleft=maxline;
 75     char *bufp=(char*)buf;
 76     while(1)
 77     {
 78         ret=recv_peek(sockfd,bufp,nleft);//不清除sockfd,只是窥看
 79         if(ret<0)
 80             return ret;
 81         else if(ret==0)
 82             return ret;
 83         nread=ret;
 84         int i;
 85         for(i=0;i<nread;i++)
 86         {
 87             if(bufp[i]=='n')
 88             {
 89                 ret=readn(sockfd,bufp,i+1);//读出sockfd中的一行并且清空
 90                 if(ret!=i+1)
 91                     exit(EXIT_FAILURE);
 92                 return ret;
 93             }
 94         }
 95         if(nread>nleft)
 96             exit(EXIT_FAILURE);
 97         nleft-=nread;
 98         ret=readn(sockfd,bufp,nread);
 99         if(ret!=nread)
100             exit(EXIT_FAILURE);
101         bufp+=nread;//移动指针继续窥看
102     }
103     return -1;
104 }
105 int main(void)
106 {
107     int sock;//客户端创建套接字
108     if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
109         ERR_EXIT("socket error");
110     
111     struct sockaddr_in servaddr;//本地协议地址赋给一个套接字
112     memset(&servaddr,0,sizeof(servaddr));
113     servaddr.sin_family=AF_INET;
114     servaddr.sin_port=htons(5188);
115     
116     servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器段地址
117     //inet_aton("127.0.0.1",&servaddr.sin_addr);
118     
119     if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
120         ERR_EXIT("connect");
121 
122     //利用getsockname获取客户端本身地址和端口,即为对方accept中的对方套接口
123     struct sockaddr_in localaddr;
124     socklen_t addrlen=sizeof(localaddr);
125     if(getsockname(sock,(struct sockaddr *)&localaddr,&addrlen)<0)
126         ERR_EXIT("getsockname error");
127     printf("local IP=%s, local port=%dn",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port));    
128     //使用getpeername获取对方地址
129     
130     char sendbuf[1024]={0};
131     char recvbuf[1024]={0};    
132     while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)//默认有换行符
133     {
134         writen(sock,sendbuf,strlen(sendbuf));
135         int ret=readline(sock,recvbuf,1024);
136         if(ret==-1)
137             ERR_EXIT("readline");
138         else if(ret==0)
139         {
140             printf("service closedn");
141             break;
142         }
143         fputs(recvbuf,stdout);
144         memset(sendbuf,0,sizeof(sendbuf));
145         memset(recvbuf,0,sizeof(recvbuf));
146     }
147     close(sock);
148     return 0;
149 }
// 一个常见的jQuery ajax请求$.ajax(url, { 'dataType':'jsonp', // 这里总是会用get,无论是否设置 'contentType':'application/x-www-form-urlencoded; charset=UTF-8', // 如果编码不同可以指定下。考虑到某些语言强制utf-8编码或前后端统一则可以省略 'error':function(XHR, textStatus, errorThrown){ // 错误处理 }, 'complete':function(XHR, textStatus){ // 请求完成后处理在 error或success之后,必定执行 }, 'success':function(data, textStatus, jqXHR){ var o = eval(''); // 这里会利用eval函数解析返回字符串使其变成对象 console.log(JSON.stringift; // 再利用JSON对象将其解析成字符串 }});

但这样操作的文档,单纯读取和保存没有问题。可如果还会编辑,且编辑之后不保存直接调用其中元素的话,就需要注意了。

 

利用 eval('')
解析字符的话需要注意的是,这个可能会存在被攻击的风险。比如:解析的字符串是
alert('im hack') 时会有问题,因此不推荐直接引用未知源的数据。

因为格式化增加了换行和空格等字符,在修改后的节点再次读取节点数量时,ChildNodes.Count会将这些多余出来的字符,也解析进去,目前比较好的办法是通过NodeType进行判断之后,再确认那个节点是有效节点,导致代码工作量增加非常大,而且一个不小心,就是读错。

 

当然,如果是读取出数据之后,不直接使用TXMLDocument直接记录结果,而是使用其他控件记录,保存时再一次性写入的话,是不会有这个问题的。

/*
1、read,write 与 recv,send函数
ssize_t recv(int sockfd,void * buff,size_t len,int flags)
ssize_t send(int sockfd,const void *buff,size_t len,int flags)
flags为0或者为常值的或  MSG_OOB:发送或接收带外数据   MSG_PEEK:窥看外来消息

2、readline函数实现
3、用readline实现回射客户/服务器
    利用readline解决粘包问题

4、getsockname函数、getpeername函数
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

*/
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define ERR_EXIT(m)
    do
    {
        perror(m);
        exit(EXIT_FAILURE);
    }while(0)
ssize_t readn(int fd,void *buf,size_t count)
{
    size_t nleft=count;
    ssize_t nread;
    char *bufp=(char*)buf;
    while(nleft>0)
    {
        if((nread=read(fd,bufp,nleft))<0)
        {
            if(errno==EINTR)
                continue;
            else
                return -1;
        }
        else if(nread==0)
            return (count-nleft);
        bufp+=nread;
        nleft-=nread;
    }
    return count;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
    size_t nleft=count;
    ssize_t nwritten;
    char *bufp=(char*)buf;
    while(nleft>0)
    {
        if((nwritten=write(fd,bufp,nleft))<=0)
        {
            if(errno==EINTR)
                continue;
            return -1;
        }else if(nwritten==0)
            continue;
        bufp+=nwritten;
        nleft-=nwritten;
    }
    return count;

}
ssize_t recv_peek(int sockfd,void *buf,size_t len)
{
    while(1)
    {
    //recv只能用于套接口IO。通过flags可以指定接收选项。MSG_PEEK接收缓冲区数据但不清除sockfd数据。read会清除。
        int ret=recv(sockfd,buf,len,MSG_PEEK);//从sockfd读取内容到buf,但不去清空sockfd,偷窥
        if(ret==-1&&errno==EINTR)
            continue;
        return ret;
    }
}
//偷窥方案实现readline避免一次读取一个字符.maxline 一行最多的字节数。
ssize_t readline(int sockfd,void * buf,size_t maxline)
{
    int ret;
    int nread;
    size_t nleft=maxline;//不能超过maxline
    char *bufp=(char*)buf;
    while(1)
    {
        ret=recv_peek(sockfd,bufp,nleft);//不清除sockfd,只是窥看。接下来可以去读sockfd内容。
        if(ret<0)
            return ret;//失败
        else if(ret==0)
            return ret;//对方关闭套接口
        nread=ret;
        int i;
        //判断缓冲区中是否有n
        for(i=0;i<nread;i++)
        {
            if(bufp[i]=='n')
            {
                ret=readn(sockfd,bufp,i+1);//读出sockfd中的一行并且清空sockfd这一行
                if(ret!=i+1)
                    exit(EXIT_FAILURE);
                return ret;
            }
        }
        //查看数据的时候缓冲区没有n.还不满一条消息,先将其读出来
        if(nread>nleft)
            exit(EXIT_FAILURE);//字节数大于nleft(maxline)
        nleft-=nread;//这行剩余最多的字节数
        ret=readn(sockfd,bufp,nread);//这些字符先从sockfd读走(还没有遇到n)
        if(ret!=nread)
            exit(EXIT_FAILURE);//readn出错
        bufp+=nread;//移动recv_peek缓冲区的指针,继续窥看。
    }
    return -1;
}
void do_service(int conn)
{
        int ret;
        char recvbuf[1024];
        while(1)
        {
            memset(&recvbuf,0,sizeof(recvbuf));
            //使用readn之后客户端发送的数据不足1024会阻塞
            //在客户端程序中确定消息的边界,发送定长包
            ret=readline(conn,recvbuf,1024);                                                               
            //客户端关闭
            if(ret==-1)
                ERR_EXIT("readline");            
            else if(ret==0)
            {
                printf("client closen");
                break;//不用继续循环等待客户端数据
            }
            fputs(recvbuf,stdout);
            writen(conn,recvbuf,strlen(recvbuf));
        }
}
int main(void)
{
    int listenfd;
    if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
        ERR_EXIT("socket error");
    //if((listenfd=socket(PF_INET,SOCK_STREAM,0))<0)


    //本地协议地址赋给一个套接字
    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(5188);
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//表示本机地址
    //servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    //inet_aton("127.0.0.1",&servaddr.sin_addr);

    //开启地址重复使用,关闭服务器再打开不用等待TIME_WAIT
    int on=1;
    if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
        ERR_EXIT("setsockopt error");
    //绑定本地套接字
    if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
        ERR_EXIT("bind error");
    if(listen(listenfd,SOMAXCONN)<0)//设置监听套接字(被动套接字)
        ERR_EXIT("listen error");

    struct sockaddr_in peeraddr;//对方套接字地址
    socklen_t peerlen=sizeof(peeraddr);
    int conn;//已连接套接字(主动套接字)
    pid_t pid;
    while(1){
        if((conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0)
            ERR_EXIT("accept error");
        //连接好之后就构成连接,端口是客户端的。peeraddr是对端
        printf("ip=%s port=%dn",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
        pid=fork();
        if(pid==-1)
            ERR_EXIT("fork");
        if(pid==0){    
                close(listenfd);
                do_service(conn);
                //某个客户端关闭,结束该子进程,否则子进程也去接受连接
                exit(EXIT_SUCCESS);
        }else     close(conn);
    }
    return 0;
}
You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图