... int sockfd; fd_set fdR; struct timeval timeout = ..; ... for(;;) { FD_ZERO(&fdR); FD_SET(sockfd, &fdR); switch (select(sockfd + 1, &fdR, NULL, &timeout)) { case -1: error handled by u; case 0: timeout hanled by u; default: if (FD_ISSET(sockfd)) { now u read or recv something; /* if sockfd is father and server socket, u can now accept() */ } } }
#define PORTNUM 50000 /* random port number, we need something */
void fireman(void); void do_something(int); main() { int s, t; if ((s= establish(PORTNUM)) < 0) { /* plug in the phone */ perror("establish"); exit(1); } signal(SIGCHLD, fireman); /* this eliminates zombies */ for (;;) { /* loop for phone calls */ if ((t= get_connection(s)) < 0) { /* get a connection */ if (errno == EINTR) /* EINTR might happen on accept(), */ continue; /* try again */ perror("accept"); /* bad */ exit(1); } switch(fork()) { /* try to handle connection */ case -1 : /* bad news. scream and die */ perror("fork"); close(s); close(t); exit(1); case 0 : /* we're the child, do something */ close(s); do_something(t); exit(0); default : /* we're the parent so look for */ close(t); /* another connection */ continue; } } } /* as children die we should get catch their returns or else we get * zombies, A Bad Thing. fireman() catches falling children. */ void fireman(void) { while (waitpid(-1, NULL, WNOHANG) > 0) ; } /* this is the function that plays with the socket. it will be called * after getting a connection. */ void do_something(int s) { /* do your thing with the socket here : : */ } 拨号 (如何调用 socket) 现在你应该知道如何建立 socket 来接受调用了。那么如何调用呢?和电话一样,你要先有个电话。用 socket() 函数来完成这件事情,就象建立侦听的 socket 一样。 在给 socket 地址后,你可以用 connect() 函数来连接侦听的 socket 了。下面是一段代码。 int call_socket(char *hostname, unsigned short portnum) { struct sockaddr_in sa; struct hostent *hp; int a, s; if ((hp= gethostbyname(hostname)) == NULL) { /* do we know the host's */ errno= ECONNREFUSED; /* address? */ return(-1); /* no */ } memset(&sa,0,sizeof(sa)); memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length); /* set address */ sa.sin_family= hp->h_addrtype; sa.sin_port= htons((u_short)portnum); if ((s= socket(hp->h_addrtype,SOCK_STREAM,0)) < 0) /* get socket */ return(-1); if (connect(s,&sa,sizeof sa) < 0) { /* connect */ close(s); return(-1); } return(s); }
这个函数返回一个可以流过数据的 socket 。 谈话(如何通过 sockets 交谈) 好了,你在要传输数据的双方建立连接了,现在该传输数据了。read() 和 write() 函数来处理吧。除了在 socket 读写和文件读写中的一个区别外,和处理一般的文件一样。区别是你一般不能得到你所要的数目的数据。所以你要一直循环到你需要的数据的到来。 一个简单的例子:将一定的数据读到缓存。 int read_data(int s, /* connected socket */ char *buf, /* pointer to the buffer */ int n /* number of characters (bytes) we want */ ) { int bcount; /* counts bytes read */ int br; /* bytes read this pass */ bcount= 0; br= 0; while (bcount < n) { /* loop until full buffer */ if ((br= read(s,buf,n-bcount)) > 0) { bcount += br; /* increment byte counter */ buf += br; /* move buffer ptr for next read */ } else if (br < 0) /* signal an error to the caller */ return(-1); } return(bcount); } 相同的函数也可以写数据,留给我们的读者吧。
NAME socket - create an endpoint for communication
SYNOPSIS #include /* See NOTES */ #include
int socket(int domain, int type, int protocol);
DESCRIPTION socket() creates an endpoint for communication and returns a descrip- tor.
协议簇domain
Name Purpose Man page PF_UNIX, PF_LOCAL Local communication unix(7) PF_INET IPv4 Internet protocols ip(7) PF_INET6 IPv6 Internet protocols ipv6(7) PF_IPX IPX - Novell protocols PF_NETLINK Kernel user interface device netlink(7) PF_X25 ITU-T X.25 / ISO-8208 protocol x25(7) PF_AX25 Amateur radio AX.25 protocol PF_ATMPVC Access to raw ATM PVCs PF_APPLETALK Appletalk ddp(7) PF_PACKET Low level packet interface packet(7)
type类型(常用)
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be sup- ported. SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
SOCK_RAW Provides raw network protocol access.
协议protocol The protocol specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol family, in which case protocol can be specified as 0. However, it is possible that many protocols may exist, in which case a particular protocol must be specified in this manner. The protocol number to use is specific to the “communication domain” in which communication is to take place; see protocols(5). See getprotoent(3) on how to map protocol name strings to protocol numbers. 一般设置为零