Socket通信之操作系统的字节序和位数
關于Socket通信過程中字節序
在網絡編程里,網絡字節序是big-endian的,而大部分的PC的系統都是X86處理器系列,X86采用的是little-endian,所以需要將網絡數據流轉換成本地數據流的話,需要進行字節序的轉換。
標準庫里提供了hlton()和nthl()兩個函數來支持轉換。
hston(unsigned short), hlton(unsigned long) ?將本地字節序轉換為網絡字節序
ntohl(unsigned long), ntohs(unsigned short) ?將網絡字節序轉換為本地字節序
但是對于64位的整數進行轉換,標準庫并沒有提供相應的轉換函數。
關于本系統是Big-endian還是Little-endian存儲,可以寫一個簡單函數進行判斷:
#include<stdio.h>
int main(){int num = 0x1234;int* p = #if(*((char*)p) == 0x12){printf("Big-endian\n");}else{printf("Little-endian\n");}return 0;
}
另外,關于socket通信過程中客戶端和服務端操作系統位數的問題,本人在Vmware中開啟了三個Ubuntu(兩個為32位操作系統,一個為64位操作系統,皆為小端存儲),以一個32位系統運行socket通信的客戶端,另外兩個設置為服務端,通過修改客戶端的訪問目標ip地址觀察不同位系統socket通信結果,發現32to32,一切正常可以運行,程序命令行傳入參數可以順利達到服務端經服務端數據處理函數處理之后返回至客戶端并打印出來,期間在服務端打印出客戶端傳送過來的數據并打印客戶端的地址、端口信息。
但在32to64通信過程中,服務端顯示亂碼,且不能正常返回處理后的數據。
查閱資料:在32位機器和64機器中int類型都占用4個字節。編譯器可以根據自身硬件來選擇合適的大小,但是需要滿足約束:short和int型至少為16位,long型至少為32位,并且short型長度不能超過int型,而int型不能超過long型。?
這即是說各個類型的變量長度是由編譯器來決定的,而當前主流的編譯器中一般是32位機器和64位機器中int型都是4個字節,總體而言,最大的不同點就是在long型和指針類型長度不一樣,對于指針而言,64位機器可以尋址2^64,每個內存地址長度為64位,即8字節。
服務端程序:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define MAX_LINE 100
/* 處理函數,用于將大寫字符轉換為小寫字符。參數為需要轉換的字符串 */
void my_fun(char * p)
{if(p == NULL) /* 空串 */return;for (; *p != '\0'; p++)if(*p >= 'A' && *p <= 'Z') /* 判斷字符并進行轉換,也可以使用庫函數 */*p = *p -'A' + 'a';
}
int main(void)
{struct sockaddr_in sin;struct sockaddr_in cin;int l_fd;int c_fd;socklen_t len;char buf[MAX_LINE]; /* 存儲傳送內容的緩沖區 */char addr_p[INET_ADDRSTRLEN]; /* 存儲客戶端地址的緩沖區 */int port = 8000; /* 端口號,使用8000 */int n; /* 讀寫字節數 */bzero(&sin, sizeof(sin)); /* 清空地址結構 */sin.sin_family = AF_INET; /* 使用IPv4通信域 */sin.sin_addr.s_addr = INADDR_ANY; /* 服務器可以接受任意地址 */sin.sin_port = htons(port); /* 端口號轉換為網絡字節序 */l_fd = socket(AF_INET, SOCK_STREAM, 0); /* 創立套接字,使用TCP協議 */bind(l_fd, (struct sockaddr*) &sin, sizeof(sin)); /* 將地址和套接字綁定 */listen(l_fd, 10); /* 開始監聽連接請求 */printf("waiting ...\n");while(1){/* 服務器程序多半是死循環 *//* 接受連接請求,從此函數中返回后就可以開始通信了 */c_fd = accept(l_fd, (struct sockaddr*) &cin, &len); n = read(c_fd, buf, MAX_LINE); /* 讀取客戶端傳來的信息 *///inet_ntop(AF_INET, &cin.sin_addr, addr_p, sizeof(addr_p));/* 將客戶端地址轉換為字符串 *///printf("client IP is %s, port is %d\n", addr_p, ntohs(cin.sin_port)); /* 打印客戶端地址和端口號 */printf("content is : %s\n", buf); /* 打印客戶端發送過來的字符串 */my_fun(buf); /* 調用大小寫轉換函數 */write(c_fd, buf, n); /* 將轉換后的字串發給客戶端 */close(c_fd); /* 通信結束,關閉套接字,準備下一次通信 */}if(close(l_fd) == -1){ /* 通信結束,關閉套接字,準備下一次通信 */perror("fail to close");exit(1);}return 0; /* 不應該執行到這里 */
}
?客戶端程序:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#define MAX_LINE 100int main(void)
{struct sockaddr_in sin;char msg[100];int l_fd;char buf[MAX_LINE]; /* 存儲傳送內容的緩沖區 */int port = 8000; /* 端口號,使用8000 */bzero(&sin, sizeof(sin)); /* 清空地址結構 */sin.sin_family = AF_INET; /* 使用IPv4通信域 */inet_pton(AF_INET,"10.17.38.98", &sin.sin_addr);sin.sin_port = htons(port); /* 端口號轉換為網絡字節序 */l_fd = socket(AF_INET, SOCK_STREAM, 0); /* 創立套接字,使用TCP協議 */connect(l_fd, (const struct sockaddr*)&sin, sizeof(sin));printf("Input Message:\n");scanf("%s", msg);write(l_fd, msg, strlen(msg)+1);sleep(5); //延時讀取read(l_fd, buf, MAX_LINE);printf("Receive messaege from server: %s\n", buf);//close(l_fd);if(close(l_fd) == -1){ /* 通信結束,關閉套接字,準備下一次通信 */perror("Fail to close !");exit(1);}return 0;
}
對64位服務端的server.c程序屏蔽掉客戶端IP、端口獲取并打印的相關代碼段,此時服務端、客戶端均能能夠正常運行。因此,在char型命令行參數傳遞過程中是沒有問題的,32位和64位操作系統的主要區別在于指針以及結構體(內存對齊位數不同),而accept()的第二個參數是客戶端的地址結構,由客戶端的connect()函數發送給服務端,在這過程中存在位數不匹配的問題,因此不能進行正常通信。
另,根據上述原理,可以聲明一個空類型指針用來判斷系統的位數。
#include<iostream>int main(){void* p;if(sizeof(p)==4){std::cout << "This is a 32-bit machine." << std::endl;}else std::cout << "This is a 64-bit machine." << std::endl;return 0;
}
在32位和64位系統測試結果如下:
?
總結
以上是生活随笔為你收集整理的Socket通信之操作系统的字节序和位数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 冷媒多少钱啊?
- 下一篇: 大龄女,211学校毕业,现找工作迷茫中?