一、基本socket函數(shù)
Linux系統(tǒng)是通過提供套接字(socket)來進(jìn)行網(wǎng)絡(luò)編程的。網(wǎng)絡(luò)的socket數(shù)據(jù)傳輸是一種特殊的I/O,socket也是一種文件描述符。socket也有一個(gè)類似于打
開文件的函數(shù):socket(),調(diào)用socket(),該函數(shù)返回一個(gè)整型的socket的描述符,隨后的連接建立、數(shù)據(jù)傳輸?shù)炔僮饕捕际峭ㄟ^該socket實(shí)現(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ù)報(bào)套接字、原始套接字
protocol通常賦值"0"。
兩個(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ù)
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是一個(gè)指向包含有本機(jī)IP地址及端口號(hào)等信息的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ù)請(qǐng)求。成功返回0,否則返回-1,并置errno。
參數(shù)說明:
???sock_fd 是socket函數(shù)返回的socket描述符;serv_addr是包含遠(yuǎn)端主機(jī)IP地址和端口號(hào)的指針;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指定在請(qǐng)求隊(duì)列中允許的最大請(qǐng)求數(shù)
5、accecpt函數(shù)
syntax:??
???int accept(int sock_fd, struct sockadd_in* addr, int addrlen);
功能說明:
???用于接受客戶端的服務(wù)請(qǐng)求,成功返回新的套接字描述符,失敗返回-1,并置errno。
參數(shù)說明:
???sock_fd是被監(jiān)聽的socket描述符,
???addr通常是一個(gè)指向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.成功時(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ò)誤類型來處理.
????????如果錯(cuò)誤為EINTR表示在寫的時(shí)候出現(xiàn)了中斷錯(cuò)誤.
????????如果錯(cuò)誤為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)讀成功時(shí),read返回實(shí)際所讀的字節(jié)數(shù),如果返回的值是0 表示已經(jīng)讀到文件的結(jié)束了,小于0表示出現(xiàn)了錯(cuò)誤.
????如果錯(cuò)誤為EINTR說明讀是由中斷引起的,
????如果錯(cuò)誤是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ù)運(yùn)行成功返回0,否則返回-1
二、socket編程的其他函數(shù)說明
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)換,從程序的可移植性要求來講,就算本機(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ù)自己來決定的。
2) 有關(guān)的轉(zhuǎn)換函數(shù)
* unsigned short int htons(unsigned short int hostshort):
主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,對(duì)無符號(hào)短型進(jìn)行操作4bytes
* unsigned long int htonl(unsigned long int hostlong):
主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,對(duì)無符號(hào)長型進(jìn)行操作8bytes
* unsigned short int ntohs(unsigned short int netshort):
網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機(jī)字節(jié)順序,對(duì)無符號(hào)短型進(jìn)行操作4bytes
* unsigned long int ntohl(unsigned long int netlong):
網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機(jī)字節(jié)順序,對(duì)無符號(hào)長型進(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(const char * cp):該函數(shù)把一個(gè)用數(shù)字和點(diǎn)表示的IP地址的字符串轉(zhuǎn)換成一個(gè)無符號(hào)長整型,如: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)制的無符號(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語言中的字符串是不同的。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個(gè)字節(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個(gè)字節(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個(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中。
二、程序說明
本使用tcp協(xié)議進(jìn)行通信,服務(wù)端進(jìn)行監(jiān)聽,在收到客戶端的連接后,發(fā)送數(shù)據(jù)給客戶端;客戶端在接受到數(shù)據(jù)后打印出來,然后關(guān)閉。
1、client.c
#include
#include
#include
#include
#include
#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]='