Linux的socket函數(shù)的編程與說明
一、基本socket()函數(shù)
? ? Linux系統(tǒng)是通過提供套接字(socket)來進行網(wǎng)絡(luò)編程的。網(wǎng)絡(luò)的socket數(shù)據(jù)傳輸是一種特殊的I/O,socket也是一種文件描述符。socket也有一個類似于打開文件的函數(shù):socket(),調(diào)用socket(),該函數(shù)返回一個整型的socket的描述符,隨后的連接建立、數(shù)據(jù)傳輸?shù)炔僮饕捕际峭ㄟ^該socket實現(xiàn)。
1、socket函數(shù)
? ? syntax:int socket(int domain, int type, int protocol);
功能說明:
???調(diào)用成功,返回socket文件描述符;失敗,返回-1,并設(shè)置errno
參數(shù)說明:
domain指明所使用的協(xié)議族,通常為PF_INET,表示TCP/IP協(xié)議;
type參數(shù)指定socket的類型,基本上有三種:數(shù)據(jù)流套接字、數(shù)據(jù)報套接字、原始套接字
protocol通常賦值"0"。
兩個網(wǎng)絡(luò)程序之間的一個網(wǎng)絡(luò)連接包括五種信息:通信協(xié)議、本地協(xié)議地址、本地主機端口、遠端主機地址和遠端協(xié)議端口。socket數(shù)據(jù)結(jié)構(gòu)中包含這五種信息。
2、bind函數(shù)
? ? syntax:int bind(int sock_fd,struct sockaddr_in *my_addr, int addrlen);
功能說明:
???將套接字和指定的端口相連。成功返回0,否則,返回-1,并置errno.
參數(shù)說明:
????sock_fd是調(diào)用socket函數(shù)返回值,
my_addr是一個指向包含有本機IP地址及端口號等信息的sockaddr類型的指針;
struct sockaddr_in結(jié)構(gòu)類型是用來保存socket信息的:
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
????addrlen為sockaddr的長度。
3、connect函數(shù)
? ? syntax:?int connect(int sock_fd, struct sockaddr *serv_addr,int addrlen);
功能說明:
???客戶端發(fā)送服務(wù)請求。成功返回0,否則返回-1,并置errno。
參數(shù)說明:
???sock_fd 是socket函數(shù)返回的socket描述符;serv_addr是包含遠端主機IP地址和端口號的指針;addrlen是結(jié)構(gòu)sockaddr_in的長度。
4、listen函數(shù)
? ? syntax:int listen(int sock_fd, int backlog);
功能說明:
???等待指定的端口的出現(xiàn)客戶端連接。調(diào)用成功返回0,否則,返回-1,并置errno.
參數(shù)說明:
???sock_fd 是socket()函數(shù)返回值;
???backlog指定在請求隊列中允許的最大請求數(shù)。
5、accecpt函數(shù)
? ? syntax:int accept(int sock_fd, struct sockadd_in* addr, int addrlen);
功能說明:
???用于接受客戶端的服務(wù)請求,成功返回新的套接字描述符,失敗返回-1,并置errno。
參數(shù)說明:
???sock_fd是被監(jiān)聽的socket描述符,
???addr通常是一個指向sockaddr_in變量的指針,
???addrlen是結(jié)構(gòu)sockaddr_in的長度。
6、write函數(shù)
? ? syntax:ssize_t write(int fd,const void *buf,size_t nbytes)
功能說明:
????write函數(shù)將buf中的nbytes字節(jié)內(nèi)容寫入文件描述符fd.成功時返回寫的字節(jié)數(shù).失敗時返回-1. 并設(shè)置errno變量。
????在網(wǎng)絡(luò)程序中,當(dāng)我們向套接字文件描述符寫時有倆種可能:
??????1)write的返回值大于0,表示寫了部分或者是全部的數(shù)據(jù).
??????2)返回的值小于0,此時出現(xiàn)了錯誤.需要根據(jù)錯誤類型來處理.
????????如果錯誤為EINTR表示在寫的時候出現(xiàn)了中斷錯誤.
????????如果錯誤為EPIPE表示網(wǎng)絡(luò)連接出現(xiàn)了問題.
7、read函數(shù)
? ? syntax:ssize_t read(int fd,void *buf,size_t nbyte)
函數(shù)說明:
????read函數(shù)是負(fù)責(zé)從fd中讀取內(nèi)容.當(dāng)讀成功時,read返回實際所讀的字節(jié)數(shù),如果返回的值是0 表示已經(jīng)讀到文件的結(jié)束了,小于0表示出現(xiàn)了錯誤.
????如果錯誤為EINTR說明讀是由中斷引起的,
????如果錯誤是ECONNREST表示網(wǎng)絡(luò)連接出了問題.
8、close函數(shù)
? ? syntax:int close(sock_fd);
說明:
? ?當(dāng)所有的數(shù)據(jù)操作結(jié)束以后,你可以調(diào)用close()函數(shù)來釋放該socket,從而停止在該socket上的任何數(shù)據(jù)操作:
函數(shù)運行成功返回0,否則返回-1。
二、socket編程的其他函數(shù)說明
1、 網(wǎng)絡(luò)字節(jié)順序及其轉(zhuǎn)換函數(shù)
1) 網(wǎng)絡(luò)字節(jié)順序
每一臺機器內(nèi)部對變量的字節(jié)存儲順序不同,而網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)是一定要統(tǒng)一順序的。所以對內(nèi)部字節(jié)表示順序與網(wǎng)絡(luò)字節(jié)順序不同的機器,
一定要對數(shù)據(jù)進行轉(zhuǎn)換,從程序的可移植性要求來講,就算本機的內(nèi)部字節(jié)表示順序與網(wǎng)絡(luò)字節(jié)順序相同也應(yīng)該在傳輸數(shù)據(jù)以前先調(diào)用數(shù)據(jù)轉(zhuǎn)換函數(shù),
以便程序移植到其它機器上后能正確執(zhí)行。真正轉(zhuǎn)換還是不轉(zhuǎn)換是由系統(tǒng)函數(shù)自己來決定的。
2) 有關(guān)的轉(zhuǎn)換函數(shù)
* unsigned short int htons(unsigned short int hostshort):
主機字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,對無符號短型進行操作4bytes
* unsigned long int htonl(unsigned long int hostlong):
主機字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,對無符號長型進行操作8bytes
* unsigned short int ntohs(unsigned short int netshort):
網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機字節(jié)順序,對無符號短型進行操作4bytes
* unsigned long int ntohl(unsigned long int netlong):
網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機字節(jié)順序,對無符號長型進行操作8bytes
注:以上函數(shù)原型定義在netinet/in.h里。
2、IP地址轉(zhuǎn)換
有三個函數(shù)將數(shù)字點形式表示的字符串IP地址與32位網(wǎng)絡(luò)字節(jié)順序的二進制形式的IP地址進行轉(zhuǎn)換
(1) unsigned long int inet_addr(const char * cp):該函數(shù)把一個用數(shù)字和點表示的IP地址的字符串轉(zhuǎn)換成一個無符號長整型,如:struct sockaddr_in ina
ina.sin_addr.s_addr=inet_addr("202.206.17.101")
該函數(shù)成功時:返回轉(zhuǎn)換結(jié)果;失敗時返回常量INADDR_NONE,該常量=-1,二進制的無符號整數(shù)-1相當(dāng)于255.255.255.255,這是一個廣播地址,所以在程序中調(diào)用iner_addr()時,一定要人為地對調(diào)用失敗進行處理。由于該函數(shù)不能處理廣播地址,所以在程序中應(yīng)該使用函數(shù)inet_aton()。
(2)int inet_aton(const char * cp,struct in_addr * inp):此函數(shù)將字符串形式的IP地址轉(zhuǎn)換成二進制形式的IP地址;成功時返回1,否則返回0,轉(zhuǎn)換后的IP地址存儲在參數(shù)inp中。
(3) char * inet_ntoa(struct in-addr in):將32位二進制形式的IP地址轉(zhuǎn)換為數(shù)字點形式的IP地址,結(jié)果在函數(shù)返回值中返回,返回的是一個指向字符串的指針。
3、字節(jié)處理函數(shù)
? ? Socket地址是多字節(jié)數(shù)據(jù),不是以空字符結(jié)尾的,這和C語言中的字符串是不同的。Linux提供了兩組函數(shù)來處理多字節(jié)數(shù)據(jù),一組以b(byte)開頭,是和BSD系統(tǒng)兼容的函數(shù),另一組以mem(內(nèi)存)開頭,是ANSI C提供的函數(shù)。
以b開頭的函數(shù)有:
(1) void bzero(void * s,int n):將參數(shù)s指定的內(nèi)存的前n個字節(jié)設(shè)置為0,通常它用來將套接字地址清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個字節(jié)內(nèi)容,如果相同則返回0,否則返回非0。
注:以上函數(shù)的原型定義在strings.h中。
? ? 以mem開頭的函數(shù)有:
(1) void * memset(void * s,int c,size_t n):將參數(shù)s指定的內(nèi)存區(qū)域的前n個字節(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個字節(jié)內(nèi)容,如果相同則返回0,否則返回非0。
注:以上函數(shù)的原型定義在string.h中。
三、程序說明
本使用tcp協(xié)議進行通信,服務(wù)端進行監(jiān)聽,在收到客戶端的連接后,發(fā)送數(shù)據(jù)給客戶端;客戶端在接受到數(shù)據(jù)后打印出來,然后關(guān)閉。
1、client.c
#include
int main()
{
int cfd;
int recbytes;
int sin_size;
char buffer[1024]={0};???
struct sockaddr_in s_add,c_add;
unsigned short portnum=0x8888;?
printf("Hello,welcome to client !rn");
cfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == cfd)
{
????printf("socket fail ! rn");
????return -1;
}
printf("socket ok !rn");
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr= inet_addr("192.168.1.2");
s_add.sin_port=htons(portnum);
printf("s_addr = %#x ,port : %#xrn",s_add.sin_addr.s_addr,s_add.sin_port);
if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
????printf("connect fail !rn");
????return -1;
}
printf("connect ok !rn");
if(-1 == (recbytes = read(cfd,buffer,1024)))
{
????printf("read data fail !rn");
????return -1;
}
printf("read okrnREC:rn");
buffer[recbytes]='