#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 即可。








