下面介绍程序中用到的 socket API,这些函数都在
- /* server.c */
- #include < stdio.h > #include < stdlib.h > #include < string.h > #include < unistd.h > #include < sys / socket.h > #include < netinet / in.h > #define MAXLINE 80#define SERV_PORT 8000 int main(void) {
- struct sockaddr_in servaddr,
- cliaddr;
- socklen_t cliaddr_len;
- int listenfd,
- connfd;
- char buf[MAXLINE];
- char str[INET_ADDRSTRLEN];
- int i,
- n;
- listenfd = socket(AF_INET, SOCK_STREAM, 0);
- bzero( & servaddr, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
- servaddr.sin_port = htons(SERV_PORT);
- bind(listenfd, (struct sockaddr * ) & servaddr, sizeof(servaddr));
- listen(listenfd, 20);
- printf("Accepting connections ...\n");
- while (1) {
- cliaddr_len = sizeof(cliaddr);
- connfd = accept(listenfd, (struct sockaddr * ) & cliaddr, &cliaddr_len);
- n = read(connfd, buf, MAXLINE);
- printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));
- for (i = 0; i < n; i++) buf[i] = toupper(buf[i]);
- write(connfd, buf, n);
- close(connfd);
- }
- }
中。
- sys/socket.h
socket() 打开一个网络通讯端口,如果成功的话,就像 open() 一样返回一个文件描述符,应用程序可以像读写文件一样用 read/write 在网络上收发数据,如果 socket() 调用出错则返回 - 1。对于 IPv4,family 参数指定为 AF_INET。对于 TCP 协议,type 参数指定为 SOCK_STREAM,表示面向流的传输协议。如果是 UDP 协议,则 type 参数指定为 SOCK_DGRAM,表示面向数据报的传输协议。protocol 参数的介绍从略,指定为 0 即可。
- int socket(int family, int type, int protocol);
服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,因此服务器需要调用 bind 绑定一个固定的网络地址和端口号。bind() 成功返回 0,失败返回 - 1。
- int bind(int sockfd, const struct sockaddr * myaddr, socklen_t addrlen);
首先将整个结构体清零,然后设置地址类型为 AF_INET,网络地址为 INADDR_ANY,这个宏表示本地的任意 IP 地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个 IP 地址,这样设置可以在所有的 IP 地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个 IP 地址,端口号为 SERV_PORT,我们定义为 8000。
- bzero( & servaddr, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
- servaddr.sin_port = htons(SERV_PORT);
典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的 accept() 返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未 accept 的客户端就处于连接等待状态,listen() 声明 sockfd 处于监听状态,并且最多允许有 backlog 个客户端处于连接待状态,如果接收到更多的连接请求就忽略。listen() 成功返回 0,失败返回 - 1。
- int listen(int sockfd, int backlog);
三方握手完成后,服务器调用 accept() 接受连接,如果服务器调用 accept() 时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来。cliaddr 是一个传出参数,accept() 返回时传出客户端的地址和端口号。addrlen 参数是一个传入传出参数(value-result argument),传入的是调用者提供的缓冲区 cliaddr 的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区)。如果给 cliaddr 参数传 NULL,表示不关心客户端的地址。
- int accept(int sockfd, struct sockaddr * cliaddr, socklen_t * addrlen);
整个是一个 while 死循环,每次循环处理一个客户端连接。由于 cliaddr_len 是传入传出参数,每次调用 accept() 之前应该重新赋初值。accept() 的参数 listenfd 是先前的监听文件描述符,而 accept() 的返回值是另外一个文件描述符 connfd,之后与客户端之间就通过这个 connfd 通讯,最后关闭 connfd 断开连接,而不关闭 listenfd,再次回到循环开头 listenfd 仍然用作 accept 的参数。accept() 成功返回一个文件描述符,出错返回 - 1。
- while (1) {
- cliaddr_len = sizeof(cliaddr);
- connfd = accept(listenfd, (struct sockaddr * ) & cliaddr, &cliaddr_len);
- n = read(connfd, buf, MAXLINE);...close(connfd);
- }
- /* client.c */
- #include < stdio.h > #include < stdlib.h > #include < string.h > #include < unistd.h > #include < sys / socket.h > #include < netinet / in.h > #define MAXLINE 80#define SERV_PORT 8000 int main(int argc, char * argv[]) {
- struct sockaddr_in servaddr;
- char buf[MAXLINE];
- int sockfd,
- n;
- char * str;
- if (argc != 2) {
- fputs("usage: ./client message\n", stderr);
- exit(1);
- }
- str = argv[1];
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- bzero( & servaddr, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
- servaddr.sin_port = htons(SERV_PORT);
- connect(sockfd, (struct sockaddr * ) & servaddr, sizeof(servaddr));
- write(sockfd, str, strlen(str));
- n = read(sockfd, buf, MAXLINE);
- printf("Response from server:\n");
- write(STDOUT_FILENO, buf, n);
- close(sockfd);
- return 0;
- }
由于客户端不需要固定的端口号,因此不必调用 bind(),客户端的端口号由内核自动分配。注意,客户端不是不允许调用 bind(),只是没有必要调用 bind() 固定一个端口号,服务器也不是必须调用 bind(),但如果服务器不调用 bind(),内核会自动给服务器分配监听端口,每次启动服务器时端口号都不一样,客户端要连接服务器就会遇到麻烦。
客户端需要调用 connect() 连接服务器,connect 和 bind 的参数形式一致,区别在于 bind 的参数是自己的地址,而 connect 的参数是对方的地址。connect() 成功返回 0,出错返回 - 1。
- int connect(int sockfd, const struct sockaddr * servaddr, socklen_t addrlen);
来源: http://lib.csdn.net/article/embeddeddevelopment/41973