Linux Socket 编程简介和实现

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

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.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 messagen", 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); // 由于客户端不需要固定的端口号,因此不必调用 bind(),客户端的端口号由内核自动分配。 // 注意,客户端不是不允许调用 bind(),只是没有必要调用 bind() 固定一个端口号, // 服务器也不是必须调用 bind(),但如果服务器不调用 bind(),内核会自动给服务器分配监听端口, // 每次启动服务器时端口号都不一样,客户端要连接服务器就会遇到麻烦。 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); printf("n"); close(sockfd); return 0; }

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

$ gcc client.c -o client

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

$ ./client hello

此时服务器端会收到请求并返回转换为大写的字符串,并输出相应的信息:

而客户端在发送请求后会收到转换过的字符串:

在客户端的代码中有两点需要注意:

1. 由于客户端不需要固定的端口号,因此不必调用 bind(),客户端的端口号由内核自动分配。
2. 客户端需要调用 connect() 连接服务器,connect 和 bind 的参数形式一致,区别在于 bind 的参数是自己的地址,而 connect 的参数是对方的地址。

至此我们已经使用 socket 技术完成了一个最简单的客户端服务器程序,虽然离实际应用还非常遥远,但就学习而言已经足够了。

提升服务器端的响应能力

虽然我们的服务器程序可以响应客户端的请求,但是这样的效率太低了。一般情况下服务器程序需要能够同时处理多个客户端的请求。可以通过 fork 系统调用创建子进程来处理每个请求,下面是大体的实现思路:

listenfd = socket(...); bind(listenfd, ...); listen(listenfd, ...); while (1) { connfd = accept(listenfd, ...); n = fork(); if (n == -1) { perror("call to fork"); exit(1); } else if (n == 0) { // 在子进程中处理客户端的请求。 close(listenfd); while (1) { read(connfd, ...); ... write(connfd, ...); } close(connfd); exit(0); } else { close(connfd); } }