嵌入式Linux网络编程,TCP多并发服务器,TCP多线程并发服务器,TCP多进程并发服务器

2019-07-12 15:28发布

文章目录


在这里插入图片描述 TCP循环服务器模型 TCP多进程并发服务器 TCP多线程服务器 socket(...);
bind(...);
listen(...);
while(1){
  accept(...);
  process(...);
  close(...);
}
socket(...);
bind(...);
listen(...);
while(1){
  accpet(...);
  if(fork(...) == 0){
    process(...);
    close(...);
    exit(...);
  }
  close(...);
}
socket(...);
bind(...);
listen(...);
while(1){
  accpet(...);
  if((pthread_create(...))!==-1) {
    process(...);
    close(...);
    exit(...);
  }
  close(...)
}
TCP服务器一般很少用 TCP并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是由服务器创建一个子进程来处理。 多线程服务器是对多进程的服务器的改进

1,TCP多线程并发服务器

1.1,头文件net.h

#ifndef __NET_H__ #define __NET_H__ #include #include #include #include #include #include #include #include #include #define SERV_IP_ADDR "192.168.31.100" #define SERV_PORT 5005 #define BACKLOG 5 #define QUIT_STR "quite" #endif

1.2,客户端client.c

>## /* ./client serv_ip serv_port */ #include "net.h" void usage(char *s) { printf("Usage: %s ",s); printf(" serv_ip: server ip address "); printf(" serv_port: server port(>5000) "); } int main(int argc, const char *argv[]) { int fd; short port; struct sockaddr_in sin; if(argc != 3) { usage((char *)argv[0]); exit(1); } if((port = atoi(argv[2])) < 5000) { usage((char *)argv[0]); exit(1); } /* 1 创建socket fd */ if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0) { perror("socket"); exit(-1); } /* 2 连接服务器 */ /* 2.1 填充struct sockaddr_in结构体变量*/ bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(port);//转为网络字节序端口号 if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0) { perror("inet_pton"); goto _error1; } /* 2.2 连接服务器*/ if(connect(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0) { perror("connect"); goto _error1; } /* 3 读写*/ char buf[BUFSIZ]; while(1) { bzero(buf,BUFSIZ); if(fgets(buf,BUFSIZ-1,stdin) == NULL) { continue; } write(fd,buf,strlen(buf)); if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0) { printf("client is existing! "); break; } } _error1: close(fd); return 0; }

1.3,服务器端server.c

#include "net.h" #include /* 线程传参 */ typedef struct{ int addr;//客户端IP地址 int port;//客户端端口号 int fd;//为请求链接的客户端分配的新的socket fd }ARG; /* 子线程处理函数 */ void client_data_handle(void *arg); int main(int argc, const char *argv[]) { pthread_t tid;//子线程ID号 int fd; struct sockaddr_in sin;//如果是IPV6的编程,要使用struct sockddr_in6结构体(详细情况请参考man 7 ipv6),通常更通用的方法可以通过struct sockaddr_storage来编程 /* 1 创建socket fd */ if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0) { perror("socket"); exit(-1); } /* 优化 1 允许绑定地址快速重用 */ int b_reuse = 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int)); /* 2 绑定 */ /* 2.1 填充struct sockaddr_in 结构体变量*/ bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); #if 1 /* 优化 2 让服务器可以绑定在任意的IP上*/ sin.sin_addr.s_addr = htonl(INADDR_ANY); #else if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0) { perror("inet_pton"); goto _error1; } #endif /* 2.2 绑定*/ if(bind(fd,(struct sockaddr *)&sin,sizeof(sin))) { perror("bind"); goto _error1; } /* 3 使用listen()把主动套接字变成被动套接字 */ if(listen(fd,BACKLOG) < 0) { perror("listen"); goto _error1; } int newfd = -1; struct sockaddr_in cin; socklen_t cin_addr_len = sizeof(cin); /* 优化 4 用多进程/多线程处理已经建立好链接的客户端数据*/ while(1) { /* 4 阻塞等待客户端链接请求*/ /* 优化 3 通过函数获取刚建立链接的客户端的IP地址和端口号*/ if((newfd = accept(fd,(struct sockaddr *)&cin,&cin_addr_len)) < 0) { perror("connect"); goto _error1; } ARG arg; arg.addr = cin.sin_addr.s_addr; arg.port = ntohs(cin.sin_port); arg.fd = newfd; if(pthread_create(&tid,NULL,(void *)client_data_handle,(void *)&arg) != 0) { perror("pthread_create"); goto _errno2; } } _errno2: close(newfd); _error1: close(fd); return 0; } void client_data_handle(void *arg) { int ret = -1;//read()是个阻塞函数,要做读写错误的工程处理 char buf[BUFSIZ];//BUFSIZ是系统提供的 char cin_ipv4_addr[16]; ARG parg = *(ARG *)arg; if(inet_ntop(AF_INET,&parg.addr,cin_ipv4_addr,sizeof(cin_ipv4_addr)) < 0) { perror("inet_ntop"); exit(-1); } printf("client(:%s potr(:%d ",cin_ipv4_addr,parg.port); printf("the client pthread fd is %d ",parg.fd); while(1) { /* 5 读写*/ bzero(buf,BUFSIZ); do { ret = read(parg.fd,buf,BUFSIZ-1); }while(ret < 0 && errno == EINTR);//阻塞读写 if(ret < 0) { perror("read"); break; } if(ret == 0)//对方已关闭 { break; } printf("receive data: %s",buf); if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0) { printf("client is existing! "); break; } } close(parg.fd); printf("pthread fd %d is closed! ",parg.fd); }

2,TCP多进程并发服务器

2.1,头文件net.h

#ifndef __NET_H__ #define __NET_H__ #include #include #include #include #include #include #include #include #include #define SERV_IP_ADDR "192.168.31.100" #define SERV_PORT 5005 #define BACKLOG 5 #define QUIT_STR "quite" #endif

2.2,客户端程序client.c

/* ./client serv_ip serv_port */ #include "net.h" void usage(char *s) { printf("Usage: %s ",s); printf(" serv_ip: server ip address "); printf(" serv_port: server port(>5000) "); } int main(int argc, const char *argv[]) { int fd; short port; struct sockaddr_in sin; if(argc != 3) { usage((char *)argv[0]); exit(1); } if((port = atoi(argv[2])) < 5000) { usage((char *)argv[0]); exit(1); } /* 1 创建socket fd */ if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0) { perror("socket"); exit(-1); } /* 2 连接服务器 */ /* 2.1 填充struct sockaddr_in结构体变量*/ bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(port);//转为网络字节序端口号 if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0) { perror("inet_pton"); goto _error1; } /* 2.2 连接服务器*/ if(connect(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0) { perror("connect"); goto _error1; } /* 3 读写*/ char buf[BUFSIZ]; while(1) { bzero(buf,BUFSIZ); if(fgets(buf,BUFSIZ-1,stdin) == NULL) { continue; } write(fd,buf,strlen(buf)); if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0) { printf("client is existing! "); break; } } _error1: close(fd); return 0; }

2.3,服务器端程序service.c

#include "net.h" #include #include /* 线程传参 */ typedef struct{ int addr;//客户端IP地址 int port;//客户端端口号 int fd;//为请求链接的客户端分配的新的socket fd }ARG; /* 子线程处理函数 */ void client_data_handle(ARG *arg); void sig_child_handle(int signo) { if(SIGCHLD == signo) { waitpid(-1,NULL,WNOHANG); } } int main(int argc, const char *argv[]) { int fd; struct sockaddr_in sin;//如果是IPV6的编程,要使用struct sockddr_in6结构体(详细情况请参考man 7 ipv6),通常更通用的方法可以通过struct sockaddr_storage来编程 pid_t pid;//子进程ID signal(SIGCHLD,sig_child_handle); /* 1 创建socket fd */ if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0) { perror("socket"); exit(-1); } /* 优化 1 允许绑定地址快速重用 */ int b_reuse = 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int)); /* 2 绑定 */ /* 2.1 填充struct sockaddr_in 结构体变量*/ bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); #if 1 /* 优化 2 让服务器可以绑定在任意的IP上*/ sin.sin_addr.s_addr = htonl(INADDR_ANY); #else if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0) { perror("inet_pton"); goto _error1; } #endif /* 2.2 绑定*/ if(bind(fd,(struct sockaddr *)&sin,sizeof(sin))) { perror("bind"); goto _error1; } /* 3 使用listen()把主动套接字变成被动套接字 */ if(listen(fd,BACKLOG) < 0) { perror("listen"); goto _error1; } struct sockaddr_in cin; socklen_t cin_addr_len = sizeof(cin); /* 优化 4 用多进程/多线程处理已经建立好链接的客户端数据*/ while(1) { int newfd = -1; /* 4 阻塞等待客户端链接请求*/ /* 优化 3 通过函数获取刚建立链接的客户端的IP地址和端口号*/ if((newfd = accept(fd,(struct sockaddr *)&cin,&cin_addr_len)) < 0) { perror("connect"); goto _error1; } if((pid = fork()) < 0) { perror("fork"); break;