服务器设计技术有很多,按使用的协议来分有 TCP 服务器和 UDP 服务器,按处理方式来分有循环服务器并发服务器

循环服务器与并发服务器模型

在网络程序里面,一般来说都是许多客户对应一个服务器(多对一),为了处理客户的请求,对服务端的程序就提出了特殊的要求。

目前最常用的服务器模型有:

·循环服务器:服务器在同一时刻只能响应一个客户端的请求

·并发服务器:服务器在同一时刻可以响应多个客户端的请求

UDP 循环服务器的实现方法

UDP 循环服务器每次从套接字上读取一个客户端的请求 -> 处理 -> 然后将结果返回给客户机。

因为 UDP 是非面向连接的,没有一个客户端可以老是占住服务端。只要处理过程不是死循环,或者耗时不是很长,服务器对于每一个客户机的请求在某种程度上来说是能够满足。

UDP 循环服务器模型为

socket(...); // 创建套接字
bind(...);   // 绑定while(1)
{recvfrom(...); // 接收客户端的请求process(...);  // 处理请求sendto(...);   // 反馈处理结果
}

示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{unsigned short port = 8080;   // 本地端口int sockfd;sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建udp套接字if(sockfd < 0){perror("socket");exit(-1);}// 初始化本地网络信息struct sockaddr_in my_addr;bzero(&my_addr, sizeof(my_addr));  // 清空my_addr.sin_family = AF_INET;     // IPv4my_addr.sin_port   = htons(port);   // 端口my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // ipprintf("Binding server to port %d\n", port);// 绑定int err_log;err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));if(err_log != 0){perror("bind");close(sockfd);       exit(-1);}printf("receive data...\n");while(1){int recv_len;char recv_buf[512] = {0};struct sockaddr_in client_addr;char cli_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16socklen_t cliaddr_len = sizeof(client_addr);// 接收客户端数据recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len);// 处理数据,这里只是把接收过来的数据打印inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf("\nip:%s ,port:%d\n",cli_ip, ntohs(client_addr.sin_port)); // 客户端的ipprintf("data(%d):%s\n",recv_len,recv_buf); // 客户端的数据// 反馈结果,这里把接收直接到客户端的数据回复过去sendto(sockfd, recv_buf, recv_len, 0, (struct sockaddr*)&client_addr, cliaddr_len);}close(sockfd);return 0;
}

运行结果如下:

TCP 循环服务器的实现方法

TCP 循环服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接。TCP 循环服务器一次只能处理一个客户端的请求,只有在这个客户的所有请求满足后,服务器才可以继续后面的请求。如果有一个客户端占住服务器不放时,其它的客户机都不能工作了,因此,TCP 服务器一般很少用循环服务器模型的。

TCP循环服务器模型为:

socket(...);// 创建套接字
bind(...);// 绑定
listen(...);// 监听while(1)
{accept(...);// 取出客户端的请求连接process(...);// 处理请求,反馈结果close(...);// 关闭连接套接字:accept()返回的套接字
}

示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{unsigned short port = 8080;       // 本地端口 // 创建tcp套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);   if(sockfd < 0){perror("socket");exit(-1);}// 配置本地网络信息struct sockaddr_in my_addr;bzero(&my_addr, sizeof(my_addr));    // 清空   my_addr.sin_family = AF_INET;          // IPv4my_addr.sin_port   = htons(port);   // 端口my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // ip// 绑定int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));if( err_log != 0){perror("binding");close(sockfd);       exit(-1);}// 监听,套接字变被动err_log = listen(sockfd, 10); if(err_log != 0){perror("listen");close(sockfd);     exit(-1);}  printf("listen client @port=%d...\n",port);while(1){    struct sockaddr_in client_addr;        char cli_ip[INET_ADDRSTRLEN] = "";       socklen_t cliaddr_len = sizeof(client_addr);    // 取出客户端已完成的连接int connfd;connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);       if(connfd < 0){perror("accept");continue;}// 打印客户端的ip和端口inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf("----------------------------------------------\n");printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));// 接收数据char recv_buf[512] = {0};int len =  recv(connfd, recv_buf, sizeof(recv_buf), 0);// 处理数据,这里只是打印接收到的内容printf("\nrecv data:\n");printf("%s\n",recv_buf);// 反馈结果send(connfd, recv_buf, len, 0);close(connfd);     //关闭已连接套接字printf("client closed!\n");}close(sockfd);         //关闭监听套接字return 0;
}

运行结果如下:

三种并发服务器实现方法

一个好的服务器,一般都是并发服务器(同一时刻可以响应多个客户端的请求)。并发服务器设计技术一般有:多进程服务器、多线程服务器、I/O复用服务器等。

多进程并发服务器

在 Linux 环境下多进程的应用很多,其中最主要的就是网络/客户服务器。多进程服务器是当客户有请求时,服务器用一个子进程来处理客户请求。父进程继续等待其它客户的请求。这种方法的优点是当客户有请求时,服务器能及时处理客户,特别是在客户服务器交互系统中。对于一个 TCP 服务器,客户与服务器的连接可能并不马上关闭,可能会等到客户提交某些数据后再关闭,这段时间服务器端的进程会阻塞,所以这时操作系统可能调度其它客户服务进程,这比起循环服务器大大提高了服务性能

TCP多进程并发服务器
TCP 并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是由服务器创建一个子进程来处理。

示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>    int main(int argc, char *argv[])
{unsigned short port = 8080;       // 本地端口 // 创建tcp套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);   if(sockfd < 0){perror("socket");exit(-1);}// 配置本地网络信息struct sockaddr_in my_addr;bzero(&my_addr, sizeof(my_addr));    // 清空   my_addr.sin_family = AF_INET;          // IPv4my_addr.sin_port   = htons(port);   // 端口my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // ip// 绑定int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));if( err_log != 0){perror("binding");close(sockfd);       exit(-1);}// 监听,套接字变被动err_log = listen(sockfd, 10); if(err_log != 0){perror("listen");close(sockfd);     exit(-1);}while(1) //主进程 循环等待客户端的连接{char cli_ip[INET_ADDRSTRLEN] = {0};struct sockaddr_in client_addr;socklen_t cliaddr_len = sizeof(client_addr);// 取出客户端已完成的连接int connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);if(connfd < 0){perror("accept");close(sockfd);exit(-1);}pid_t pid = fork();if(pid < 0){perror("fork");_exit(-1);}else if(0 == pid){ //子进程 接收客户端的信息,并发还给客户端/*关闭不需要的套接字可节省系统资源,同时可避免父子进程共享这些套接字可能带来的不可预计的后果*/close(sockfd);   // 关闭监听套接字,这个套接字是从父进程继承过来char recv_buf[1024] = {0};int recv_len = 0;// 打印客户端的 ip 和端口memset(cli_ip, 0, sizeof(cli_ip)); // 清空inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf("----------------------------------------------\n");printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));// 接收数据while( (recv_len = recv(connfd, recv_buf, sizeof(recv_buf), 0)) > 0 ){printf("recv_buf: %s\n", recv_buf); // 打印数据send(connfd, recv_buf, recv_len, 0); // 给客户端回数据}printf("client closed!\n");close(connfd);    //关闭已连接套接字exit(0);}else if(pid > 0){  // 父进程close(connfd);    //关闭已连接套接字}}close(sockfd);return 0;
}

运行结果如下:

多线程服务器

多线程服务器是对多进程的服务器的改进,由于多进程服务器在创建进程时要消耗较大的系统资源,所以用线程来取代进程,这样服务处理程序可以较快的创建。据统计,创建线程与创建进程要快 10100 倍,所以又把线程称为“轻量级”进程。线程与进程不同的是:一个进程内的所有线程共享相同的全局内存、全局变量等信息,这种机制又带来了同步问题

以下是多线程服务器模板:

示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>/************************************************************************
函数名称:    void *client_process(void *arg)
函数功能:    线程函数,处理客户信息
函数参数:    已连接套接字
函数返回:    无
************************************************************************/
void *client_process(void *arg)
{int recv_len = 0;char recv_buf[1024] = ""; // 接收缓冲区int connfd = (int)arg; // 传过来的已连接套接字// 接收数据while((recv_len = recv(connfd, recv_buf, sizeof(recv_buf), 0)) > 0){printf("recv_buf: %s\n", recv_buf); // 打印数据send(connfd, recv_buf, recv_len, 0); // 给客户端回数据}printf("client closed!\n");close(connfd);    //关闭已连接套接字return    NULL;
}//===============================================================
// 语法格式: void main(void)
// 实现功能: 主函数,建立一个TCP并发服务器
// 入口参数: 无
// 出口参数: 无
//===============================================================
int main(int argc, char *argv[])
{int sockfd = 0;               // 套接字int connfd = 0;int err_log = 0;struct sockaddr_in my_addr;  // 服务器地址结构体unsigned short port = 8080; // 监听端口pthread_t thread_id;printf("TCP Server Started at port %d!\n", port);sockfd = socket(AF_INET, SOCK_STREAM, 0);   // 创建TCP套接字if(sockfd < 0){perror("socket error");exit(-1);}bzero(&my_addr, sizeof(my_addr));       // 初始化服务器地址my_addr.sin_family = AF_INET;my_addr.sin_port   = htons(port);my_addr.sin_addr.s_addr = htonl(INADDR_ANY);printf("Binding server to port %d\n", port);// 绑定err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));if(err_log != 0){perror("bind");close(sockfd);        exit(-1);}// 监听,套接字变被动err_log = listen(sockfd, 10);if( err_log != 0){perror("listen");close(sockfd);     exit(-1);}printf("Waiting client...\n");while(1){char cli_ip[INET_ADDRSTRLEN] = "";       // 用于保存客户端IP地址struct sockaddr_in client_addr;           // 用于保存客户端地址socklen_t cliaddr_len = sizeof(client_addr);   // 必须初始化!!!//获得一个已经建立的连接 connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);                             if(connfd < 0){perror("accept this time");continue;}// 打印客户端的 ip 和端口inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf("----------------------------------------------\n");printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));if(connfd > 0){//由于同一个进程内的所有线程共享内存和变量,因此在传递参数时需作特殊处理,值传递。pthread_create(&thread_id, NULL, (void *)client_process, (void *)connfd);  //创建线程pthread_detach(thread_id); // 线程分离,结束时自动回收资源}}close(sockfd);return 0;
}

运行结果如下:

注意,上面例子给线程传参有很大的局限性,最简单的一种情况,如果我们需要给线程传多个参数,这时候我们需要结构体传参,这种值传递编译都通不过,这里之所以能够这么值传递,是因为, int 长度时 4 个字节, void * 长度也是 4 个字节。

int connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);
pthread_create(&thread_id, NULL, (void *)client_process, (void *)connfd); 

如果考虑类型匹配的话,应该是这么传参,pthread_create()最后一个参数应该传地址( &connfd ),而不是值:

int connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);
pthread_create(&thread_id, NULL, (void *)client_process, (void *)&connfd); 

但是,如果按地址传递的话,又会有这么一个问题,假如有多个客户端要连接这个服务器,正常的情况下,一个客户端连接对应一个 connfd,相互之间独立不受影响,但是,假如多个客户端同时连接这个服务器,A 客户端的连接套接字为 connfd,服务器正在用这个 connfd 处理数据,还没有处理完,突然来了一个 B 客户端,accept()之后又生成一个 connfd, 因为是地址传递, A 客户端的连接套接字也变成 B 这个了,这样的话,服务器肯定不能再为 A 客户端服务器了,这时候,我们就需要考虑多任务的互斥或同步问题了,这里通过互斥锁来解决这个问题,确保这个connfd值被一个临时变量保存过后,才允许修改。

#include <pthread.h>pthread_mutex_t mutex; // 定义互斥锁,全局变量pthread_mutex_init(&mutex, NULL); // 初始化互斥锁,互斥锁默认是打开的// 上锁,在没有解锁之前,pthread_mutex_lock()会阻塞
pthread_mutex_lock(&mutex);
int connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);//给回调函数传的参数,&connfd,地址传递
pthread_create(&thread_id, NULL, (void *)client_process, (void *)&connfd);  //创建线程// 线程回调函数
void *client_process(void *arg)
{int connfd = *(int *)arg; // 传过来的已连接套接字// 解锁,pthread_mutex_lock()唤醒,不阻塞pthread_mutex_unlock(&mutex); return     NULL;
}

修改的完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>pthread_mutex_t mutex;    // 定义互斥锁,全局变量/************************************************************************
函数名称:    void *client_process(void *arg)
函数功能:    线程函数,处理客户信息
函数参数:    已连接套接字
函数返回:    无
************************************************************************/
void *client_process(void *arg)
{int recv_len = 0;char recv_buf[1024] = ""; // 接收缓冲区int connfd = *(int *)arg; // 传过来的已连接套接字// 解锁,pthread_mutex_lock()唤醒,不阻塞pthread_mutex_unlock(&mutex); // 接收数据while((recv_len = recv(connfd, recv_buf, sizeof(recv_buf), 0)) > 0){printf("recv_buf: %s\n", recv_buf); // 打印数据send(connfd, recv_buf, recv_len, 0); // 给客户端回数据}printf("client closed!\n");close(connfd); //关闭已连接套接字return    NULL;
}//===============================================================
// 语法格式: void main(void)
// 实现功能: 主函数,建立一个TCP并发服务器
// 入口参数: 无
// 出口参数: 无
//===============================================================
int main(int argc, char *argv[])
{int sockfd = 0;               // 套接字int connfd = 0;int err_log = 0;struct sockaddr_in my_addr;  // 服务器地址结构体unsigned short port = 8080; // 监听端口pthread_t thread_id;pthread_mutex_init(&mutex, NULL); // 初始化互斥锁,互斥锁默认是打开的printf("TCP Server Started at port %d!\n", port);sockfd = socket(AF_INET, SOCK_STREAM, 0);   // 创建TCP套接字if(sockfd < 0){perror("socket error");exit(-1);}bzero(&my_addr, sizeof(my_addr));       // 初始化服务器地址my_addr.sin_family = AF_INET;my_addr.sin_port   = htons(port);my_addr.sin_addr.s_addr = htonl(INADDR_ANY);printf("Binding server to port %d\n", port);// 绑定err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));if(err_log != 0){perror("bind");close(sockfd);        exit(-1);}// 监听,套接字变被动err_log = listen(sockfd, 10);if( err_log != 0){perror("listen");close(sockfd);     exit(-1);}printf("Waiting client...\n");while(1){char cli_ip[INET_ADDRSTRLEN] = "";       // 用于保存客户端IP地址struct sockaddr_in client_addr;           // 用于保存客户端地址socklen_t cliaddr_len = sizeof(client_addr);   // 必须初始化!!!// 上锁,在没有解锁之前,pthread_mutex_lock()会阻塞pthread_mutex_lock(&mutex);    //获得一个已经建立的连接   connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);                             if(connfd < 0){perror("accept this time");continue;}// 打印客户端的 ip 和端口inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf("----------------------------------------------\n");printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));if(connfd > 0){//给回调函数传的参数,&connfd,地址传递pthread_create(&thread_id, NULL, (void *)client_process, (void *)&connfd);  //创建线程pthread_detach(thread_id); // 线程分离,结束时自动回收资源}}close(sockfd);return 0;
}

I/O复用服务器

I/O 复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用。它也可用于并发服务器的设计,常用函数 select() 或 epoll() 来实现。详情,请看《select、poll、epoll的区别使用》。

socket(...); // 创建套接字
bind(...);   // 绑定
listen(...); // 监听while(1)
{if(select(...) > 0) // 检测监听套接字是否可读{if(FD_ISSET(...)>0) // 套接字可读,证明有新客户端连接服务器  {accpet(...);// 取出已经完成的连接process(...);// 处理请求,反馈结果}}close(...); // 关闭连接套接字:accept()返回的套接字
}

示例代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>#define SERV_PORT 8080
#define LIST 20                //服务器最大接受连接
#define MAX_FD 10              //FD_SET支持描述符数量int main(int argc, char *argv[])
{int sockfd;int err;int i;int connfd;int fd_all[MAX_FD]; //保存所有描述符,用于select调用后,判断哪个可读//下面两个备份原因是select调用后,会发生变化,再次调用select前,需要重新赋值fd_set fd_read;    //FD_SET数据备份fd_set fd_select;  //用于selectstruct timeval timeout;         //超时时间备份struct timeval timeout_select;  //用于selectstruct sockaddr_in serv_addr;   //服务器地址struct sockaddr_in cli_addr;    //客户端地址socklen_t serv_len;socklen_t cli_len;//超时时间设置timeout.tv_sec = 10;timeout.tv_usec = 0;//创建TCP套接字sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){perror("fail to socket");exit(1);}// 配置本地地址memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;            // ipv4serv_addr.sin_port = htons(SERV_PORT);  // 端口, 8080serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // ipserv_len = sizeof(serv_addr);// 绑定err = bind(sockfd, (struct sockaddr *)&serv_addr, serv_len);if(err < 0){perror("fail to bind");exit(1);}// 监听err = listen(sockfd, LIST);if(err < 0){perror("fail to listen");exit(1);}//初始化fd_all数组memset(&fd_all, -1, sizeof(fd_all));fd_all[0] = sockfd;   //第一个为监听套接字FD_ZERO(&fd_read);   // 清空FD_SET(sockfd, &fd_read);  //将监听套接字加入fd_readint maxfd;maxfd = fd_all[0];  //监听的最大套接字while(1){// 每次都需要重新赋值,fd_select,timeout_select每次都会变fd_select = fd_read;timeout_select = timeout;// 检测监听套接字是否可读,没有可读,此函数会阻塞// 只要有客户连接,或断开连接,select()都会往下执行err = select(maxfd+1, &fd_select, NULL, NULL, NULL);//err = select(maxfd+1, &fd_select, NULL, NULL, (struct timeval *)&timeout_select);if(err < 0){perror("fail to select");exit(1);}if(err == 0){printf("timeout\n");}// 检测监听套接字是否可读if( FD_ISSET(sockfd, &fd_select) ){//可读,证明有新客户端连接服务器cli_len = sizeof(cli_addr);// 取出已经完成的连接connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len);if(connfd < 0){perror("fail to accept");exit(1);}// 打印客户端的 ip 和端口char cli_ip[INET_ADDRSTRLEN] = {0};inet_ntop(AF_INET, &cli_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf("----------------------------------------------\n");printf("client ip=%s,port=%d\n", cli_ip,ntohs(cli_addr.sin_port));// 将新连接套接字加入 fd_all 及 fd_readfor(i=0; i < MAX_FD; i++){if(fd_all[i] != -1){continue;}else{fd_all[i] = connfd;printf("client fd_all[%d] join\n", i);break;}}FD_SET(connfd, &fd_read);if(maxfd < connfd){maxfd = connfd;  //更新maxfd}}//从1开始查看连接套接字是否可读,因为上面已经处理过0(sockfd)for(i=1; i < maxfd; i++){if(FD_ISSET(fd_all[i], &fd_select)){printf("fd_all[%d] is ok\n", i);char buf[1024]={0};  //读写缓冲区int num = read(fd_all[i], buf, 1024);if(num > 0){//收到 客户端数据并打印printf("receive buf from client fd_all[%d] is: %s\n", i, buf);//回复客户端num = write(fd_all[i], buf, num);if(num < 0){perror("fail to write ");exit(1);}else{//printf("send reply\n");}}else if(0 == num){ // 客户端断开时//客户端退出,关闭套接字,并从监听集合清除printf("client:fd_all[%d] exit\n", i);FD_CLR(fd_all[i], &fd_read);close(fd_all[i]);fd_all[i] = -1;continue;}}else {//printf("no data\n");                  }}}return 0;
}

运行结果如下:

本教程示例代码下载请点此处。

参考于:http://blog.chinaunix.net

Linux 网络编程——并发服务器的三种实现模型相关推荐

  1. 【Linux网络编程】并发服务器的三种实现模型

    服务器设计技术有很多,按使用的协议来分有 TCP 服务器和 UDP 服务器,按处理方式来分有循环服务器和并发服务器. 循环服务器与并发服务器模型 在网络程序里面,一般来说都是许多客户对应一个服务器(多 ...

  2. linux网络编程之并发服务器的三种实现模型 (超级经典)

    转载 : http://blog.csdn.net/tennysonsky/article/details/45671215 服务器设计技术有很多,按使用的协议来分有 TCP 服务器和 UDP 服务器 ...

  3. LINUX环境并发服务器的三种实现模型

    服务器设计技术有很多,按使用的协议来分有TCP服务器和UDP服务器.按处理方式来分有循环服务器和并发服务器. 1  循环服务器与并发服务器模型 在网络程序里面,一般来说都是许多客户对应一个服务器,为了 ...

  4. 1 linux下tcp并发服务器的几种设计的模式套路,Linux下几种并发服务器的实现模式(详解)...

    1>单线程或者单进程 相当于短链接,当accept之后,就开始数据的接收和数据的发送,不接受新的连接,即一个server,一个client 不存在并发. 2>循环服务器和并发服务器 1.循 ...

  5. 【Linux网络编程】循环服务器之UDP循环模型

    00. 目录 文章目录 00. 目录 01. 概述 02. UDP循环服务器的实现方法 03. UDP循环服务器模型 04. UDP循环服务器实现 05. 附录 01. 概述 服务器设计技术有很多,按 ...

  6. Linux网络编程 | 并发模式:半同步/半异步模式、领导者/追随者模式

    文章目录 同步与异步 半同步/半异步模式 变体:半同步/半反应堆模式 改进:更高效的半同步/半异步模式 领导者/追随者模式 组件 :句柄集.线程集.事件处理器 并发模式是指I/O处理单元和多个逻辑单元 ...

  7. Linux网络编程--TCP中的三次握手和四次挥手

    服务器编程和客户端编程的大致流程如下: 三次握手是在客户端中的connect中完成的,具体如下: 那么上述说到的SYN     ACK这些是什么东西呢? 上述的截图取自<Linux高性能服务器编 ...

  8. Linux网络编程——浅谈 TCP 三次握手和四次挥手

    一.tcp协议格式 二.三次握手 在 TCP/IP 协议中,TCP 协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送 syn 包(tcp协议中syn位置1,序号为 ...

  9. linux网络编程——webserver服务器编写

    1.HTTP协议 超文本传输协议 2.http请求报文协议 在浏览器上输入http://192.168.0.2:80/hello.txt,浏览器会给服务器发送一个http请求报文,其报文如下. 请求行 ...

最新文章

  1. Vue开源项目库汇总
  2. 案例:验证用户名是否可用
  3. 【学习求职必备】百度AI和它的7大AI黑科技
  4. 居民身份证号码组成规则
  5. P4332-[SHOI2014]三叉神经树【LCT】
  6. Unity3D--枚举+协程控制定点移动
  7. APP测试——经典面试题分享
  8. 刘知远:在深度学习时代用HowNet搞事情
  9. linux centos 网易云音乐,Centos7.4安装网易云音乐教程
  10. 1.1、什么是编程?
  11. 验证“哥德巴赫猜想”
  12. 学人工智能看什么书?AI入门书籍推荐
  13. JavaScript 中的事件类型1(读书笔记思维导图)
  14. Oracle怎么安装
  15. 淘宝运营助理工作性质 运营助理工作难吗
  16. 如何写一个自动投票工具
  17. html段落布局,WPS文字段落布局功能让眼前的文稿一目了然
  18. 沙特阿拉伯在中东实力很强大
  19. ANU-COMP6442 7-12
  20. 在Manjaro Linux( ArchLinux )上用Chrome走代理端口配置

热门文章

  1. Android Studio 使用WIFI调试adb (2种方法)
  2. Vue项目 HBuilder X打包H5 APP
  3. 安卓常见shell命令
  4. AndroidStudio之Robolectric单元测试 No Such manifest file
  5. 如何修改npm仓库地址
  6. 《乔新亮的CTO成长复盘》—— 产品思维,契约精神是基础,洞察人性才能成就卓越。——有感
  7. 将用记事本打开添加到鼠标右键
  8. 开源轻量级数据库访问框架
  9. 外挂技术研究系列-分析人物属性数据和偏移
  10. C语言中的‘\0’、0、‘0’和NULL辨析