/* 1 */
/* 创建套接字 socket() */
#include
#include
int socket(int domain, int type, int protocol);
/* 2 */
/* 套接字地址,每个套接字域都有其自己的地址格式 sockaddr */
/* AF_UNIX 域地址格式 */
struct sockaddr_un {
sa_family_t sun_family;
char sun_path[];
};
/* AF_INET 域地址格式 */
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
};
/* in_addr 结构体 */
struct in_addr {
unsigned long int s_addr;
};
/* 3 */
/* 命名套接字 bind() */
/* AF_UNIX 域套接字会关联到一个文件系统的路径名 */
/* AF_INET 套接字会关联到一个 IP 端口号 */
/* bind 系统调用把参数 address 中的地址分配给与文件描述符 socket 关联的未命名套接字 */
#include
int bind(int socket, const struct sockaddr *address, size_t address_len);
/* 4 */
/* 创建套接字队列,创建一个队列来保存未处理的请求 */
#include
int listen(int socket, int backlog);
/* 5 */
/* 接受连接 */
#include
int accept(int socket, struct sockaddr *address, size_t *address_len);
/* 6 */
/* 请求连接 */
#include
int connect(int socket, const struct sockaddr *address, size_t address_len);
/* 7 */
/* 关闭套接字 */
int close(int fd);
名称 | 说明 |
---|---|
AF_UNIX | UNIX 域协议(文件系统套接字) (常用) |
AF_INET | ARPA 因特网协议( UNIX 网络套接字) (常用) |
AF_ISO | ISO 标准协议 |
AF_NS | 施乐(Xerox)网络系统协议 |
AF_IPX | Novell IPX 协议 |
AF_APPLETALK | Appletalk DDS |
/* sc1.c */
#include
#include
#include
#include
#include
#include
#include
#include
void main(){
int sockfd;
int len;
struct sockaddr_un address;
int res;
char ch = 'A';
// 新建一个套接字
assert((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1);
address.sun_family = AF_UNIX; // UNIX 域套接字
strcpy(address.sun_path, "server_socket");
len = sizeof(address);
// 连接到服务器
assert((res = connect(sockfd, (struct sockaddr *)&address, len)) == 0);
assert(write(sockfd, &ch, 1) == 1); // 向服务器发送一个字节的数据
assert(read(sockfd, &ch, 1) == 1); // 读取服务器端的响应
fprintf(stdout, "client received: %c\n", ch);
}
/* ss1.c */
#include
#include
#include
#include
#include
#include
#include
#include
#include
void *thread_handle_client_conn(void *arg);
void main(){
int server_sock_fd, client_sock_fd;
int server_len, client_len;
struct sockaddr_un server_address;
struct sockaddr_un client_address;
char ch;
unlink("server_socket");
// 新建一个套接字
assert((server_sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1);
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "server_socket");
server_len = sizeof(server_address);
// 命名套接字
assert(bind(server_sock_fd, (struct sockaddr *)&server_address, server_len) == 0);
// 创建套接字队列
assert(listen(server_sock_fd, 5) == 0);
while(1){
fprintf(stdout, "server waiting for client ...\n");
client_len = sizeof(client_address);
// accept client conn(接受客户端连接)
assert((client_sock_fd = accept(server_sock_fd, (struct sockaddr *)&client_address, &client_len)) != -1);
// handle client conn(处理客户端连接)
//
// 方式 1, 服务器逐个处理客户端连接请求
// assert(read(client_sock_fd, &ch, 1) == 1); // 读取客户端发送来的数据
// fprintf(stdout, "server received: %c\n", ch);
// sleep(5);
// ch++;
// assert(write(client_sock_fd, &ch, 1) == 1); // 写入数据,响应客户端
// close(client_sock_fd);
//
// 方式 2, 每个客户端连接使用一个单独线程处理,
// 使用下面 2 行取代 ‘int i = client_sock_fd;’语句,
// 当涉及多线程编程时,传递指针要尤其注意。
int *i = (int *)malloc(sizeof(int));
*i = client_sock_fd;
pthread_t thread_client;
assert(pthread_create(&thread_client, NULL, thread_handle_client_conn, i) == 0);
}
}
void *thread_handle_client_conn(void *arg){
char ch;
int client_sock_fd = *((int *)arg);
assert(read(client_sock_fd, &ch, 1) == 1); // 读取客户端发送来的数据
fprintf(stdout, "server received: %c\n", ch);
sleep(5);
ch++;
assert(write(client_sock_fd, &ch, 1) == 1); // 写入数据,响应客户端
close(client_sock_fd);
free(arg); // 释放 malloc 分配的内存资源
pthread_exit(NULL);
}
编译,并运行服务器程序:
ubuntu@cuname:~/dev/beginning-linux-programming/test$ gcc -o ss1.1 ss1.1.c -lpthread
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./ss1.1
server waiting for client ...
同时启动多个客户端(简单 shell 脚本 sc1_many.sh):
#!/bin/bash
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
do
./sc1 &
done
exit 0
输出结果(执行脚本后,等待 5 秒,同时输出服务器返回结果):
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./sc1_many.sh
ubuntu@cuname:~/dev/beginning-linux-programming/test$ client received: B
client received: B
client received: B
...
client received: B
client received: B
/* sc2.c */
#include
#include
#include
#include
#include
#include
#include
#include
void main(){
int sockfd;
int len;
struct sockaddr_un address;
int res;
char ch = 'A';
assert((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1);
address.sun_family = AF_UNIX;
strcpy(address.sun_path, "server_socket");
len = sizeof(address);
assert((res = connect(sockfd, (struct sockaddr *)&address, len)) == 0);
sleep(5); // 先睡眠 5 秒
assert(write(sockfd, &ch, 1) == 1);
assert(read(sockfd, &ch, 1) == 1);
fprintf(stdout, "client received: %c\n", ch);
}
/* ss2.c */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// 最大客户端连接数目
#define MAX_CLIENT_NUM 500
void main(){
int server_sock_fd, client_sock_fd;
int server_len, client_len;
struct sockaddr_un server_address;
struct sockaddr_un client_address;
char ch;
int res;
// 查看 man 手册 ‘man select’
// 该变量用于 select 函数调用的第一个参数,含义如下:
// nfds is the highest-numbered file descriptor in any of the three sets, plus 1.
int max_fd_plus_one;
// 简单用于保存客户端连接的 socket 描述符
int fd_clients[MAX_CLIENT_NUM];
// init,使用 -1 初始化数组中的每一个元素
for(int i = 0;i < MAX_CLIENT_NUM;i++){
fd_clients[i] = -1;
}
// 统计客户端数目,只增不减,必须注意数组下标越界!
int fd_clients_count = 0;
fd_set readfds;
FD_ZERO(&readfds);
unlink("server_socket");
assert((server_sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1);
max_fd_plus_one = server_sock_fd + 1; // 初始化
FD_SET(server_sock_fd, &readfds); // 监听服务器套接字
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "server_socket");
server_len = sizeof(server_address);
assert(bind(server_sock_fd, (struct sockaddr *)&server_address, server_len) == 0);
assert(listen(server_sock_fd, 5) == 0);
fprintf(stdout, "server waiting for client...\n");
while(1){
switch(res = select(max_fd_plus_one, &readfds, NULL, NULL, NULL)){
case 0:
continue;
case -1:
// error
fprintf(stdout, "error: select failed.\n");
exit(EXIT_FAILURE);
default:
if(FD_ISSET(server_sock_fd, &readfds)){
// server fd
// 无阻塞调用 accept 函数,获取客户端连接
client_len = sizeof(client_address);
assert((client_sock_fd = accept(server_sock_fd, (struct sockaddr *)&client_address, &client_len)) != -1);
// 更新
max_fd_plus_one = (client_sock_fd > max_fd_plus_one ? client_sock_fd + 1 : max_fd_plus_one);
// 添加客户端 socket 描述符到 fd_clients 数组
fd_clients[fd_clients_count++] = client_sock_fd;
}else{
// retry add server fd
// 将服务器端连接描述符再次添加到 readfds 中,供 select 函数监听可读操作
FD_SET(server_sock_fd, &readfds);
}
// handle client
for(int i = 0;i < fd_clients_count;i++){
int c_fd = fd_clients[i];
if (c_fd != -1 && FD_ISSET(c_fd, &readfds)){
assert(read(c_fd, &ch, 1) == 1);
fprintf(stdout, "server received: %c, max_client_fd:%d\n", ch, max_fd_plus_one - 1);
ch++;
assert(write(c_fd, &ch, 1) == 1);
// 移除已经处理完毕的描述符
FD_CLR(c_fd, &readfds);
// remove c_fd from fd_clients
// 客户端请求处理完毕后,将其从 fd_clients 中移除
for(int k = 0;k < fd_clients_count;k++){
if(fd_clients[k] == c_fd){
fd_clients[k] = -1;
break;
}
}
close(c_fd);
}
}
// 将客户端连接描述符再次添加到 readfds 中,供 select 函数监听可读操作
// 一定要避免添加无效的描述符
// 准备继续下一次监听
for(int j = 0;j < fd_clients_count;j++){
if(fd_clients[j] != -1 && !FD_ISSET(fd_clients[j], &readfds)){
FD_SET(fd_clients[j], &readfds);
}
}
}
}
}
多客户端脚本(sc2_many.sh):
#!/bin/bash
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
do
./sc2 &
done
exit 0
执行情况:
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ps ax -O wchan:22 | grep sc2
39271 unix_stream_read_gener S pts/19 00:00:00 ./sc2
39351 pipe_wait S pts/19 00:00:00 grep --color=auto sc2
/* ss3.c */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// 最大客户端连接数目
#define MAX_CLIENT_NUM 1000
void main(){
int server_sock_fd, client_sock_fd;
int server_len, client_len;
struct sockaddr_un server_address;
struct sockaddr_un client_address;
char ch;
int res;
// 查看 man 手册 ‘man select’
// 该变量用于 select 函数调用的第一个参数,含义如下:
// nfds is the highest-numbered file descriptor in any of the three sets, plus 1.
int max_fd_plus_one;
// 简单用于保存客户端连接的 socket 描述符
int fd_clients_read[MAX_CLIENT_NUM];
int fd_clients_write[MAX_CLIENT_NUM];
// init,使用 -1 初始化数组中的每一个元素
for(int i = 0;i < MAX_CLIENT_NUM;i++){
fd_clients_read[i] = -1;
fd_clients_write[i] = -1;
}
// 统计客户端数目,只增不减,必须注意数组下标越界!
int fd_clients_count_read = 0;
int fd_clients_count_write = 0;
fd_set readfds;
fd_set writefds;
FD_ZERO(&writefds);
FD_ZERO(&readfds);
unlink("server_socket");
assert((server_sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1);
max_fd_plus_one = server_sock_fd + 1; // 初始化
FD_SET(server_sock_fd, &readfds); // 监听服务器套接字
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "server_socket");
server_len = sizeof(server_address);
assert(bind(server_sock_fd, (struct sockaddr *)&server_address, server_len) == 0);
assert(listen(server_sock_fd, 5) == 0);
fprintf(stdout, "server waiting for client...\n");
while(1){
switch(res = select(max_fd_plus_one, &readfds, &writefds, NULL, NULL)){
case 0:
continue;
case -1:
// error
fprintf(stdout, "error: select failed.\n");
exit(EXIT_FAILURE);
default:
if(FD_ISSET(server_sock_fd, &readfds)){
// server fd
// 无阻塞调用 accept 函数,获取客户端连接
client_len = sizeof(client_address);
assert((client_sock_fd = accept(server_sock_fd, (struct sockaddr *)&client_address, &client_len)) != -1);
// 更新
max_fd_plus_one = (client_sock_fd > max_fd_plus_one ? client_sock_fd + 1 : max_fd_plus_one);
// 添加客户端 socket 描述符到 fd_clients 数组
fd_clients_read[fd_clients_count_read++] = client_sock_fd;
}else{
// retry add server fd
// 将服务器端连接描述符再次添加到 readfds 中,供 select 函数监听可读操作
FD_SET(server_sock_fd, &readfds);
}
// handle client for read
for(int i = 0;i < fd_clients_count_read;i++){
int c_fd = fd_clients_read[i];
if (c_fd != -1 && FD_ISSET(c_fd, &readfds)){
// read from client
assert(read(c_fd, &ch, 1) == 1);
fprintf(stdout, "server received: %c, max_client_fd:%d\n", ch, max_fd_plus_one - 1);
// 移除已经处理完毕的描述符
FD_CLR(c_fd, &readfds);
// remove c_fd from fd_clients
// 客户端请求处理完毕后,将其从 fd_clients 中移除
for (int k = 0; k < fd_clients_count_read; k++){
if (fd_clients_read[k] == c_fd){
fd_clients_read[k] = -1;
break;
}
}
// for write
fd_clients_write[fd_clients_count_write++] = c_fd;
}
}
// handle client for write
for(int k = 0;k < fd_clients_count_write;k++){
int c_fd = fd_clients_write[k];
if(c_fd != -1 && FD_ISSET(c_fd, &writefds)){
// write to client
assert(write(c_fd, "B", 1) == 1);
// 移除已经处理完毕的描述符
FD_CLR(c_fd, &writefds);
// remove c_fd from fd_clients
// 客户端请求处理完毕后,将其从 fd_clients 中移除
for (int k = 0; k < fd_clients_count_write; k++){
if (fd_clients_write[k] == c_fd){
fd_clients_write[k] = -1;
break;
}
}
close(c_fd); // client is over.
}
}
// 将客户端连接描述符再次添加到 readfds 中,供 select 函数监听可读操作
// 一定要避免添加无效的描述符
// 准备继续下一次监听
for(int j = 0;j < fd_clients_count_read;j++){
if(fd_clients_read[j] != -1 && !FD_ISSET(fd_clients_read[j], &readfds)){
FD_SET(fd_clients_read[j], &readfds);
}
}
for(int j = 0;j < fd_clients_count_write;j++){
if(fd_clients_write[j] != -1 && !FD_ISSET(fd_clients_write[j], &writefds)){
FD_SET(fd_clients_write[j], &writefds);
}
}
}
}
}