随笔 - 23  文章 - 0 评论 - 68 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

广告中国绩效网,注册立刻送10元 广告中国绩效网,注册立刻送10元

常用链接

留言簿(7)

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

看了很多的资料,决定自己写个程序练习练习,于是就自己写了个小程序来练练手:
Server端(linux环境下):
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
 #include <arpa/inet.h>
 #include <fcntl.h>


#define PORT    3456
#define BACKLOG   10
#define MAX_EPOLL_FD 65535

int CreateTcpListenSocket();
int InitEpollFd();
void UseConnectFd(int sockfd);
void setnonblocking(int sock);

static int listenfd;

int main()
{
 int epoll_fd;
 int nfds;
 int i;
 
 struct epoll_event events[50];
 struct epoll_event tempEvent;

 int sockConnect;
 struct sockaddr_in remoteAddr;
 int addrLen;

 addrLen = sizeof(struct sockaddr_in);
 epoll_fd = InitEpollFd();
 if (epoll_fd == -1)
 {
  perror("init epoll fd error.");
  exit(1);
 }

 printf("begin in loop.\n");
 while (1)
  {
  nfds = epoll_wait(epoll_fd, events, 50, 1000);
  //sleep(3);
  printf("connect num: %d\n", nfds);
  if (nfds == -1)
   {
   perror("epoll_wait error.");
   }
  for (i = 0; i < nfds; i++)
   {
   if (listenfd == events[i].data.fd)
    {
    printf("connected success\n");
    sockConnect = accept(events[i].data.fd, (struct sockaddr*)&remoteAddr, &addrLen);
    if (sockConnect == -1)
     {
     perror("accept error.");
     continue;
     }
    setnonblocking(sockConnect);
    tempEvent.events = EPOLLIN | EPOLLET;
    tempEvent.data.fd = sockConnect;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockConnect, &tempEvent) < 0)
     {
     perror("epoll ctl error.");
     return -1;
     }
    }
   else
    {
    UseConnectFd(events[i].data.fd);
    }
   }
  }
 
}

int CreateTcpListenSocket()
{
 int sockfd;
 struct sockaddr_in localAddr;
 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
 {
  perror("create socket fail");
  return -1;
 }

 setnonblocking(sockfd);
 
 bzero(&localAddr, sizeof(localAddr));
 localAddr.sin_family = AF_INET;
 localAddr.sin_port = htons(PORT);
 localAddr.sin_addr.s_addr = htonl(INADDR_ANY);

 if (bind(sockfd,  (struct sockaddr*)&localAddr, sizeof(struct sockaddr)) == -1)
 {
  perror("bind error");
  return -1;
 }

 if (listen(sockfd, BACKLOG) == -1)
 {
  perror("listen error");
  return -1;
 }

 return sockfd;
}

int InitEpollFd()
{
 //epoll descriptor
 int s_epfd;
 struct epoll_event ev;
 

 listenfd = CreateTcpListenSocket();

 if (listenfd == -1)
 {
  perror("create tcp listen socket error");
  return -1;
 }

 s_epfd = epoll_create(MAX_EPOLL_FD);
 ev.events = EPOLLIN;
 ev.data.fd = listenfd;
 if (epoll_ctl(s_epfd, EPOLL_CTL_ADD, listenfd, &ev) < 0)
 {
  perror("epoll ctl error");
  return -1;
 }

 return s_epfd;
}

void UseConnectFd(int sockfd)
{
 char recvBuff[1500];
 int recvNum = 0;

 recvNum = recv(sockfd, recvBuff, 1500, 0);
 if (recvNum == -1)
  {
  perror("recv error.");
  return;
  }

 recvBuff[recvNum] = '\0';
 printf("message: %s \n", recvBuff);
}

void setnonblocking(int sock)

{

     int opts;

     opts=fcntl(sock,F_GETFL);

     if(opts<0)

     {

          perror("fcntl(sock,GETFL)");

          exit(1);

     }

     opts = opts|O_NONBLOCK;

     if(fcntl(sock,F_SETFL,opts)<0)

     {

          perror("fcntl(sock,SETFL,opts)");

          exit(1);

     }   

}

client端(VC6.0):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
//#include <sockets.h>
//#include <unistd.h>
//#include <sys/types.h>
//#include <sys/socket.h>
//#include <arpa/inet.h>
#pragma comment(lib,"wsock32.lib")
#define PORT    3456

int main()
{
 int sockfd;
 struct sockaddr_in remoteAddr;
 char mesg[] = "This is a client for epoll test";
 WSADATA wsdata;

 memset(&remoteAddr, 0, sizeof(remoteAddr));
 remoteAddr.sin_family = AF_INET;
 remoteAddr.sin_port = htons(PORT);
 remoteAddr.sin_addr.s_addr = inet_addr("10.40.8.15");

 
 WSAStartup(0x0202,&wsdata);

 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  {
  perror("create socket fail.");
  return -1;
  }
 WSAAsyncSelect(sockfd, NULL, NULL, 0);
 printf("server ip is: %s\n", (char *)inet_ntoa(remoteAddr.sin_addr));

// while (1)
// {
  if (connect(sockfd, (struct sockaddr*)&remoteAddr, sizeof(remoteAddr)) == -1)
  {
   //perror("connect error.");
   printf("Error code: %d\n", WSAGetLastError());
   return -1;
  }
  //printf("times\n");
// }

 printf("connected!\n");
 while (1)
  {
  
  send(sockfd, mesg, strlen(mesg), 0);
  printf("send over\n");
  Sleep(1000);
  }
}

posted @ 2008-02-19 18:17 吴剑 阅读(988) | 评论 (2)编辑 收藏

      (申明:此文章属于原创,若转载请表明作者和原处链接 )      
      /*      author:   wu.jian   (吴剑)      English name: Sword
      /*      date:      2007-12-13
      /*      purpose:   知识共享

      这几天工作上碰到了UTF-8转GB2312的问题,而且是在嵌入式的环境下,没有API可用,查了很多网上的资料,大多调用VC或者linux下自带的接口。在这里我将这两天的工作做个总结。
      总的来说分为两大步(这里就不介绍基础知识了):

      一、UTF8 -> Unicode
      由于UTF8和Unicode存在着联系,所以不需要任何库就可以直接进行转换。首先要看懂UTF8的编码格式:
      U-00000000 - U-0000007F: 0xxxxxxx  
      U-00000080 - U-000007FF: 110xxxxx 10xxxxxx  
      U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx  
      U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx  
      U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  
      U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  
      前面几个1就代表后面几个字节是属于一起的。如果要解析一长串UTF8格式的字符串,这点就很有用了。下面这个函数就是判断前面几个1的(这里有define APP_PRINT printf,这样当release的时候将这个宏定义为空就行了,不需要一个一个去改,又方便重新调试):
      int GetUtf8ByteNumForWord(u8 firstCh)
      {
          u8 temp = 0x80;
          int num = 0;
 
          while (temp & firstCh)
          {
                num++;
                 temp = (temp >> 1);
           }

       APP_PRINT("the num is: %d", num);
        return num;
      }
      利用这个函数可以得到字符串中那几个字节是一起的。因为UTF8最大只有6个字节,所以就根据返回值来处理这里我只处理了3个字节和1个字节的UTF8的编码,因为一般来说中文在UTF8中是3个字节。

//将len个字节的UTF8格式的转换成GB2312格式存放在temp预先申请好的缓冲区中
void Utf8ToGb2312(const char* utf8, int len, char *temp)
{
       APP_PRINT("utf8->unicode: \n");
       APP_PRINT("utf8: [");
       for (int k = 0; k < len; k++)
       {
              APP_PRINT("%02x ", utf8[k]);
       }
       APP_PRINT("]\n");
 
       int byteCount = 0;
       int i = 0;
       int j = 0;

       u16 unicodeKey = 0;
       u16 gbKey = 0;


      //循环解析
       while (i < len)
       {   
        switch(GetUtf8ByteNumForWord((u8)utf8[i]))
        {
          case 0:
                temp[j] = utf8[i];
                byteCount = 1;
          break;

          case 2:
          temp[j] = utf8[i];
          temp[j + 1] = utf8[i + 1];
          byteCount = 2;
          break;

         case 3:
                 //这里就开始进行UTF8->Unicode
                 temp[j + 1] = ((utf8[i] & 0x0F) << 4) | ((utf8[i + 1] >> 2) & 0x0F);
                 temp[j] = ((utf8[i + 1] & 0x03) << 6) + (utf8[i + 2] & 0x3F);

                //取得Unicode的值
                 memcpy(&unicodeKey, (temp + j), 2);
                 APP_PRINT("unicode key is: 0x%04X\n", unicodeKey);

                  //根据这个值查表取得对应的GB2312的值
                gbKey = SearchCodeTable(unicodeKey);
                APP_PRINT("gb2312 key is: 0x%04X\n", gbKey);
    
                if (gbKey != 0)
                {
                       //here change the byte
                        //不为0表示搜索到,将高低两个字节调换调成我要的形式
                       gbKey = (gbKey >> 8) | (gbKey << 8);
                       APP_PRINT("after changing, gb2312 key is: 0x%04X\n", gbKey);
                       memcpy((temp + j), &gbKey, 2);
                  }

                byteCount = 3;
          break;

          case 4:
          byteCount = 4;
          break;
         case 5:
          byteCount = 5;
          break;
         case 6:
          byteCount = 6;
          break;
    
         default:
          APP_PRINT("the len is more than 6\n");
          break;    
        }

        i += byteCount;
        if (byteCount == 1)
        {
               j++;
        }
        else
        {
               j += 2;
        }
  
       }
       APP_PRINT("utf8: [");
       for (k = 0; k < j; k++)
       {
              APP_PRINT("%02x ", temp[k]);
       }
       APP_PRINT("]\n");
}

      二、下面主要谈谈利用查表法来进行Unicode->GB2312的转换,首先下载码表,一般码表都是将GB2312的放在前面,Unicode放在后面,这样对于我们来说不方便使用,所以我转换了下,将Unicode放在前面,而且按照从小到大排好序。(这里只需要考虑都为两个字节的情况,因为前面的UTF8->Unicode并没有将单字节的ASCII转换成Unicode)
            (1)做表:(可以到这里下载:http://blog.91bs.com/?action=show&id=20,这里谢谢渣渣的猪窝)
            这个是原来的样子:
            0x8140 0x4E02 #CJK UNIFIED IDEOGRAPH
            0x8141 0x4E04 #CJK UNIFIED IDEOGRAPH
            0x8142 0x4E05 #CJK UNIFIED IDEOGRAPH
            先弄成(这个可以写个小程序来做,我就是在VC上做的,如果需要可以联系我):
            { 0x4E02 ,0x8140 }, //CJK UNIFIED IDEOGRAPH
            { 0x4E04 ,0x8141 }, //CJK UNIFIED IDEOGRAPH
            { 0x4E05 ,0x8142 }, //CJK UNIFIED IDEOGRAPH
            这样就可以把这些放在.h文件中了,下面是我的定义:
            typedef struct unicode_gb
            {
                   unsigned short unicode;
                   unsigned short gb;
            } UNICODE_GB;

            UNICODE_GB code_table[] = 
            {
                  { 0x4E02, 0x8140 },   //CJK UNIFIED IDEOGRAPH
                  { 0x4E04, 0x8141 },  //CJK UNIFIED IDEOGRAPH
                  { 0x4E05, 0x8142 },  //CJK UNIFIED IDEOGRAPH
                  。。。。。。省略

            下面这一步也很简单,在VC中用冒泡排序法,对这个数组按照unicode值进行排序,如果需要可以联系我,把最终结果打印出来,在cmd下运行name > 1.txt就输出到文件,这样就有了一个按照unicode排好序的unicode->gb2312码表。

   int main(int argc, char *argv[])
{

    int num = 0;
    UNICODE_GB temp;
    int i = 0;
    int j = 0;

    num = sizeof(code_table) / sizeof(UNICODE_GB);

    printf("struct size: %d | total size: %d | num is: %d \n", 
    sizeof(UNICODE_GB), sizeof(code_table), num);

    for (i = 0; i < num; i++)
    {
        for (j = 1; j < num - i; j++)
        {
            if (code_table[j - 1].unicode > code_table[j].unicode)
            {
                temp.unicode = code_table[j - 1].unicode;
                temp.gb = code_table[j - 1].gb;
                code_table[j - 1].unicode = code_table[j].unicode;
                code_table[j - 1].gb = code_table[j].gb;
                code_table[j].unicode = temp.unicode;
                code_table[j].gb = temp.gb;
            }
        }
    }

    printf("here is the code table sorted by unicode\n\n");

    for (i = 0; i < num; i++)
    {
        printf("{\t0x%04X,\t0x%04X\t},\t\n", code_table[i].unicode, code_table[i].gb);
     }

       printf("\n\n print over!\n");

   //以下注释掉的其实就是我用来对原来的码表添加,{,}等用的
   /*
    char buff[100];
    char buff_1[100]; 
 
    FILE* fp = NULL;
    FILE *fp_1 = NULL;

    memset(buff, 0, 100);
    memset(buff_1, 0, 100);
 
    fp = fopen("table.txt", "rw");
    fp_1 = fopen("table_1.txt", "a+");

    if ((fp == NULL) || (fp_1 == NULL))
    {
        printf("open file error!\n");
        return 1;
    }

    while (fgets(buff, 100, fp) != NULL)
    {
        buff[8] = ',';

        fputs(buff, fp_1);
    }
 */

    return 0;
}

      最后就是搜索算法了,前面已经排好序了,现在我们把排好序的码表放在我们真正需要的.h文件中。大家应该猜我用什么算法搜索了吧,二分法。

#define CODE_TABLE_SIZE 21791
//这个表是死的,所以就直接用宏表示长度,不用每次都用size,不过这样可能对移植性不好。
u16 SearchCodeTable(u16 unicodeKey)
{
    int first = 0;
    int end = CODE_TABLE_SIZE - 1;
    int mid = 0;

    while (first <= end)
    {
        mid = (first + end) / 2;

        if (code_table[mid].unicode == unicodeKey)
        {
            return code_table[mid].gb;
        }
        else if (code_table[mid].unicode > unicodeKey)
        {
            end = mid - 1;
        }
        else 
        {
            first = mid + 1;
        }
    }
    return 0;
}
      到此,已经能够将UTF8串转换成GB2312了。是一长串哦,而不是单个汉字的编码转换。


posted @ 2007-12-13 19:21 吴剑 阅读(26106) | 评论 (63)编辑 收藏

#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>

#include <netdb.h>
#include <netinet/in.h>

#include "ATA_Update.h"

#define MY_SOCK_PORT 2468
int socketId;
BOOL isSetServerAddr;
struct sockaddr sa;


int main(int argc, char *argv[])
{
 
 isSetServerAddr = FALSE;
 

 socketId = socket(PF_PACKET, SOCK_PACKET, htons(0x0003));
 if (socketId < 0)
 {
  perror ("can not get SOCK_PACKET socket");
  return 0;
 }

 //struct sockaddr sa;
 memset(&sa, 0, sizeof(struct sockaddr));
 sa.sa_family = PF_PACKET;
 strcpy(sa.sa_data, "eth0");

 if (bind(socketId, &sa, sizeof(sa)) < 0)
  {
  perror("bind ");
  return 0;
  }
 
// int value = 1;
// setsockopt(socketId, SOL_SOCKET, SO_BROADCAST, &value, sizeof(value));

 printf("send data test...\n");

 while (1)
  {
   
   SendData(CMD_BROADCAST, NO_NEED_ACK);
   sleep(5);
  }
/*
 printf("send broad cast pkg...\n");
 BlockWaitForUpdate();
 printf("get the server addr, after BlockWaitForUpdate\n");

 printf("begin to recv data......\n");
 RecvData(socketId);
 printf("recv data over.........\n");
*/
 return 0;
 
}

BOOL SendData(UINT8 cmdType, UINT8 ackType)
{
 int i = 0;
 int writeNum = 0;
 printf("the cmd is: %d || ack type is : %d \n", cmdType, ackType);
 UINT8 sendBuffer[100];
 memset(sendBuffer, 0 , 100);
 for (i = 0; i < 100; i++)
  {
  sendBuffer[i] = 16;
  }

 //00-16-76-A8-6E-AA
 sendBuffer[0] = 0x00;
 sendBuffer[1] = 0x16;
 sendBuffer[2] = 0x76;
 sendBuffer[3] = 0xA8;
 sendBuffer[4] = 0x6E;
 sendBuffer[5] = 0xAA;


 //50:78:4C:70:92:EF
 sendBuffer[6] = 0x50;
 sendBuffer[7] = 0x78;
 sendBuffer[8] = 0x4c;
 sendBuffer[9]= 0x70;
 sendBuffer[10] = 0x92;
 sendBuffer[11] = 0xEF;
/*

 for (i = 6; i < 12; i++)
  {
   sendBuffer[i] = 0x00;
  }
  */
 sendBuffer[12] = 0x00;
 sendBuffer[13] = 0x00;

 sendBuffer[14] = cmdType;
 sendBuffer[15] = ackType;

 memcpy(sendBuffer + 14, "ATA_UPDATE", 10);

 for (i = 0; i < 100; i++)
  {
  printf(" %02x", sendBuffer[i]);
  }

 writeNum = sendto(socketId, sendBuffer, 100, 0, &sa, sizeof sa);
 printf("    ==    write %d \n", writeNum);
 if (writeNum == -1)
  perror("sendto");
 
 return TRUE;
}

/*
BOOL BlockWaitForUpdate()
{
 fd_set readFd;
 struct timeval tv;
 int ret;
 
 SendData(CMD_BROADCAST, NO_NEED_ACK);
 
 while(1)
 {
  tv.tv_sec = 5;
  tv.tv_usec = 0;
  
  FD_ZERO(&readFd);
  FD_SET(socketId, &readFd);

  ret = select(socketId + 1, &readFd, NULL, NULL, &tv);

  switch(ret)
  {
   case -1:

    break;

   case 0:
    printf("resend data\n");
    SendData(CMD_BROADCAST, NO_NEED_ACK);
    break;

   default:
    if (FD_ISSET(socketId, &readFd))
    {
     printf("here come pkg\n");
     if (isRightPkg(socketId, CMD_UPDATE))
     {
      return TRUE;
     }
    }
  }
 }
}

//这里还可以进行一些字符内容的设定,增加安全性
BOOL isRightData(char *recvBuff, UINT8 cmdType)
{
 if (recvBuff == NULL)
  return FALSE;

 if (recvBuff[0] == cmdType)
  return TRUE;

 return FALSE;
}

BOOL isRightPkg(int sockfd, UINT8 cmdType)
{
 int recvNum;
 char recvBuff[1024];
 struct sockaddr_in addr;

 int addrLen = sizeof(struct sockaddr_in);
 memset(&addr, 0, sizeof(addr));
 recvNum = recvfrom(sockfd, recvBuff, sizeof(recvBuff),
  0, (struct sockaddr*)&addr, &addrLen);

 if (recvNum < 0)
 {
  return FALSE;
 }

 recvBuff[recvNum] = 0;
 printf("receive from [%s] : %s\n", inet_ntoa(addr.sin_addr), recvBuff);

 if (!isSetServerAddr)
 {
  if ((cmdType == CMD_UPDATE)
   &&( isRightData(recvBuff, CMD_UPDATE)))
  {
   isSetServerAddr = TRUE;
   serverAddress = addr;
   printf("address is set: %s\n", inet_ntoa(serverAddress.sin_addr));

   SendData(CMD_ACK, ACK_FOR_UPDATE);

   printf("\n\n---after here begin to data transfer........\n");
   return TRUE;
  }
  else
   return FALSE;
 }
 else
 {
  if ((addr.sin_addr.s_addr == serverAddress.sin_addr.s_addr)
   && (isRightData(recvBuff, cmdType)))
  {
   return TRUE;
  }
 }

 return FALSE;
}

void RecvData(int sockfd)
{
 int recvNum;
 char recvBuff[1024];
 struct sockaddr_in addr;

 int addrLen = sizeof(struct sockaddr_in);
 memset(&addr, 0, sizeof(addr));

 while (1)
 {
  recvNum = recvfrom(sockfd, recvBuff, sizeof(recvBuff),
   0, (struct sockaddr*)&addr, &addrLen);

  if (recvNum < 0)
  {
   printf("error with recv data\n");
   continue;
  }

  recvBuff[recvNum] = 0;
  PrintPkgAndAddr(PKG_RECV, addr, recvBuff, recvNum);

  //here process the pkg
  //PutDataInPkg();
  //CheckRecvPkg();

  //send ack back
  if (recvBuff[0] == CMD_DATA)
  {
   SendData(CMD_ACK, ACK_FOR_DATA);
  }
  else
  {
  }
  
  
 }
 
}

void PrintPkgAndAddr(PKG_TYPE type, struct sockaddr_in addr, char *buff, int len)
{
 int i;
 
 if (type == PKG_SEND)
 {
  printf("\nsend [%d bytes] to [%s] : ", len, inet_ntoa(addr.sin_addr));
 }
 else if (type == PKG_RECV)
 {
  printf("\nrecv [%d bytes] from [%s] : ", len, inet_ntoa(addr.sin_addr));
 }

 for (i = 0; i < len; i++)
 {
  printf(" %02x ", (unsigned char)buff[i]);
 }

 printf("\n");
 
}

void PutDataInPkg(char *buff)
{
}

BOOL CheckRecvPkg(APP_PKG *pkg)
{
}
*/

posted @ 2007-11-26 19:18 吴剑 阅读(253) | 评论 (0)编辑 收藏
预编译一般用来防止头文件的重复包含和编译。

在我们用C做开发的是候,有时候项目很大,我们所编写的程序会很长。这样我们如果还是写在一个文件中会出现管理上的问题和 
查看上的不方便。因此,我们可以分多个文件编写我们的程序,这样把一个功能的程序写到一个文件里,便于查看 也有助于我们管理。 
如main.c sd.h sd.c lcd.h lcd.c fat.h fat.c delay.h delay.c 

    至于头文件(sd.h)和源文件(sd.c)的写法应该是这样的: 
     在头文件中 我们写的是 函数的声明,和一些使用到的变量的声明; 
     在源文件中 我们写的是 对应于头文件中的函数的具体实现,即函数左大括号和右大括号中的内容。 

    在具体使用相关函数是可以使用#include 命令来包含其函数所在的头文件。下面我们虚拟如下的一个程序结构来展开说明: 

如在main.c中 
#include delay.h 
#include sd.h 
#include lcd.h 
#include fat.h 
main() 

  sd_init(); 
  delay_nms(10); 
  lcd_init(); 
  while(1) 
  { 
    .... 
    ....; 
  } 


在sd.c中 
void sd_init() 

 ...... 
 ......;  


在sd.h中 
#include delay.h 
#include ....... 
void sd_init(); 
..... 

在delay.c中 
void delay_nms(UINT n) 

 for(....) 
  {... 
   _asm_("nop"); 
   ... 
  }  


在delay.h中 
void delay_nms(UINT n); 

..... 

   我们看,如果在头文件的书写中都没加预编译指令 
#ifndef XXXXXXXX  
#define XXXXXXXX 
  .... 
  .... 
  (头文件内容) 
  .... 
#endif     


会出现怎么样的情况????    其实这样,编译会报错。编译器会提示 void delay_nms()定义了两次。为什么会出现这种情况呢? 
    我们顺着程序读一下,当我们在main.c中,编译器读到#include delay.h时会把 delay.h文件中的内容包含进来,这样delay.h中的 
delay_nms()函数定义了一次,当我们在往下,读到#include sd.h时,把sd.h包含进来,在把sd.h的内容展开,我们又读到了一次delay.h 
这样又把delay.h的内容包含近了main.c 。因此,我们发现delay.h被main.c重复包含了2次。这样使得void delay_nms()函数也被定义了二次。 

    如何解决重复定义的问题,我们是用条件编译,#ifndef ..等。在头文件中加入条件编译指令,如下: 

在sd.h中 
#ifndef _SD_H_  
#define _SD_H_ 

#include delay.h 
#include ....... 
void sd_init(); 
..... 

#endif 

在delay.h中 
#ifndef _DELAY_H_  
#define _DELAY_H_ 

void delay_nms(UINT n); 

..... 
#endif 

   这样,让我们再看下会出现什么情况。 
   当我们在main.c中,编译器读到#include delay.h时会把 delay.h文件中的内容包含进来,(#ifndef _DELAY_H_)我们先判断有没有定义 
了 _DELAY_H_  这个宏,由于这是第一次调用delay.h,_DELAY_H_这个宏还没定义,这样编译器会顺序向下执行,执行到#define _DELAY_H_ 
则定义了一个_DELAY_H_宏,宏值为NULL. 当我们main.c中再往下,读到#include sd.h时,把sd.h包含进来,在把sd.h的内容展开, 
我们又读到了一次delay.h。 这一次 我们判断 #ifndef _DELAY_H_ 结果是已经定义了,这样,编译器会跳过下面的内容,直接到 #endif。 
可见,我们的函数 void delay_nms(UINT n); 就不会被重复定义二次! 对于其他的头文件,我们现在也加上 预编译指令,防止被重复包含。 

  另外,在WinAVR中,如果使用多文件编译,需要在Makeflie中修改 SRC = $(TARGET).c 在其后面添加你的头文件的源文件的名字。如 
  SRC = $(TARGET).c delay.c sd.c fat.c lcd.h 




希望以上内容能对初学者有所帮助,有什么不对的地方也恳请大家指出修正。
posted @ 2007-11-11 22:20 吴剑 阅读(1562) | 评论 (0)编辑 收藏
_init关键字不是gcc的, 而是linux内核的。  __init, __initdata等属性标志, 是要把这种属性的代码放入目标文件的.init.text节, 数据放入.init.data节──这一过程是通过编译内核时为相关目标平台提供了xxx.lds链接脚本, 来指导ld完成的。 对i386来说, 可以参考arch/i386/kernel/vmlinux.lds.S文件。

对编译成module的代码和数据来说, 当模块加载时, __init属性的函数就被执行;
对静态编入内核的代码和数据来说, 当内核引导时, do_basic_setup()函数调用do_initcalls()函数, 后者负责所有.init节函数的执行。

__init说明这个函数仅在初始化的时候使用,在模块挂载以后,就会把初始化函数扔掉,可以把该函数占用的内存扔掉,

posted @ 2007-11-06 18:36 吴剑 阅读(460) | 评论 (0)编辑 收藏
// File: prg6_9.c
    #include <stdio.h>          /* These are the usual header files */
    #include <strings.h>          /* for bzero() */
    #include <unistd.h>         /* for close() */
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/time.h>
    #include <stdlib.h>

    #define PORT 1234   /* Port that will be opened */
    #define BACKLOG 5   /* Number of allowed connections */
    #define MAXDATASIZE 1000
    typedef struct CLIENT{
       int     fd;
       char*  name;
       struct sockaddr_in addr; /* client's address information */
       char* data;                        
    };  
    void process_cli(CLIENT *client, char* recvbuf, int len);
    void savedata(char* recvbuf, int len, char* data);

    main()
    {
    int    i, maxi, maxfd,sockfd;
    int    nready;
    ssize_t    n;
    fd_set    rset, allset;
    int listenfd, connectfd; /* socket descriptors */     
    struct sockaddr_in server; /* server's address information */
    /* client's information */
    CLIENT client[FD_SETSIZE];
    char recvbuf[MAXDATASIZE];
    int sin_size;

    /* Create TCP socket  */
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
       /* handle exception */
       perror("Creating socket failed.");
       exit(1);
       }

    int opt = SO_REUSEADDR;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    bzero(&server,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_port=htons(PORT);
    server.sin_addr.s_addr = htonl (INADDR_ANY);
    if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) {
       /* handle exception */
       perror("Bind error.");
       exit(1);
       }    

    if(listen(listenfd,BACKLOG) == -1){  /* calls listen() */
       perror("listen() error\n");
       exit(1);
       }

    sin_size=sizeof(struct sockaddr_in);
    /*initialize for select */
    maxfd = listenfd;    
    maxi = -1;            
    for (i = 0; i < FD_SETSIZE; i++) {
       client[i].fd = -1;    
       }
    FD_ZERO(&allset);
    FD_SET(listenfd, &allset);

    while(1)
    {
    struct sockaddr_in addr;
    rset = allset;        
    nready = select(maxfd+1, &rset, NULL, NULL, NULL);

    if (FD_ISSET(listenfd, &rset)) {    /* new client connection */
       /* Accept connection */
       if ((connectfd = accept(listenfd,(struct sockaddr *)&addr,&sin_size))==-1) {
          perror("accept() error\n");
          continue;
          }
       /* Put new fd to client */
       for (i = 0; i < FD_SETSIZE; i++)
          if (client[i].fd < 0) {
             client[i].fd = connectfd;    /* save descriptor */
             client[i].name = new char[MAXDATASIZE];
             client[i].addr = addr;
             client[i].data = new char[MAXDATASIZE];
             client[i].name[0] = '\0';
             client[i].data[0] = '\0';
             printf("You got a connection from %s.  ",inet_ntoa(client[i].addr.sin_addr) );
             break;
             }
          if (i == FD_SETSIZE)        printf("too many clients\n");
          FD_SET(connectfd, &allset);    /* add new descriptor to set */
          if (connectfd > maxfd)  maxfd = connectfd;    
          if (i > maxi)    maxi = i;        
          if (--nready <= 0) continue;    /* no more readable descriptors */
          }

       for (i = 0; i <= maxi; i++) {    /* check all clients for data */
          if ( (sockfd = client[i].fd) < 0)    continue;
          if (FD_ISSET(sockfd, &rset)) {
             if ( (n = recv(sockfd, recvbuf, MAXDATASIZE,0)) == 0) {
                /*connection closed by client */
                close(sockfd);
                printf("Client( %s ) closed connection. User's data: %s\n",client[i].name,client[i].data);
                FD_CLR(sockfd, &allset);
                client[i].fd = -1;
                delete client[i].name;
                delete client[i].data;
                } else
                process_cli(&client[i], recvbuf, n);
             if (--nready <= 0)    break;    /* no more readable descriptors */
             }
       }
    }
    close(listenfd);   /* close listenfd */         
    }

    void process_cli(CLIENT *client, char* recvbuf, int len)
    {
    char sendbuf[MAXDATASIZE];

    recvbuf[len-1] = '\0';
    if (strlen(client->name) == 0) {
       /* Got client's name from client */
       memcpy(client->name,recvbuf, len);
       printf("Client's name is %s.\n",client->name);
       return;
       }

    /* save client's data */
    printf("Received client( %s ) message: %s\n",client->name, recvbuf);
    /* save user's data */
    savedata(recvbuf,len, client->data);
    /* reverse usr's data */
    for (int i1 = 0; i1 < len - 1; i1++) {
       sendbuf[i1] = recvbuf[len - i1 -2];
    }
    sendbuf[len - 1] = '\0';

    send(client->fd,sendbuf,strlen(sendbuf),0);
    }

    void savedata(char* recvbuf, int len, char* data)
    {
    int start = strlen(data);
    for (int i = 0; i < len; i++) {
       data[start + i] = recvbuf[i];
    }         
    }



posted @ 2007-11-05 22:10 吴剑 阅读(504) | 评论 (0)编辑 收藏

1、概述

1) 链路层,有时也称作数据链路层或网络接口层,(硬件接口,ARP,RARP)

2) 网络层,有时也称作互联网层,(IP, ICMP, IGMP)I C M PI P协议的附属协议,I P层用它来与其他主机或路由器交换错误报文和其他重要信息。P i n gTr a c e r o u t e它们都使用了I C M P

3) 运输层主要为两台主机上的应用程序提供端到端的通信。(TCP, UDP)

4) 应用层负责处理特定的应用程序细节。

 

一个互连网就是一组通过相同协议族互连在一起的网络。

 

构造互连网最简单的方法是把两个或多个网络通过路由器进行连接。它是一种特殊的用

于网络互连的硬件盒。路由器的好处是为不同类型的物理网络提供连接:以太网、令牌环网、

点对点的链接和F D D I(光纤分布式数据接口)等等。

 

连接网络的另一个途径是使用网桥。网桥是在链路层上对网络进行互连,而路由器则是

在网络层上对网络进行互连。网桥使得多个局域网( L A N)组合在一起,这样对上层来说就好像是一个局域网。

有三类I P地址:单播地址(目的为单个主机)、广播地址(目的端为给定网络上的所有主

机)以及多播地址(目的端为同一组内的所有主机)。在T C P / I P领域中,域名系统(D N S)是一个分布的数据库,由它来提供 I P地址和主机名之间的映射信息。

T C P传给I P的数据单元称作 T C P报文段或简称为T C P段(T C P

s e g m e n t)。I P传给网络接口层的数据单元称作I P数据报(IP datagram)。通过以太网传输的比特流称作帧(Fr a m e )

由于T C PU D PI C M PI G M P都要向I P传送数据,因此I P必须在

生成的I P首部中加入某种标识,以表明数据属于哪一层。为此, I P在首部中存入一个长度为8 b i t的数值,称作协议域。1表示为I C M P协议,2表示为I G M P协议,6表示为T C P协议,1 7表示为U D P协议。

类似地,许多应用程序都可以使用 T C PU D P来传送数据。运输层协议在生成报文首部时要存入一个应用程序的标识符。 T C PU D P都用一个1 6 b i t的端口号来表示不同的应用程序。T C PU D P把源端口号和目的端口号分别存入报文首部中。

网络接口分别要发送和接收I PA R PR A R P数据,因此也必须在以太网的帧首部中加入某种形式的标识,以指明生成数据的网络层协议。为此,以太网的帧首部也有一个 16 bit的帧类型域。

为协议I C M PI G M P定位一直是一件很棘手的事情。在图1 - 4中,把它们与I P放在

同一层上,那是因为事实上它们是I P的附属协议。但是在这里,我们又把它们放在I P

的上面,这是因为ICMPIGMP报文都被封装在IP数据报中。

对于A R PR A R P,我们也遇到类似的难题。在这里把它们放在以太网设备驱动程

序的上方,这是因为它们和I P数据报一样,都有各自的以太网数据帧类型。但在图2 - 4

中,我们又把A R P作为以太网设备驱动程序的一部分,放在I P层的下面,其原因在逻

辑上是合理的。

 

2、链路层

T C P / I P协议族中,链路层主要有三个目的:(1)为I P模块发送和接收I P数据报;(2)为A R P模块发送A R P请求和接收A R P应答;(3)为R A R P发送R A R P请求和接收R A R P应答。T C P / I P支持多种不同的链路层协议,这取决于网络所使用的硬件,如以太网、令牌环网、F D D I(光纤分布式数据接口)及R S-2 3 2串行线路等。

以太网和IEEE 802封装: 当今 T C P / I P采用的主要的局域网技术。它采用一种称作 C S M A / C D的媒体接入方法,其意思是带冲突检测的载波侦听多路接入(Carrier Sense, Multiple Access with Collision Detection)。它的速率为10 Mb/s,地址为48 bitI E E E(电子电气工程师协会) 8 0 2委员会公布了一个稍有不同的标准集。

S L I P的全称是Serial Line IP。它是一种在串行线路上对I P数据报进行封装的简单形式

P P P,点对点协议修改了S L I P协议中的所有缺陷。P P P包括以下三个部分:

1) 在串行链路上封装 I P数据报的方法。 P P P既支持数据为8位和无奇偶检验的异步模式大多数计算机上都普遍存在的串行接口),还支持面向比特的同步链接。

2) 建立、配置及测试数据链路的链路控制协议( L C PLink Control Protocol)。它允许通

方进行协商,以确定不同的选项。

3) 针对不同网络层协议的网络控制协议( N C PNetwork Control Protocol)体系。当前定义的网络层有I PO S I网络层、D E C n e t以及A p p l e Ta l k。例如,IP NCP允许双方商定是报文首部进行压缩,类似于C S L I P(缩写词N C P也可用在T C P的前面)。

 

大多数的产品都支持环回接口(Loopback Interface),以允许运行在同一台主机上的客户

程序和服务器程序通过 T C P / I P进行通信。一旦传输层检测到目的端地址是环回地址时,应该可以省略部分传输层和所有网络层的逻辑操作。但是大多数的产品还是照样完成传输层和网络层的所有过程,只是当I P数据报离开网络层时把它返回给自己。正因为不经过链路层,所以这种数据包不会出现在网络上。

以太网和8 0 2 . 3对数据帧的长度都有一个限制,其最大值分别是1 5 0 01 4 9 2字节。链路层的这个特性称作MTU字节网络M T U,最大传输单元。

T C P / I P成功的原因之一是它几乎能在任何数据链路技术上运行。

 

3I PT C P / I P协议族中最为核心的协议。所有的T C PU D PI C M PI G M P数据都以I P数据报格式传输。

不可靠(u n r e l i a b l e)的意思是它不能保证 I P数据报能成功地到达目的地。 I P仅提供最好的传输服务。如果发生某种错误时,如某个路由器暂时用完了缓冲区, I P有一个简单的错误处理算法:丢弃该数据报,然后发送 I C M P消息报给信源端。任何要求的可靠性必须由上层来提供(如T C P)。

无连接(c o n n e c t i o n l e s s)这个术语的意思是I P并不维护任何关于后续数据报的状态信息。每个数据报的处理是相互独立的。这也说明, I P数据报可以不按发送顺序接收。如果一信源向相同的信宿发送两个连续的数据报(先是 A,然后是B),每个数据报都是独立地进行路由选择,可能选择不同的路线,因此B可能在A到达之前先到达。

为了计算一份数据报的 I P检验和,首先把检验和字段置为 0。然后,对首部中每个 16 bit

进行二进制反码求和(整个首部看成是由一串 16 bit的字组成),结果存在检验和字段中。当收到一份I P数据报后,同样对首部中每个16 bit进行二进制反码的求和。由于接收方在计算过程中包含了发送方存在首部中的检验和,因此,如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全 1。如果结果不是全1(即检验和错误),那么I P就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。I P路由选择是简单的,特别对于主机来说。如果目的主机与源主机直接相连(如点对点链路)或都在一个共享网络上(以太网或令牌环网),那么I P数据报就直接送到目的主机上。否则,主机把数据报发往一默认的路由器上,由路由器来转发该数据报。大多数的主机都是采用这种简单机制。即 I P层既可以配置成路由器的功能,也可以配置成主机的功能。当今的大多数多用户系统,包括几乎所有的 U n i x系统,都可以配置成一个路由器。我们可以为它指定主机和路由器都可以使用的简单路由算法。本质上的区别在于主机从不把数据报从一个接口转发到另一个接口,而路由器则要转发数据报。内含路由器功能的主机应该从不转发数据报,除非它被设置成那样。在 9 . 4小节中,我们将进一步讨论配置的有关问题。

在一般的体制中,I P可以从T C PU D PI C M PI G M P接收数据报(即在本地生成的数据报)并进行发送,或者从一个网络接口接收数据报(待转发的数据报)并进行发送。 I P层在内存中有一个路由表。当收到一份数据报并进行发送时,它都要对该表搜索一次。当数据报来自某个网络接口时,I P首先检查目的I P地址是否为本机的I P地址之一或者I P广播地址。如果确实是这样,数据报就被送到由 I P首部协议字段所指定的协议模块进行处理。如果数据报的目的不是这些地址,那么( 1)如果I P层被设置为路由器的功能,那么就对数据报进行转发(也就是说,像下面对待发出的数据报一样处理);否则( 2)数据报被丢弃。

路由表中的每一项都包含下面这些信息:

* 目的I P地址。它既可以是一个完整的主机地址,也可以是一个网络地址,由该表目中的标志字段来指定(如下所述)。主机地址有一个非0的主机号(见图1 - 5),以指定某一特定的主机,而网络地址中的主机号为0,以指定网络中的所有主机(如以太网,令牌环网)。

* 下一站(或下一跳)路由器( next-hop router)的I P地址,或者有直接连接的网络 I P地址。下一站路由器是指一个在直接相连网络上的路由器,通过它可以转发数据报。下

一站路由器不是最终的目的,但是它可以把传送给它的数据报转发到最终目的。

*  标志。其中一个标志指明目的 I P地址是网络地址还是主机地址,另一个标志指明下一

站路由器是否为真正的下一站路由器,还是一个直接相连的接口(我们将在 9 . 2节中

详细介绍这些标志)。

为数据报的传输指定一个网络接口。

 

I P路由选择是逐跳地(h o p - b y - h o p )进行的。从这个路由表信息可以看出,I P并不知道到 达任何目的的完整路径(当然,除了那些与主机直接相连的目的)。所有的I P路由选择只为数 据报传输提供下一站路由器的 I P地址。它假定下一站路由器比发送数据报的主机更接近目的, 而且下一站路由器与该主机是直接相连的。

    I P路由选择主要完成以下这些功能:

    1) 搜索路由表,寻找能与目的I P地址完全匹配的表目(网络号和主机号都要匹配)。如果找到,则把报文发送给该表目指定的下一站路由器或直接连接的网络接口(取决于标

志字段的值)。

    2) 搜索路由表,寻找能与目的网络号相匹配的表目。如果找到,则把报文发送给该表目指定的下一站路由器或直接连接的网络接口(取决于标志字段的值)。目的网络上的所有主机都可以通过这个表目来处置。例如,一个以太网上的所有主机都是通过这种表目进行寻径的。这种搜索网络的匹配方法必须考虑可能的子网掩码。关于这一点我们在下一节中进行

讨论。

3) 搜索路由表,寻找标为“默认( d e f a u l t )”的表目。如果找到,则把报文发送给该表目指定的下一站路由器。

如果上面这些步骤都没有成功,那么该数据报就不能被传送。如果不能传送的数据报来自本机,那么一般会向生成数据报的应用程序返回一个“主机不可达”或“网络不可达”的错误。

 

给定I P地址和子网掩码以后,主机就可以确定 I P数据报的目的是:(1)本子网上的主机;

(2)本网络中其他子网中的主机;( 3)其他网络上的主机。如果知道本机的 I P地址,那么就知道它是否为A类、B类或C类地址(I P地址的高位可以得知),也就知道网络号和子网号之间的分界线。而根据子网掩码就可知道子网号与主机号之间的分界线。

 

举例

假设我们的主机地址是1 4 0 . 2 5 2 . 1 . 1(一个B类地址),而子网掩码为2 5 5 . 2 5 5 . 2 5 5 . 0(其中8b i t为子网号,8 bit为主机号)。* 如果目的I P地址是1 4 0 . 2 5 2 . 4 . 5,那么我们就知道B类网络号是相同的(1 4 0 . 2 5 2),但是子网号是不同的(14)。用子网掩码在两个I P地址之间的比较如图3 - 8所示。* 如果目的I P地址是1 4 0 . 2 5 2 . 1 . 2 2,那么B类网络号还是一样的(1 4 0 . 2 5 2),而且子网号也是一样的(1),但是主机号是不同的。

如果目的I P地址是1 9 2 . 4 3 . 2 3 5 . 6(一个C类地址),那么网络号是不同的,因而进一步的比较就不用再进行了。

posted @ 2007-10-25 19:43 吴剑 阅读(463) | 评论 (0)编辑 收藏

6、ICMP: Internet控制报文协议

I C M P报文通常被I P层或更高层协议( T C P或U D P)使用。I C M P报文是在I P数据报内部被传输的

ICMP不同类型由报文中的类型字段和代码字段来共同决定。可以确定I C M P报文是一份查询报文还是一份差错报文。因为对I C M P差错报

文有时需要作特殊处理,因此我们需要对它们进行区分。例如,在对I C M P差错报文进行响应

时,永远不会生成另一份I C M P差错报文(如果没有这个限制规则,可能会遇到一个差错产生

另一个差错的情况,而差错再产生差错,这样会无休止地循环下去)。当发送一份I C M P差错报文时,报文始终包含I P的首部和产生I C M P差错报文的I P数据报的前8个字节。这样,接收I C M P差错报文的模块就会把它与某个特定的协议(根据I P数据报首部中的协议字段来判断)和用户进程(根据包含在I P数据报前8个字节中的T C P或U D P报文首部中的T C P或U D P端口号来判断)联系起来。

 

7、ping程序

发送回显请求的p i n g程序为客户,而称被p i n g的主机为服务器。大多数的T C P / I P

实现都在内核中直接支持P i n g服务器—这种服务器不是一个用户进程(在第6章中描述的两

种I C M P查询服务,地址掩码和时间戳请求,也都是直接在内核中进行处理的)

I C M P回显请求和回显应答报文如图7 - 1所示。

对于其他类型的I C M P查询报文,服务器必须响应标识符和序列号字段。另外,客户发送

的选项数据必须回显,假设客户对这些信息都会感兴趣。

U n i x系统在实现p i n g程序时是把I C M P报文中的标识符字段置成发送进程的I D号。这样

即使在同一台主机上同时运行了多个p i n g程序实例, p i n g程序也可以识别出返回的信息。序列号从0开始,每发送一次新的回显请求就加1。p i n g程序打印出返回的每个分组的序列号,允许我们查看是否有分组丢失、失序或重复。I P是一种最好的数据报传递服务,因此

这三个条件都有可能发生。

敲入p i n g命令,几秒钟过后会在第1行打印出I P地址, D N S就是利用这段时间来确定主机

名所对应的I P地址。通常,第1个往返时间值要比其他的大。这是由于目的端的硬件地址不在A R P高速缓存中的缘故。在发送第一个回显请求之前要发送一个A R P请求并接收A R P应答,这需要花费几毫秒的时间。

IP首部最长为6 0个字节, 固定长度为2 0字节,R R选项用去3个字节(下面我们再讨论),这样只剩下3 7个字节( 6 0-2 0-3)来存放I P地址清单,也就是说只能存放9个I P地址。I P数据报中的R R选项的一般格式如图所示。

c o d e是一个字节,指明I P选项的类型。对于R R选项来说,它的值为7。l e n是R R选项总字

节长度,在这种情况下为3 9(尽管可以为R R选项设置比最大长度小的长度,但是p i n g程序

总是提供3 9字节的选项字段,最多可以记录9个I P地址。由于I P首部中留给选项的空间有限,

它一般情况都设置成最大长度)。

p t r称作指针字段。它是一个基于1的指针,指向存放下一个I P地址的位置。它的最小值为

4,指向存放第一个I P地址的位置。随着每个I P地址存入清单, p t r的值分别为8,1 2,1 6,最

大到3 6。当记录下9个I P地址后,p t r的值为4 0,表示清单已满。

I P时间戳选项与记录路由选项类似。

8、Traceroute程序

在7 . 3节中,我们描述了I P记录路由选项( R R)。为什么不使用这个选项而另外开发一个

新的应用程序?有三个方面的原因。首先,原先并不是所有的路由器都支持记录路由选项,

因此该选项在某些路径上不能使用( Tr a c e r o u t e程序不需要中间路由器具备任何特殊的或可选的功能)。

其次,记录路由一般是单向的选项。发送端设置了该选项,那么接收端不得不从收到的I P

首部中提取出所有的信息,然后全部返回给发送端。在7 . 3节中,我们看到大多数P i n g服务器的实现(内核中的I C M P回显应答功能)把接收到的R R清单返回,但是这样使得记录下来的I P地址翻了一番(一来一回)。这样做会受到一些限制,这一点我们在下一段讨论( Tr a c e r o u t e程序只需要目的端运行一个U D P模块—其他不需要任何特殊的服务器应用程序)。

最后一个原因也是最主要的原因是, I P首部中留给选项的空间有限,不能存放当前大多

数的路径。在I P首部选项字段中最多只能存放9个I P地址。在原先的A R PA N E T中这是足够的,

但是对现在来说是远远不够的。

Tr a c e r o u t e程序使用I C M P报文和I P首部中的T T L字段(生存周期)。每个处理数据报的路由器都需要把T T L的值减1或减去数据报在路由器中停留的秒数。由于大多数的路由器转发数据报的时延都小于1秒钟,因此T T L最终成为一个跳站的计数器,所经过的每个路由器都将其值减1。T T L字段的目的是防止数据报在选路时无休止地在网络中流动。

当路由器收到一份I P数据报,如果其T T L字段是0或1,则路由器不转发该数据报(接收到

这种数据报的目的主机可以将它交给应用程序,这是因为不需要转发该数据报。但是在通常

情况下,系统不应该接收T T L字段为0的数据报)。相反,路由器将该数据报丢弃,并给信源

机发一份I C M P“超时”信息。Tr a c e r o u t e程序的关键在于包含这份I C M P信息的I P报文的信源地址是该路由器的I P地址。

我们现在可以猜想一下Tr a c e r o u t e程序的操作过程。它发送一份T T L字段为1的I P数据报给

目的主机。处理这份数据报的第一个路由器将T T L值减1,丢弃该数据报,并发回一份超时

I C M P报文。这样就得到了该路径中的第一个路由器的地址。然后Tr a c e r o u t e程序发送一份

T T L值为2的数据报,这样我们就可以得到第二个路由器的地址。继续这个过程直至该数据报

到达目的主机。但是目的主机哪怕接收到T T L值为1的I P数据报,也不会丢弃该数据报并产生

一份超时I C M P报文,这是因为数据报已经到达其最终目的地。那么我们该如何判断是否已经

到达目的主机了呢?

Tr a c e r o u t e程序发送一份U D P数据报给目的主机,但它选择一个不可能的值作为U D P端口

号(大于30 000),使目的主机的任何一个应用程序都不可能使用该端口。因为,当该数据报

到达时,将使目的主机的U D P模块产生一份“端口不可达”错误(见6 . 5节)的I C M P报文。

这样,Tr a c e r o u t e程序所要做的就是区分接收到的I C M P报文是超时还是端口不可达,以判断什么时候结束。


posted @ 2007-10-24 23:09 吴剑 阅读(367) | 评论 (0)编辑 收藏

大家都知道数据在计算机中都是按字节来储存了,1个字节等于8位(1Byte=8bit),而计算机只能识别0和1这两个数,所以根据排列,1个字 节能代表256种不同的信息,即28(0和1两种可能,8位排列),比如定义一个字节大小的无符号整数(unsigned char),那么它能表示的是0~255(0~28-1)这些数,一共是256个数,因为,前面说了,一个字节只能表示256种不同的信息。别停下,还是 一个字节的无符号整数,我们来进一步剖析它,0是这些数中最小的一个,我们先假设它在计算机内部就用8位二进制表示为00000000(从理论上来说也可 以表示成其他不同的二进制码,只要这256个数每个数对应的二进制码都不相同就可以了),再假设1表示为00000001,2表示为00000010,3 表示为00000011,依次类推,那么最大的那个数255在8位二进制中就表示为最大的数11111111,然后,我们把这些二进制码换算成十进制看 看,会发现刚好和我们假设的数是相同的,而事实上,在计算机中,无符号的整数就是按这个原理来储存的,所以告诉你一个无符号的整数的二进制码,你就可以知 道这个数是多少,而且知道在计算机中,这个数本身就是以这个二进制码来储存的。比如我给你一个2个字节大小的二进制码,首先声明它表示的是无符号的整数: 00000000 00000010,我们把前面的0省略,换算一下,它表示的也是数值2,和前面不同的是,它占了2个字节的内存。不同的类型占的内存空间不同,如在我的电 脑中char是1个字节,int是4个字节,long是8个字节(你的可能不同,这取决于不同的计算机设置),它们的不同之处仅仅是内存大的能表示的不同 的信息多些,也就是能表示的数范围更大些(unsigned int能表示的范围是0~28*4-1),至于怎么算,其实都是一样的,直接把二进制与十进制相互转换,二进制就是它在计算机中的样子,十进制就是我们所 表示的数。啊哈,原来这些都是可以计算的呀,我曾经还以为不同的计算机储存的原理是不同的,取决于商家的喜好呢,呵呵。说了这么多怎么还没有提到原码、反 码和补码呀,别急别急,心急吃不了热豆腐,呵呵,因为无符号的整数根本就没有原码、反码和补码。(啊,那不是被欺骗了,5555````我告诉妈妈去,哥 哥欺负我)都说了别急嘛,你就不想想我说了这么半天的无符号整数,那么有符号的整数怎么办啊?

  呵呵,对,只有有符号的整数才有原码、反 码和补码的!其他的类型一概没有。虽然我们也可以用二进制中最小的数去对应最小的负数,最大的也相对应,但是那样不科学,下面来说说科学的方法。还是说一 个字节的整数,不过这次是有符号的啦,1个字节它不管怎么样还是只能表示256个数,因为有符号所以我们就把它表示成范围:-128-127。它在计算机 中是怎么储存的呢?可以这样理解,用最高位表示符号位,如果是0表示正数,如果是1表示负数,剩下的7位用来储存数的绝对值的话,能表示27个数的绝对 值,再考虑正负两种情况,27*2还是256个数。首先定义0在计算机中储存为00000000,对于正数我们依然可以像无符号数那样换算,从 00000001到01111111依次表示1到127。那么这些数对应的二进制码就是这些数的原码。到这里很多人就会想,那负数是不是从 10000001到11111111依次表示-1到-127,那你发现没有,如果这样的话那么一共就只有255个数了,因为10000000的情况没有考 虑在内。实际上,10000000在计算机中表示最小的负整数,就是这里的-128,而且实际上并不是从10000001到11111111依次表示-1 到-127,而是刚好相反的,从10000001到11111111依次表示-127到-1。负整数在计算机中是以补码形式储存的,补码是怎么样表示的 呢,这里还要引入另一个概念——反码,所谓反码就是把负数的原码(负数的原码和和它的绝对值所对应的原码相同,简单的说就是绝对值相同的数原码相同)各个 位按位取反,是1就换成0,是0就换成1,如-1的原码是00000001,和1的原码相同,那么-1的反码就是11111110,而补码就是在反码的基 础上加1,即-1的补码是11111110+1=11111111,因此我们可以算出-1在计算机中是按11111111储存的。总结一下,计算机储存有 符号的整数时,是用该整数的补码进行储存的,0的原码、补码都是0,正数的原码、补码可以特殊理解为相同,负数的补码是它的反码加1。下面再多举几个例 子,来帮助大家理解!

十进制 → 二进制  (怎么算?要是不知道看计算机基础的书去)
47   → 101111

有符号的整数    原码    反码    补码
  47      00101111  11010000  00101111(正数补码和原码相同)
 -47      00101111  11010000  11010001(负数补码是在反码上加1)

再举个例子,学C语言的同学应该做过这道题:
把-1以无符号的类型输出,得什么结果?(程序如下)

#include<iostream.h>
void main()
{
 short int n=-1;
 cout<<(unsigned short int)n<<endl;
}

首先在我的电脑中short int类型的储存空间是2个字节,你的可能不同,我说过,这取决于你的计算机配置。它能储存28*2=65536个不同的数据信息,如果是无符号那么它的 范围是0~65535(0~216-1),如果是有符号,那么它的范围是-32768~32767(-215~215-1)。这道题目中,开始n是一个有 符号的短整型变量,我们给它赋值为-1,根据我们前面所说的,它在计算机中是以补码11111111 11111111储存的,注意前面说了是2个字节。如果把它强制为无符号的短整型输出的话,那么我们就把刚才的二进制把看成无符号的整型在计算机中储存的 形式,对待无符号的整型就没有什么原码、反码和补码的概念了,直接把11111111 11111111转化成十进制就是65535,其实我们一看都是一就知道它是范围中最大的一个数了。呵呵,就这么简单。你个把上面的源代码编译运行看看, 如果你的电脑short int也是两个字节,那就会和我得一样的结果。你可以先用这个语句看看:cout<<sizeof(short int)<<endl;看看你的电脑里的短整型占多少的储存空间,也可以用sizeof来看其它任何类型所分配的储存空间。

  最后提醒一句,关于数据如何在计算机中储存的,这里只适用于整型的数据,对于浮点型的是另一种方式,这里我们暂时就不深究了。


posted @ 2007-10-23 23:11 吴剑 阅读(433) | 评论 (0)编辑 收藏
//*********************************************************  
  //      
    //      

//字符串转化成16进制存入CHAR数组中  
  //       作者:QQTCC  
  //       日期:2006.12.7  
  //  
  //*********************************************************  
  #include   <iostream>  
  using   namespace   std;  
  bool   isNumber(char   *p)               //字符串位是否数字或字母判断;  
  {  
  if(*p>='0'&&   *p<='9')  
  {return   true;}  
  else   return   false;  
  }  
   
  void   convert(char   *p,unsigned   char   str[])  
  {  
  int   high,low,i=0;  
  while(*p!='\0')  
  {  
  if(isNumber(p))  
  {high=(*p-48)<<4;   }       //按ASCII转换成整数并左移4位  
  else  
  {high=(*p-55)<<4;   }       //ASCII转换成整数左移4位  
  high=high&0xF0;               //低4位屏蔽  
  ++p;                                     //读低4位  
  if(isNumber(p))  
  {low=*p-48;   }                   //左移4位  
  else  
  {low=*p-55;     }                 //左移4位  
  low=low&0x0F;                   //高4位屏蔽  
  str[i++]=low|high;  
  ++p;  
  }  
  }  
  void   main()  
  {  
  char   str[]="1234567890ABCDEF";  
  unsigned   char   dest[8];  
  char   *p=str;  
  convert(p,dest);  
  for(int   j=0;j<8;j++)  
  {cout<<hex<<dest[j]<<endl;} //结果扩展ASCII,我查表验证过.  
  }


posted @ 2007-10-21 23:44 吴剑 阅读(1685) | 评论 (1)编辑 收藏
仅列出标题
共3页: 1 2 3