Linux C高级编程——网络编程之API(5)
Linux C網(wǎng)絡(luò)編程——API
宗旨:技術(shù)的學(xué)習(xí)是有限的,分享的精神的無(wú)限的。
? ? ? ??
一、基本socket函數(shù)
? ? ? ? Linux系統(tǒng)是通過(guò)提供套接字(socket)來(lái)進(jìn)行網(wǎng)絡(luò)編程的。網(wǎng)絡(luò)的socket數(shù)據(jù)傳輸是一種特殊的I/O,socket也是一種文件描述符。socket也有一個(gè)類似于打開(kāi)文件的函數(shù):socket(),調(diào)用socket(),該函數(shù)返回一個(gè)整型的socket的描述符,隨后的連接建立、數(shù)據(jù)傳輸?shù)炔僮饕捕际峭ㄟ^(guò)該socket實(shí)現(xiàn)。
1、socket函數(shù)
?????? ——?jiǎng)?chuàng)建套接字
(1)函數(shù)原型
? ? ? ? int socket(int domain, int type, int protocol);
(2)函數(shù)參數(shù)
? ? ? ? domain指明所使用的協(xié)議族,通常為PF_INET,表示TCP/IP協(xié)議;
? ? ? ? type參數(shù)指定socket的類型,基本上有三種:數(shù)據(jù)流套接字、數(shù)據(jù)報(bào)套接字、原始套接字
? ? ? ? protocol通常賦值"0"。
(3)返回值
? ? ? ? 調(diào)用成功,返回socket文件描述符;失敗,返回-1,并設(shè)置errno
? ? ? ? 兩個(gè)網(wǎng)絡(luò)程序之間的一個(gè)網(wǎng)絡(luò)連接包括五種信息:通信協(xié)議、本地協(xié)議地址、本地主機(jī)端口、遠(yuǎn)端主機(jī)地址和遠(yuǎn)端協(xié)議端口。socket數(shù)據(jù)結(jié)構(gòu)中包含這五種信息。
2、bind函數(shù)
?????? ——將套接字和指定的端口相連
(1)函數(shù)原型
struct sockaddr_in {short intsin_family;unsignedshort int sin_port;structin_addr sin_addr;unsignedchar sin_zero[8]; }; // addrlen為sockaddr的長(zhǎng)度。int bind(int sock_fd, struct sockaddr_in *my_addr, int addrlen);(2)函數(shù)參數(shù)
? ? ? ? sock_fd是調(diào)用socket函數(shù)返回值;
? ? ? ? my_addr是一個(gè)指向包含有本機(jī)IP地址及端口號(hào)等信息的sockaddr類型的指針;
struct sockaddr_in結(jié)構(gòu)類型是用來(lái)保存socket信息的。
(3)返回值
? ? ? ? 成功返回0,否則,返回-1,并置errno。
3、connect函數(shù)
?????? ——客戶端發(fā)送服務(wù)請(qǐng)求
(1)函數(shù)原型
? ? ? ? int connect(int sock_fd, struct sockaddr*serv_addr,int addrlen);
(2)函數(shù)參數(shù)
? ? ? ? sock_fd 是socket函數(shù)返回的socket描述符;
? ? ? ? serv_addr是包含遠(yuǎn)端主機(jī)IP地址和端口號(hào)的指針;
? ? ? ? addrlen是結(jié)構(gòu)sockaddr_in的長(zhǎng)度。
(3)返回值
? ? ? ? 成功返回0,否則返回-1,并置errno。
4、listen函數(shù)
? ? ? ? ——等待指定的端口的出現(xiàn)客戶端連接
(1)函數(shù)原型
? ? ? ? int listen(int sock_fd, int backlog);
(2)函數(shù)原型
? ? ? ? sock_fd 是socket()函數(shù)返回值;
? ? ? ? backlog指定在請(qǐng)求隊(duì)列中允許的最大請(qǐng)求數(shù)
(3)返回值
? ? ? ? 調(diào)用成功返回0,否則,返回-1,并置errno.
5、accecpt函數(shù)
?????? ——用于接受客戶端的服務(wù)請(qǐng)求
(1)函數(shù)原型
? ? ? ? int accept(int sock_fd, struct sockadd_in* addr, int addrlen);
(2)函數(shù)參數(shù)
? ? ? ? sock_fd是被監(jiān)聽(tīng)的socket描述符,
? ? ? ? addr通常是一個(gè)指向sockaddr_in變量的指針,
? ? ? ? addrlen是結(jié)構(gòu)sockaddr_in的長(zhǎng)度。
(3)返回值
? ? ? ? 成功返回新的套接字描述符,失敗返回-1,并置errno。
6、write函數(shù)
? ? ? ? ——write函數(shù)將buf中的nbytes字節(jié)內(nèi)容寫入文件描述符
(1)函數(shù)原型
? ? ? ? ssize_t write(int fd,const void *buf,size_t nbytes);
(2)函數(shù)參數(shù)
?????? fd:套接字
?????? buf:要寫入的數(shù)據(jù)
?????? nbytes:字節(jié)數(shù)
(3)返回值???
? ? ? ? 成功時(shí)返回寫的字節(jié)數(shù).失敗時(shí)返回-1. 并設(shè)置errno變量.
在網(wǎng)絡(luò)程序中,當(dāng)我們向套接字文件描述符寫時(shí)有倆種可能:
?(1)write的返回值大于0,表示寫了部分或者是全部的數(shù)據(jù).
(2)返回的值小于0,此時(shí)出現(xiàn)了錯(cuò)誤.需要根據(jù)錯(cuò)誤類型來(lái)處理. ?如果錯(cuò)誤為EINTR表示在寫的時(shí)候出現(xiàn)了中斷錯(cuò)誤. ?如果錯(cuò)誤為EPIPE表示網(wǎng)絡(luò)連接出現(xiàn)了問(wèn)題.
7、read函數(shù)
?????? ——read函數(shù)是負(fù)責(zé)從fd中讀取內(nèi)容
(1)函數(shù)原型
? ? ? ? ssize_t read(int fd,void *buf,size_t nbyte)
(2)函數(shù)參數(shù)
?????? fd:套接字
?????? buf:存放要讀出的數(shù)據(jù)
?????? nbytes:字節(jié)數(shù)
(3)返回值
? ? ? ? 當(dāng)讀成功時(shí),read返回實(shí)際所讀的字節(jié)數(shù),如果返回的值是0 表示已經(jīng)讀到文件的結(jié)束了,小于0表示出現(xiàn)了錯(cuò)誤。如果錯(cuò)誤為EINTR說(shuō)明讀是由中斷引起的,如果錯(cuò)誤是ECONNREST表示網(wǎng)絡(luò)連接出了問(wèn)題。
8、close函數(shù)
? ? ? ? ——關(guān)閉socket
(1)函數(shù)原型
? ? ? ? int close(sock_fd);
(2)函數(shù)參數(shù)
? ? ? ??sock_fd:套接字
(3)返回值
? ? ? ? 函數(shù)運(yùn)行成功返回0,否則返回-1。
?
二、socket編程的其他函數(shù)說(shuō)明
1、網(wǎng)絡(luò)字節(jié)順序及其轉(zhuǎn)換函數(shù)
(1)網(wǎng)絡(luò)字節(jié)順序
? ? ? ? ?每一臺(tái)機(jī)器內(nèi)部對(duì)變量的字節(jié)存儲(chǔ)順序不同,而網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)是一定要統(tǒng)一順序的。所以對(duì)內(nèi)部字節(jié)表示順序與網(wǎng)絡(luò)字節(jié)順序不同的機(jī)器,一定要對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)換,從程序的可移植性要求來(lái)講,就算本機(jī)的內(nèi)部字節(jié)表示順序與網(wǎng)絡(luò)字節(jié)順序相同也應(yīng)該在傳輸數(shù)據(jù)以前先調(diào)用數(shù)據(jù)轉(zhuǎn)換函數(shù),以便程序移植到其它機(jī)器上后能正確執(zhí)行。真正轉(zhuǎn)換還是不轉(zhuǎn)換是由系統(tǒng)函數(shù)自己來(lái)決定的。
(2)有關(guān)的轉(zhuǎn)換函數(shù)
unsigned short int htons(unsigned short int hostshort):主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,對(duì)無(wú)符號(hào)短型進(jìn)行操作4bytes
unsigned long int htonl(unsigned long int hostlong):主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,對(duì)無(wú)符號(hào)長(zhǎng)型進(jìn)行操作8bytes
unsigned short int ntohs(unsigned short int netshort):網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機(jī)字節(jié)順序,對(duì)無(wú)符號(hào)短型進(jìn)行操作4bytes
unsigned long int ntohl(unsigned long int netlong):網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機(jī)字節(jié)順序,對(duì)無(wú)符號(hào)長(zhǎng)型進(jìn)行操作8bytes
注:以上函數(shù)原型定義在netinet/in.h里
2、IP地址轉(zhuǎn)換
? ? ? ? 有三個(gè)函數(shù)將數(shù)字點(diǎn)形式表示的字符串IP地址與32位網(wǎng)絡(luò)字節(jié)順序的二進(jìn)制形式的IP地址進(jìn)行轉(zhuǎn)換
(1)unsigned long int inet_addr(constchar * cp):該函數(shù)把一個(gè)用數(shù)字和點(diǎn)表示的IP地址的字符串轉(zhuǎn)換成一個(gè)無(wú)符號(hào)長(zhǎng)整型,如:struct sockaddr_in ina
ina.sin_addr.s_addr=inet_addr("202.206.17.101")
該函數(shù)成功時(shí):返回轉(zhuǎn)換結(jié)果;失敗時(shí)返回常量INADDR_NONE,該常量=-1,二進(jìn)制的無(wú)符號(hào)整數(shù)-1相當(dāng)于255.255.255.255,這是一個(gè)廣播地址,所以在程序中調(diào)用iner_addr()時(shí),一定要人為地對(duì)調(diào)用失敗進(jìn)行處理。由于該函數(shù)不能處理廣播地址,所以在程序中應(yīng)該使用函數(shù)inet_aton()。
(2)int inet_aton(const char * cp,struct in_addr *inp):此函數(shù)將字符串形式的IP地址轉(zhuǎn)換成二進(jìn)制形式的IP地址;成功時(shí)返回1,否則返回0,轉(zhuǎn)換后的IP地址存儲(chǔ)在參數(shù)inp中。
(3)char * inet_ntoa(struct in-addr in):將32位二進(jìn)制形式的IP地址轉(zhuǎn)換為數(shù)字點(diǎn)形式的IP地址,結(jié)果在函數(shù)返回值中返回,返回的是一個(gè)指向字符串的指針。
3、字節(jié)處理函數(shù)
? ? ? ? Socket地址是多字節(jié)數(shù)據(jù),不是以空字符結(jié)尾的,這和C語(yǔ)言中的字符串是不同的。Linux提供了兩組函數(shù)來(lái)處理多字節(jié)數(shù)據(jù),一組以b(byte)開(kāi)頭,是和BSD系統(tǒng)兼容的函數(shù),另一組以mem(內(nèi)存)開(kāi)頭,是ANSI C提供的函數(shù)。
以b開(kāi)頭的函數(shù)有:
(1)void bzero(void * s,int n):將參數(shù)s指定的內(nèi)存的前n個(gè)字節(jié)設(shè)置為0,通常它用來(lái)將套接字地址清0。
(2)void bcopy(const void * src,void * dest,int n):從參數(shù)src指定的內(nèi)存區(qū)域拷貝指定數(shù)目的字節(jié)內(nèi)容到參數(shù)dest指定的內(nèi)存區(qū)域。
(3)int bcmp(const void * s1,const void * s2,int n):比較參數(shù)s1指定的內(nèi)存區(qū)域和參數(shù)s2指定的內(nèi)存區(qū)域的前n個(gè)字節(jié)內(nèi)容,如果相同則返回0,否則返回非0。
注:以上函數(shù)的原型定義在strings.h中。
以mem開(kāi)頭的函數(shù)有:
(1)void * memset(void * s,int c,size_t n):將參數(shù)s指定的內(nèi)存區(qū)域的前n個(gè)字節(jié)設(shè)置為參數(shù)c的內(nèi)容。
(2)void * memcpy(void * dest,const void * src,size_t n):功能同bcopy(),區(qū)別:函數(shù)bcopy()能處理參數(shù)src和參數(shù)dest所指定的區(qū)域有重疊的情況,memcpy()則不能。
(4)int memcmp(const void * s1,const void * s2,size_t n):比較參數(shù)s1和參數(shù)s2指定區(qū)域的前n個(gè)字節(jié)內(nèi)容,如果相同則返回0,否則返回非0。
注:以上函數(shù)的原型定義在string.h中。
?
三、程序說(shuō)明
? ? ? ? 本使用tcp協(xié)議進(jìn)行通信,服務(wù)端進(jìn)行監(jiān)聽(tīng),在收到客戶端的連接后,發(fā)送數(shù)據(jù)給客戶端;客戶端在接受到數(shù)據(jù)后打印出來(lái),然后關(guān)閉。
服務(wù)端:socket -- bind -- listen -- accept -- read/write -- close
客戶端:socket -- connect -- read/write -- close
/*************************************************************************> File Name: client.c> Author: libang> Mail: 1838039453@qq.com > Created Time: 2014年04月03日 星期日 02時(shí)59分37秒************************************************************************/#include <stdio.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <resolv.h> #include <stdlib.h> #include <netinet/in.h> #include <unistd.h> #include <arpa/inet.h>#define MAXBUF 1024int main(int argc,char *argv[]) {pid_t pid;int socket_fd,len;struct sockaddr_in dest;char buffer[MAXBUF];if(argc != 3){printf("error format,it must be:\n\t\t%s IP port\n",argv[0]);exit(EXIT_FAILURE);}if((socket_fd = socket(AF_INET,SOCK_STREAM,0)) < 0){perror("socket");exit(EXIT_FAILURE);}printf("Socket created!\n");bzero(&dest,sizeof(dest));dest.sin_family = AF_INET;dest.sin_port = htons(atoi(argv[2]));if(inet_aton(argv[1],(struct in_addr *)&dest.sin_addr.s_addr) == 0){perror(argv[1]);exit(errno);}if(connect(socket_fd,(struct sockaddr *)&dest,sizeof(dest)) == -1){perror("connect");exit(errno);}printf("Server connected!\n");if(-1 == (pid = fork())){perror("fork");exit(EXIT_FAILURE);}else if(pid == 0){while(1){bzero(buffer,MAXBUF);len = recv(socket_fd,buffer,MAXBUF,0);if(len > 0){printf("recv successful:'%s',%d byte recv\n",buffer,len);}else if(len < 0){perror("recv");break;}else{printf("the other one close,quit\n");break;}}}else{while(1){bzero(buffer,MAXBUF);//printf("please send message to send:");fgets(buffer,MAXBUF,stdin);if(!strncasecmp(buffer,"quit",4)){printf("I will quit!\n");break;}len = send(socket_fd,buffer,strlen(buffer) - 1,0);if(len < 0){perror("send");break;}}}close(socket_fd);return 0; } /*************************************************************************> File Name: server.c> Author: libang> Mail: 1838039453@qq.com > Created Time: 2016年04月03日 星期日 20時(shí)57分48秒************************************************************************/#include <stdio.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <resolv.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h>#define MAXBUF 1024int main(int argc,char *argv[]) {int socket_fd,new_fd;socklen_t len;struct sockaddr_in my_addr,their_addr;pid_t pid;unsigned int myport,list_num;char buf[MAXBUF];if(argv[2]){myport = atoi(argv[2]);}elsemyport = 7575;if(argv[3]){list_num = atoi(argv[3]);}elselist_num = 10;if((socket_fd = socket(AF_INET,SOCK_STREAM,0)) == -1){perror("socket");exit(EXIT_FAILURE);}bzero(&my_addr,sizeof(my_addr));my_addr.sin_family = AF_INET;my_addr.sin_port = htons(myport);if(argv[1]){my_addr.sin_addr.s_addr = inet_addr(argv[1]);}elsemy_addr.sin_addr.s_addr = INADDR_ANY;len = sizeof(struct sockaddr);if(bind(socket_fd,(struct sockaddr *)&my_addr,len) == -1){perror("bind");exit(EXIT_FAILURE);}if(listen(socket_fd,list_num) == -1){perror("listen");exit(EXIT_FAILURE);}printf("wait for connect!\n");if((new_fd = accept(socket_fd,(struct sockaddr *)&their_addr,&len)) == -1){perror("accept");exit(EXIT_FAILURE);}elseprintf("server:got connection from %s,port %d,socket %d\n",inet_ntoa(their_addr.sin_addr),ntohs(their_addr.sin_port),new_fd);if(-1 == (pid = fork())){perror("fork");exit(EXIT_FAILURE);}else if(pid == 0){while(1){bzero(buf,MAXBUF);//printf("input the message to send:");fgets(buf,MAXBUF,stdin);if(!strncasecmp(buf,"quit",4)){printf("I will close the connect!\n");break;}len = send(new_fd,buf,strlen(buf) - 1,0);if(len < 0){printf("message '%s' send failure!error code is %d error message is '%s'\n",buf,errno,strerror(errno));break;}}}else{while(1){bzero(buf,MAXBUF);len = recv(new_fd,buf,MAXBUF,0);if(len > 0){printf("message recv successful!:'%s',%dByte recv\n",buf,len);}else if(len < 0){printf("recv failure!errno code is %d,errno message is ‘%s\n",errno,strerror(errno));break;}else{printf("the other one close quit!\n");break;}}}close(new_fd);close(socket_fd);return 0; } gcc -o server server.cgcc -o client client.c?
./server 192.168.207.129 7575 5
./client 192.168.207.129 7575
總結(jié)
以上是生活随笔為你收集整理的Linux C高级编程——网络编程之API(5)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 软件测试之编写测试用例
- 下一篇: [人工智能]动物专家系统work