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