一旦,我們建立好了tcp連接之后,我們就可以把得到的fd當(dāng)作文件描述符來(lái)使用。 由此網(wǎng)絡(luò)程序里最基本的函數(shù)就是read和write函數(shù)了。 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ù). 這樣我們用一個(gè)while循環(huán)來(lái)不停的寫入,但是循環(huán)過(guò)程中的buf參數(shù)和nbyte參數(shù)得由我們來(lái)更新。也就是說(shuō),網(wǎng)絡(luò)寫函數(shù)是不負(fù)責(zé)將全部數(shù)據(jù)寫完之后在返回的。 2)返回的值小于0,此時(shí)出現(xiàn)了錯(cuò)誤.我們要根據(jù)錯(cuò)誤類型來(lái)處理. 如果錯(cuò)誤為EINTR表示在寫的時(shí)候出現(xiàn)了中斷錯(cuò)誤. 如果為EPIPE表示網(wǎng)絡(luò)連接出現(xiàn)了問(wèn)題(對(duì)方已經(jīng)關(guān)閉了連接). 為了處理以上的情況,我們自己編寫一個(gè)寫函數(shù)來(lái)處理這幾種情況. int my_write(int fd,void *buffer,int length) { int bytes_left; int written_bytes; char *ptr; ptr=buffer; bytes_left=length; while(bytes_left>0) { } return(0); } ssize_t read(int fd,void *buf,size_t nbyte) read函數(shù)是負(fù)責(zé)從fd中讀取內(nèi)容.當(dāng)讀成功 時(shí),read返回實(shí)際所讀的字節(jié)數(shù),如果返回的值是0 表示已經(jīng)讀到文件的結(jié)束了,小于0表示出現(xiàn)了錯(cuò)誤.如果錯(cuò)誤為EINTR說(shuō)明讀是由中斷引起 的, 如果是ECONNREST表示網(wǎng)絡(luò)連接出了問(wèn)題. 和上面一樣,我們也寫一個(gè)自己的讀函數(shù). int my_read(int fd,void *buffer,int length) { int bytes_left; int bytes_read; char *ptr; bytes_left=length; while(bytes_left>0) { } return(length-bytes_left); } 數(shù)據(jù)的傳遞 有了上面的兩個(gè)函數(shù),我們就可以向客戶端或者是服務(wù)端傳遞數(shù)據(jù)了.比如我們要傳遞一個(gè)結(jié)構(gòu).可以使用如下方式 struct my_struct my_struct_client; write(fd,(void *)&my_struct_client,sizeof(struct my_struct); char buffer[sizeof(struct my_struct)]; struct *my_struct_server; read(fd,(void *)buffer,sizeof(struct my_struct)); my_struct_server=(struct my_struct *)buffer; 在網(wǎng)絡(luò)上傳遞數(shù)據(jù)時(shí)我們一般都是把數(shù)據(jù)轉(zhuǎn)化為char類型的數(shù)據(jù)傳遞.接收的時(shí)候也是一樣的注意的是我們沒有必要在網(wǎng)絡(luò)上傳遞指針(因?yàn)閭鬟f指針是沒有任何意義的,我們必須傳遞指針?biāo)赶虻膬?nèi)容) recv和send函數(shù)提供了和read和write差不多的功能.不過(guò)它們提供了第四個(gè)參數(shù)來(lái)控制讀寫操作. int recv(int sockfd,void *buf,int len,int flags) int send(int sockfd,void *buf,int len,int flags) 前面的三個(gè)參數(shù)和read,write一樣,第四個(gè)參數(shù)可以是0或者是以下的組合 _______________________________________________________________ | MSG_DONTROUTE | 不查找表 | | MSG_OOB | 接受或者發(fā)送帶外數(shù)據(jù) | | MSG_PEEK | 查看數(shù)據(jù),并不從系統(tǒng)緩沖區(qū)移走數(shù)據(jù) | | MSG_WAITALL | 等待所有數(shù)據(jù) | |--------------------------------------------------------------| MSG_DONTROUTE:是send函數(shù)使用的標(biāo)志.這個(gè)標(biāo)志告訴IP.目的主機(jī)在本地網(wǎng)絡(luò)上面,沒有必要查找表.這個(gè)標(biāo)志一般用網(wǎng)絡(luò)診斷和路由程序里面. MSG_OOB:表示可以接收和發(fā)送帶外的數(shù)據(jù).關(guān)于帶外數(shù)據(jù)我們以后會(huì)解釋的. MSG_PEEK:是recv函數(shù)的使用標(biāo)志,表示只是從系統(tǒng)緩沖區(qū)中讀取內(nèi)容,而不清除系統(tǒng)緩沖區(qū)的內(nèi)容.這樣下次讀的時(shí)候,仍然是一樣的內(nèi)容.一般在有多個(gè)進(jìn)程讀寫數(shù)據(jù)時(shí)可以使用這個(gè)標(biāo)志. MSG_WAITALL是recv函數(shù)的使用標(biāo)志,表示等到所有的信息到達(dá)時(shí)才返回.使用這個(gè)標(biāo)志的時(shí)候recv回一直阻塞,直到指定的條件滿足,或者是發(fā)生了錯(cuò)誤. 1)當(dāng)讀到了指定的字節(jié)時(shí),函數(shù)正常返回.返回值等于len 2)當(dāng)讀到了文件的結(jié)尾時(shí),函數(shù)正常返回.返回值小于len 3)當(dāng)操作發(fā)生錯(cuò)誤時(shí),返回-1,且設(shè)置錯(cuò)誤為相應(yīng)的錯(cuò)誤號(hào)(errno) MSG_NOSIGNAL is a flag used by send() in some implementations of the Berkeley sockets API. This flag requests that the implementation does not to send a SIGPIPE signal on errors on stream oriented sockets when the other end breaks the connection. The EPIPE error is still returned as normal. Though it is in some Berkely sockets APIs (notably Linux) it does not exist in what some refer to as the reference implementation, FreeBSD, which instead uses a socket option SO_NOSIGPIPE?. 對(duì)于服務(wù)器端,我們可以使用這個(gè)標(biāo)志。目的是不讓其發(fā)送SIG_PIPE信號(hào),導(dǎo)致程序退出。 如果flags為0,則和read,write一樣的操作.還有其它的幾個(gè)選項(xiàng),不過(guò)我們實(shí)際上用的很少,可以查看 Linux Programmer's Manual得到詳細(xì)解釋 |
|