Subject: Advanced I/O Function
1: There are three ways to set timeout(block):
First,signal driver: relying on the signal handle function of signal SIGALRM to generate a signal SIGINT to the blocked funtion, suck as connect, recv, read etc. Then the blocked function will return and get a EINTR error. For example:
fuction prototype:
int select(int fd, fd_set* read_set, fd_set* write_set, fd_set* except_set, struct timeval tv)
There are also four macro two handle the structure of fd_set, they are:
1. FD_ZERO(fd_set*) // always initial the fd_set 2. FD_SET(int fd, fd_set*) // add the fd to the fd_set 3. FD_ISSET(int fd, fd_set*) // check if the fd is int the fd_set 4. FD_CLR(int fd, fd_set*) // clean the fd from the fd_set
Because the fd_set is a structure of int array, so you can assign a fd_set to another
The last arguement of select implement the functon of setting the timeout, it can attain to a precision of millisecond.
The reture value of select: -1 on an error, 0 if a timeout occurs, or a positive value specifying the number of ready descriptors.
Last, seting the SO_SNDTIMEO and SO_RCVTIMEO option of setsockopt function. Autovividly, this just impact on the socket descriptor. For example:
1. struct timeval tv; 2. tv.tv_sec = 10; tv.tv_usec = 0; 3. setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); 4. // this will block the socket sockfd at most 10 seconds in reading.
2: The flag of function of send and recv
These two functions are similar to the standard read and write, other than the last flag argument.
These flags are:
MSG_DONTROUTE: tell the kernel not to perform a lookup of the routing table.(only send)
MSG_DONTWAITE: tell the kernel perform the read or write function once the buffer not empty.
MSG_OOB: tell the kernel there are out_of_band data int the datagram(truely one byte,tcp head URG option).
MSG_PEEK: (only recv) authenticate the header before receiving the data int the buffer.
MSG_WAITALL: tell the kernel not to return from a read function until the requested number of bytes have been read.(I think this flag relates with the SO_RCVLOWAT socket option ).
MSG_PEEK is not a good flag for tcp since the data size is not specified.
3: Scatter read and gather write: readv and writev
1. #include <sys/uio.h> 2. /* fd can be any descriptor */ 3. ssize_t readv(int fd, const struct iovec* iov, int iovcnt); 4. ssize_t writev(int fd, const struct iovec* iov, int iovcnt); // This is an atomic operation 5. struct iovec { 6. void* iov_base; // starting address of buffer 7. size_t iov_len; // size of buffer 8. }
From the structure, we can see these two function are not type-specified. They are operation just on memory.
There is some limit to the number of elements int array of iovec structures. POSIX requires that the constant IOV_MAX be defined and that its value be at least 16.
4: recvmsg and sendmsg functions(#include <sys/socket.h>)
Various input or output functions could be replaced with calls to these two functions.
1. ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags); // msg is a pointer, therefore, value-result passing 2. ssize_t sendmsg(int sockfd, struct msghdr* msg, int flags); // flages similar to the send and recv function 3. struct msghdr { 4. void* msg_name; // protocol address(struct sockaddr(_in|_in6|_un|_storage)?/*) : regex, haha 5. socklen_t msg_namelen; // size of protocol address, normaly 16 6. struct iovec* msg_iov; // scatter/gather array ( writev, readv) 7. int msg_iovlen; // elements int msg_iov(number, not size) 8. void* msg_control; // ancillary data(struct cmsghdr), specifying the location 9. socklen_t msg_controllen; // length of ancillary data(bytes), specifying the size 10. int msg_falgs; // only used by recvmsg,return value is used by kernel to drive its receive processing 11. }
The return msg_flags value including MSG_OOB, MSG_EOR, MSG_BCAST, MSG_MCAST, MSG_NOTIFICATION and so forth.
MSG_EOR: at least one error occurs.
MSG_BCAST: broadcast datagram.
MSG_TRUNC: the datagram was truncated, there are more data than iovlen indicated
MSG_CTRUNC: the ancillary data was truncate, there are more data than controllen indicated
MSG_MCAST: multicast datagram.
MSG_NOTIFICATION: only for SCTP, an event notification
Ancillary Data:
1. struct cmsghdr { 2. socklen_t cmsg_len; // length in bytes, including this structrue 3. int cmsg_level; // originating protocol 4. int cmsg_type; // protocol-specific type 5. /* note: followed by unsigned char cmsg_data[], not show, but exist, just like a memory gap. This is really strange, but not affect comprehension I think. */ 6. };
Msg_control points to the first ancillary data object, and the total length of the ancillary data is specified by msg_controllen. Each object is preceded by a cmsghdr structure that describe the object. There are paddings surely.
The ancillary data returned by recvmsg can contain any number of ancillary data objects.
The macros processing the ancillary data:
These macros would be used in the following pseudocode:
for ( cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr) ) { if ( cmsgptr->cmsg_level == ... && cmsgptr->cmsg_type == ... ) { u_char* ptr = CMSG_DATA(cmsgptr); ... } }
5: Sockets and StandardI/O
We can use fdopen(sockfd, flag) to make a socket become a standart I/O stream, and then use the standard I/O function. But notice: wo should not use fp = fdopen(sockfd, "r/+|w/+"), since we could lose the control of the I/O somehow and eventually lead to unpredictable error. The best way is:
fpin = fdopen(sockfd,"r") then fpout = fdopen(sockfd, "w").
This is a full-duplex way while the TCP and UDP socket are duplex.
There is also a buffering problem when we use the standard I/O to communicate with the other side because of standard I/O's buffer. This pushes the input data into the buffer but not write until the buffer is full or enforced to write(fflush(fp) or EOF is encountered(FIN is sent, then exit not _exit is called, the latter will lose all data in the buffer)).
Ther are three types of buffering performed by the standard I/O library: fully buffered(stdout, stdin), line buffered(stream refer to terminal device) and unbuffered(stderr). We can use the function setvbuf to set these options(see also setbuf, setbuffer, setlinebug). BEST not using the standard I/O for sockets.