Linux Socket 编程简介和实现

2019-01-16 22:28:56丽君

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.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; // socket() 打开一个网络通讯端口,如果成功的话, // 就像 open() 一样返回一个文件描述符, // 应用程序可以像读写文件一样用 read/write 在网络上收发数据。 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 和 servaddr 绑定在一起, // 使 listenfd 这个用于网络通讯的文件描述符监听 servaddr 所描述的地址和端口号。 bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); // listen() 声明 listenfd 处于监听状态, // 并且最多允许有 20 个客户端处于连接待状态,如果接收到更多的连接请求就忽略。 listen(listenfd, 20); printf("Accepting connections ...n"); while (1) { cliaddr_len = sizeof(cliaddr); // 典型的服务器程序可以同时服务于多个客户端, // 当有客户端发起连接时,服务器调用的 accept() 返回并接受这个连接, // 如果有大量的客户端发起连接而服务器来不及处理,尚未 accept 的客户端就处于连接等待状态。 connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); n = read(connfd, buf, MAXLINE); printf("received from %s at PORT %dn", 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); } }

把上面的代码保存到文件 server.c 文件中,并执行下面的命令编译:

$ gcc server.c -o server

然后运行编译出来的 server 程序:

$ ./server

此时我们可以通过 ss 命令来查看主机上的端口监听情况:

如上图所示,server 程序已经开始监听主机的 8000 端口了。

下面让我们介绍一下这段程序中用到的 socket 相关的 API。

int socket(int family, int type, int protocol);

socket() 打开一个网络通讯端口,如果成功的话,就像 open() 一样返回一个文件描述符,应用程序可以像读写文件一样用 read/write 在网络上收发数据。对于IPv4,family 参数指定为 AF_INET。对于 TCP 协议,type 参数指定为 SOCK_STREAM,表示面向流的传输协议。如果是 UDP 协议,则 type 参数指定为 SOCK_DGRAM,表示面向数据报的传输协议。protocol 指定为 0 即可。