1 常用縮寫
a -- address
f -- file? ? ? ? eg: fputs() -- file put stream
fd -- file descriptor
h - host(主機(jī))
in/inet -- internet? ? ? ? eg: sockaddr_in; inet_aton
n -- network(網(wǎng)絡(luò)字節(jié)序)/numeric(數(shù)值)
p -- protocol(協(xié)議)/presentation(表達(dá)/呈現(xiàn)形式)
s -- socket? ? ? ? eg: sin -- socket internet
t -- type,用于指定某種類型,很多情況下無(wú)特殊含義
u -- unsigned(無(wú)符號(hào))? ? ? ? eg: uint16 -- unsigned int 16 bits
2 常見(jiàn)類型
SA -- struct sockaddr -- 通用套接字地址結(jié)構(gòu)
socklen_t,套接字地址結(jié)構(gòu)長(zhǎng)度,一般為uint32_t
sa_family_t,在支持長(zhǎng)度字段(sin_len)中是8位無(wú)符號(hào)整型,不支持則是16位
in_addr_t, ipv4地址,至少32位的無(wú)符號(hào)整型,一般為uint32_t
in_port_t,?tcp/udp端口,至少16位的無(wú)符號(hào)整型一般為unit16_t
3 套接字與套接字函數(shù)
套接字(socket)是一種通信端點(diǎn),用于在網(wǎng)絡(luò)中進(jìn)行數(shù)據(jù)傳輸。在網(wǎng)絡(luò)編程中,套接字是一個(gè)抽象的概念,通常用于創(chuàng)建、配置和管理網(wǎng)絡(luò)連接。套接字負(fù)責(zé)處理網(wǎng)絡(luò)通信的細(xì)節(jié),包括建立連接、傳輸數(shù)據(jù)和斷開連接等
套接字函數(shù)是在網(wǎng)絡(luò)編程中常用的一類函數(shù),用于創(chuàng)建、管理和操作套接字,實(shí)現(xiàn)網(wǎng)絡(luò)通信。套接字函數(shù)以引用的形式傳遞套接字地址結(jié)構(gòu),相應(yīng)的參數(shù)是一個(gè)指向套接字地址結(jié)構(gòu)的指針。
進(jìn)程到內(nèi)核的套接字函數(shù):bind(), connect(), sendto()
內(nèi)核到進(jìn)程的套接字函數(shù):accept(), recvfrom(), getsockname(), getpeername()
關(guān)于進(jìn)程和內(nèi)核在本文不做進(jìn)一步探討。
4 套接字地址結(jié)構(gòu)
POSIX:可移植操作系統(tǒng)接口(Portable Operating System Interface of UNIX),是由IEEE定義的一系列標(biāo)準(zhǔn)
前文提到,大多數(shù)套接字函數(shù)需要一個(gè)指向套接字地址結(jié)構(gòu)的指針,而這些結(jié)構(gòu)的名字均以sockaddr_開頭。下列套接字結(jié)構(gòu)均采用POSIX定義:
4.1 ipv4套接字地址結(jié)構(gòu)sockaddr_in
struct in_addr
{
in_addr_t s_addr; //uint32_t,表示ipv4地址
//t是一個(gè)無(wú)明確含義的后綴
//網(wǎng)絡(luò)字節(jié)序
};
struct sockaddr_in //標(biāo)*為POSIX規(guī)范必要字段
{
uint8_t sin_len; //長(zhǎng)度字段,16字節(jié)
sa_family_t sin_family; //*協(xié)議族,AF_INET4
in_port_t sin_port; //*uint16_t,表示端口號(hào)
struct in_addr sin_addr; //*上面已定義
char sin_zero[8]; //未使用,零填充
};
4.2 通用套接字地址結(jié)構(gòu)sockaddr/SA
struct sockaddr //對(duì)指向特定協(xié)議的sa結(jié)構(gòu)進(jìn)行強(qiáng)制類型轉(zhuǎn)換
{
unit8_t sa_len;
sa_family_t sa_family;
char sa_data[14]; //與特定協(xié)議相關(guān)的地址信息
};
4.3 ipv6套接字地址結(jié)構(gòu)sockaddr_in6
struct in6_addr
{
uint8_t s6_addr[16]; //128bit,表示ipv6地址
//網(wǎng)絡(luò)字節(jié)序
};
#define SIN6_LEN
struct sockaddr_in6
{
uint8_t sin6_len; //長(zhǎng)度字段,28字節(jié)
sa_family_t sin6_family; //協(xié)議族,AF_INET6
in_port_t sin6_port; //網(wǎng)絡(luò)字節(jié)序,表示端口號(hào)
uint32_t sin6_flowinfo; //流信息,通常置0
struct in6_addr sin6_addr; //上面已定義
uint32_t sin6_scope_id; //標(biāo)識(shí)接口的范圍
};
5 字節(jié)操縱函數(shù)
?在處理套接字地址結(jié)構(gòu)時(shí),對(duì)字節(jié)進(jìn)行處理的函數(shù)
5.1 以b(byte)開頭的
void bzero(void *dest, size_t nbytes);
void bcopy(const void *src, void *dest, sieze_t btypes);
int bcmp(const void *ptrl, const void *ptr2, size_t nbytes);
bzero:把dest字符串中nbytes個(gè)字節(jié)置0,用于初始化套接字地址結(jié)構(gòu)
bcopy:將btypes個(gè)字節(jié)從src原地址移到dest目的地址
bcmp:比較兩個(gè)字符串,相同則返回0,不同返回非0
5.2 以mem(memory)開頭的
void *memset(void *dest, int c, size_t len);
void *memcpy(void *dest, const void *src, size_t nbytes);
void memcmp(const void *ptrl, const void *ptrl2, size_t nbytes);
memset:把dest字符串中l(wèi)en個(gè)字節(jié)置為c
memcpy:與bcopy類似,但先目的地址再源地址,與賦值語(yǔ)句的順序一致
memcmp:比較兩個(gè)字符串,相同則返回0,ptrl1比ptrl2大則返回正數(shù)(想必大家c語(yǔ)言都學(xué)過(guò))
6?地址轉(zhuǎn)換函數(shù)
6.1. 地址字符串與網(wǎng)絡(luò)字節(jié)序二進(jìn)制值的轉(zhuǎn)換
int inet_aton(const char *strptr, struct in_addr *addrptr);
inet_aton:有效返回1,無(wú)效返回0
in_addr_t inet_addr(const char *strptr);
?inet_addr:有效則返回32位二進(jìn)制網(wǎng)絡(luò)字節(jié)序的ipv4地址,否則返回INADDR_NONE
char *inet_ntoa(struct in_addr inaddr);
?inet_ntoa:返回一個(gè)點(diǎn)分十進(jìn)制的指針
6.2 表達(dá)(ASCII字符串)與數(shù)值(二進(jìn)制值)的轉(zhuǎn)換
int inet_pton(int family, const char *strptr, void *addrptr);
inet_pton:有效返回1,無(wú)效返回-1
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
inet_ntop:有效返回指針,否則返回NULL
7 時(shí)間獲取程序
7.1?一個(gè)簡(jiǎn)單的客戶端程序(ipv4/ipv6)
#include "unp.h"
int main(int argc, char **argv)
{
int sockfd;
//創(chuàng)建文件描述符,返回一個(gè)整型來(lái)唯一標(biāo)識(shí)一個(gè)打開的文件
int n;
//是read函數(shù)的返回值,代表從套接字中讀取到的字節(jié)數(shù)
char recvline[MAXLINE + 1];
//確保數(shù)組可以容納最大長(zhǎng)度為MAXLINE的字符串,并在末尾存儲(chǔ)終止符\0
struct sockaddr_in servaddr; //ipv4
//struct sockaddr_in6 servaddr; //ipv6
//創(chuàng)建套接字表示服務(wù)器IPv4/ipv6端口號(hào)和地址
if (argc != 2) //如果命令行輸入的參數(shù)數(shù)量不是2,則出錯(cuò)
err_quit("usage: a.out <IPaddress>");
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
//if ( (sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)【ipv6】
err_sys("socket error");
//socket函數(shù)三個(gè)參數(shù)代表ipv4,面向連接的tcp套接字
//0表示使用默認(rèn)的協(xié)議,對(duì)tcp來(lái)說(shuō)通常是0
//返回值sockfd若為-1則創(chuàng)建套接字失敗
//這兩行代碼可以用包裹函數(shù)Socket()等效代替,如后文服務(wù)器代碼中所示
bzero(&servaddr, sizeof(servaddr));
//將從起始位置到sizeof()大小的內(nèi)存區(qū)域置0,可用下面的代碼代替
// memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; //地址族為ipv4
//servaddr.sin6_family = AF_INET6; //地址族為ipv6
servaddr.sin_port = htons(13);
//servaddr.sin6_port = htons(13);【ipv6】
//用sin_port來(lái)存儲(chǔ)端口號(hào),這里為網(wǎng)絡(luò)字節(jié)序中的13
//用htons()將主機(jī)字節(jié)序(可能是大端或者小端)轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序(大端)
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
//if (inet_pton(AF_INET6, argv[1], &servaddr.sin6_addr) <= 0)【ipv6】
err_quit("inet_pton error for %s", argv[1]);
//用inet_pton()將第二個(gè)命令行參數(shù)(ip地址)轉(zhuǎn)換為二進(jìn)制
//并存儲(chǔ)在seraddr.sin_addr中
if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
err_sys("connect error");
//建立一個(gè)連接到遠(yuǎn)程服務(wù)器的套接字連接
//SA即為struct sockaddr通用套接字
while ( (n = read(sockfd, recvline, MAXLINE)) > 0)
{
recvline[n] = 0; //把末尾數(shù)據(jù)清零
if (fputs(recvline, stdout) == EOF)
err_sys("fputs error");
//將從服務(wù)器讀取的數(shù)據(jù)寫入標(biāo)準(zhǔn)輸出流(stdout)
//fputs返回非負(fù)數(shù)則成功,返回負(fù)數(shù)代表錯(cuò)誤(End Of File)
}
//從套接字中讀取至多MAXLINE個(gè)字,
//若n等于0則讀到了文件末尾
if(n<0)
err_sys("read error");
exit(0);
}
7.2?一個(gè)簡(jiǎn)單的服務(wù)器程序(ipv4)
看完客戶端代碼,服務(wù)器代碼也就大同小異了文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-794571.html
#include "unp.h"
#include <time.h>
int
main(int argc, char **argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[MAXLINE];
time_t ticks;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
//用listen()轉(zhuǎn)化為監(jiān)聽(tīng)套接字,ipv4,tcp
//將套接字函數(shù)的首字母大寫,則變成了對(duì)應(yīng)的具有錯(cuò)誤檢測(cè)功能的包裹函數(shù)
bzero(&servaddr, sizeof(servaddr));
//將從起始位置到sizeof()大小的內(nèi)存區(qū)域置0
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//指定ip地址為INADDR_ANY,即能在任意網(wǎng)絡(luò)接口上監(jiān)聽(tīng)客戶連接
servaddr.sin_port = htons(13); /* daytime server */
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
//將監(jiān)聽(tīng)套接字綁定到服務(wù)器地址上
//izeof(servaddr)表示要綁定的地址信息的長(zhǎng)度
Listen(listenfd, LISTENQ);
//LISTENQ:常數(shù),表示系統(tǒng)內(nèi)核允許在這排隊(duì)的最大客戶連接數(shù)
for ( ; ; ) //無(wú)限循環(huán)
{
connfd = Accept(listenfd, (SA *) NULL, NULL);
//Accept:阻塞函數(shù),當(dāng)沒(méi)有連接請(qǐng)求的時(shí)候會(huì)一直等待
//(SA*) NULL:表示不獲取客戶端的地址信息
//NULL:表示不獲取客戶端地址的地址長(zhǎng)度參數(shù)
//confid:接受Accept返回的套接字文件描述符
ticks = time(NULL);
//獲取當(dāng)前時(shí)間,并將當(dāng)前秒數(shù)返回給ticks
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
//格式化輸出,寫入buff這個(gè)指定的緩沖區(qū)
Write(connfd, buff, strlen(buff));
//向已建立連接的文件描述符connfd寫入數(shù)據(jù)
Close(connfd);
}
}
?至此,對(duì)這個(gè)簡(jiǎn)單的客戶端和服務(wù)器程序應(yīng)該有了較為全面的理解。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-794571.html
到了這里,關(guān)于unix網(wǎng)絡(luò)編程-簡(jiǎn)易服務(wù)器與客戶端程序解析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!