上面的服务器端程序执行典型的 4 个步骤来准备回应客户端的请求,然后接受其他的独立请求。这里每一个步骤都以服务器端程序调用的系统函数来命名。
socket(…) :为套接字连接获取一个文件描述符
bind(…) :将套接字和服务器主机上的一个地址进行绑定
listen(…) :监听客户端请求
accept(…) :接受一个特定的客户端请求
上面的 socket 调用的完整形式为:
int sockfd = socket(AF_INET, /* versus AF_LOCAL */ SOCK_STREAM, /* reliable, bidirectional */ 0); /* system picks protocol (TCP) */
第一个参数特别指定了使用的是一个网络套接字,而不是 IPC 套接字。对于第二个参数有多种选项,但 SOCK_STREAM 和 SOCK_DGRAM (数据报)是最为常用的。基于流的套接字支持可信通道,在这种通道中如果发生了信息的丢失或者更改,都将会被报告。这种通道是双向的,并且从一端到另外一端的有效载荷在大小上可以是任意的。相反的,基于数据报的套接字大多是不可信的,没有方向性,并且需要固定大小的载荷。socket 的第三个参数特别指定了协议。对于这里展示的基于流的套接字,只有一种协议选择:TCP,在这里表示的 0 。因为对 socket 的一次成功调用将返回相似的文件描述符,套接字可以被读写,对应的语法和读写一个本地文件是类似的。
对 bind 的调用是最为复杂的,因为它反映出了在套接字 API 方面上的各种改进。我们感兴趣的点是这个调用将一个套接字和服务器端所在机器中的一个内存地址进行绑定。但对 listen 的调用就非常直接了:
if (listen(fd, MaxConnects) < 0)
第一个参数是套接字的文件描述符,第二个参数则指定了在服务器端处理一个拒绝连接错误之前,有多少个客户端连接被允许连接。(在头文件 sock.h 中 MaxConnects 的值被设置为 8 。)
accept 调用默认将是一个阻塞等待:服务器端将不做任何事情直到一个客户端尝试连接它,然后进行处理。accept 函数返回的值如果是 -1 则暗示有错误发生。假如这个调用是成功的,则它将返回另一个文件描述符,这个文件描述符被用来指代另一个可读可写的套接字,它与 accept 调用中的第一个参数对应的接收套接字有所不同。服务器端使用这个可读可写的套接字来从客户端读取请求然后写回它的回应。接收套接字只被用于接受客户端的连接。
在设计上,服务器端可以一直运行下去。当然服务器端可以通过在命令行中使用 Ctrl+C 来终止它。
示例 2. 使用套接字的客户端
#include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <netdb.h> #include "sock.h" -
const char* books[] = {"War and Peace", "Pride and Prejudice", "The Sound and the Fury"}; -
void report(const char* msg, int terminate) { perror(msg); if (terminate) exit(-1); /* failure */ } -
int main() { /* fd for the socket */ int sockfd = socket(AF_INET, /* versus AF_LOCAL */ SOCK_STREAM, /* reliable, bidirectional */ 0); /* system picks protocol (TCP) */ if (sockfd < 0) report("socket", 1); /* terminate */ -
/* get the address of the host */ struct hostent* hptr = gethostbyname(Host); /* localhost: 127.0.0.1 */ if (!hptr) report("gethostbyname", 1); /* is hptr NULL? */ if (hptr->h_addrtype != AF_INET) /* versus AF_LOCAL */ report("bad address family", 1); /* connect to the server: configure server's address 1st */ struct sockaddr_in saddr; memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = ((struct in_addr*) hptr->h_addr_list[0])->s_addr; saddr.sin_port = htons(PortNumber); /* port number in big-endian */ if (connect(sockfd, (struct sockaddr*) &saddr, sizeof(saddr)) < 0) report("connect", 1); /* Write some stuff and read the echoes. */ puts("Connect to server, about to write some stuff..."); int i; for (i = 0; i < ConversationLen; i++) { if (write(sockfd, books[i], strlen(books[i])) > 0) { /* get confirmation echoed from server and print */ char buffer[BuffSize + 1]; memset(buffer, '\0', sizeof(buffer)); if (read(sockfd, buffer, sizeof(buffer)) > 0) puts(buffer); } } puts("Client done, about to exit..."); close(sockfd); /* close the connection */ return 0; }
(编辑:ASP站长网)
|