TCP网络服务器模型

本文介绍了两种常见的TCP服务器模型:循环服务器和并发服务器。循环服务器适用于客户端连接时间较短的情况;并发服务器包括多进程、多线程及IO多路复用等实现方式,适合客户端数量较多且连接时间较长的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

服务器模型

1、循环服务器
实现流程:
1)、服务器端从连接请求队列中提取请求,建立连接并返回已连接的套接字。
2)、服务器端通过以连接的套接字循环接收数据,处理并发给客户端,直到客户端关闭连接。
3)、服务器端关闭已连接的套接字,返回步骤 1)。


特点:
    1.服务器采用循环嵌套实现,外层循环一次提取每个客户端的链接请求,建立TCP链接。内层循环接收连接并处理当前客户端的所有数据,知道客户端关闭连接。
      如果当前客户端没有处理结束,其他客户端必须一直等待。
    2.采用这种循环服务器端无法实现对多个客户端服务。适应于客户端连接时间短的情况。 
    3.同一时刻只能处理一个客户端的请求,一般很少使用


服务器代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main()
{
   
//定义结构体变量,作为bind函数的参数
    struct sockaddr_in servAddr;
   
memset(&servAddr, 0, sizeof(servAddr));
   
servAddr.sin_family = PF_INET;
   
servAddr.sin_port = htons(8888);
   
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

   
//创建套接字
    //返回值为新创建的socket的文件描述符,失败-1
    int servFd = socket(PF_INET, SOCK_STREAM, 0);
   
if(servFd < 0)
   
{
       
perror("socket error!");
       
return -1;
   
}
   
printf("socket ok!\n");
   
//绑定地址信息
    int ret = bind(servFd, (struct sockaddr *)&servAddr, sizeof(servAddr));
   
if(ret < 0)
   
{
       
perror("bind error!");
       
close(servFd);
       
return -1;
   
}
   
printf("bind ok!\n");
   
//创建监听队列
    ret = listen(servFd, 10);
   
if(ret < 0)
   
{
       
perror("listen error!");
       
close(servFd);
       
return -1;
   
}
   
printf("listening.....\n");

   
while(1)
   
{
       
//接收连接请求
        int connFd = accept(servFd, NULL, NULL);
       
if(connFd < 0)
       
{
           
perror("accept error!");
           
close(servFd);
           
return -1;
       
}
       
printf("accept ok!\n");

       
char buf[1024] = {0};
       
//接收消息
        ret = recv(connFd, buf, sizeof(buf), 0);
       
if(ret < 0)
       
{
           
perror("recv error!");
           
close(servFd);
           
close(connFd);
           
return -1;
       
}
       
printf("recv from client: %s\n", buf);

       
strcat(buf, "+++");
       
send(connFd, buf, sizeof(buf), 0);
       
//关闭套接字
        close(connFd);
       
sleep(10);
   
}
   
close(servFd);
   
return 0;
}









































































































客户端代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>


int main()
{
   
//定义结构体变量,该变量保存的服务器的地址信息,作为connect的参数
    struct sockaddr_in servAddr;
   
memset(&servAddr, 0, sizeof(servAddr));
   
servAddr.sin_family = PF_INET;
   
servAddr.sin_port = htons(8888);
   
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

   
//创建套接字
    int cliFd = socket(PF_INET, SOCK_STREAM, 0);
   
if(cliFd < 0)
   
{
       
perror("socket error!");
       
return -1;
   
}
   
printf("socket ok!\n");
   
//发起连接请求
    int ret = connect(cliFd, (struct sockaddr *)&servAddr, sizeof(servAddr));
   
if(ret < 0)
   
{
       
perror("connect error!");
       
close(cliFd);
       
return -1;
   
}
   
printf("connect ok!\n");
   
char buf[1024] = "hello world";
   
//发送消息
    ret = send(cliFd, buf, sizeof(buf), 0);
   
if(ret < 0)
   
{
       
perror("send error!");


        close(cliFd);
        return -1;
    }
    printf("send ok!\n");

    memset(buf, 0, sizeof(buf));
    recv(cliFd, buf, sizeof(buf), 0);
    printf("recv from server %s\n", buf);
    //关闭套接字
    close(cliFd);
    return 0;

}


















































   





















2、并发服务器

>>>多进程的并发服务器
   >>>多线程的并发服务器
   >>>IO多路复用的并发服务器
  
  
>>>多进程的并发服务器

   只要有客户端连接服务器,服务器就创建子进程与客户端通信

   创建子进程后,父进程----继续等待其他客户端的连接
               子进程----与客户端通信
                
   总结:多进程服务器,比较浪费资源,适合于客户端数量较少,但是长连接的情况


服务器代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>

void sigFunc(int sigNo)
{
   
if(sigNo == SIGCHLD)
   
{
       
wait(NULL);
   
}
}

int main()
{
   
//定义结构体变量,作为bind函数的参数
    struct sockaddr_in servAddr;
   
memset(&servAddr, 0, sizeof(servAddr));
   
servAddr.sin_family = PF_INET;
   
servAddr.sin_port = htons(8888);
   
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

   
//创建套接字
    //返回值为新创建的socket的文件描述符,失败-1
    int servFd = socket(PF_INET, SOCK_STREAM, 0);
   
if(servFd < 0)
   
{
       
perror("socket error!");
       
return -1;
   
}
   
printf("socket ok!\n");
   
//绑定地址信息
    int ret = bind(servFd, (struct sockaddr *)&servAddr, sizeof(servAddr));
   
if(ret < 0)
   
{
       
perror("bind error!");
       
close(servFd);
       
return -1;
   
}
   
printf("bind ok!\n");
   
//创建监听队列
    ret = listen(servFd, 10);
   
if(ret < 0)
   
{
       
perror("listen error!");
       
close(servFd);
       
return -1;
   
}
   
printf("listening.....\n");

   
while(1)
   
{
       
//接收连接请求
        int connFd = accept(servFd, NULL, NULL);
       
if(connFd < 0)
       
{
           
perror("accept error!");
           
close(servFd);
           
return -1;
       
}
       
printf("accept ok!\n");

       
//创建子进程
        pid_t pid;
       
while((pid = fork()) < 0);
       
if(0 == pid)
       
{
           
close(servFd);
           
char buf[1024] = {0};
           
while(1)
           
{
               
//接收消息
                memset(buf, 0, sizeof(buf));
               
ret = recv(connFd, buf, sizeof(buf), 0);
               
if(ret < 0)
               
{
                   
perror("recv error!");
                   
close(connFd);
                   
return -1;
               
}
               
else if(0 == ret)
               
{
                   
printf("client shutdown!\n");
                   
break;
               
}
               
printf("recv from client: %s\n", buf);
               
memset(buf, 0, sizeof(buf));
               
printf("server:");
               
gets(buf);
               
send(connFd, buf, sizeof(buf), 0);
           
}
           
//关闭套接字
            close(connFd);
           
exit(0);
       
}
       
else
       
{
           
close(connFd);
           
signal(SIGCHLD, sigFunc);
       
}

   
}
   
close(servFd);
   
return 0;
}




















































































































































客户端代码:

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>


int main()
{
   
//定义结构体变量,该变量保存的服务器的地址信息,作为connect的参数
    struct sockaddr_in servAddr;
   
memset(&servAddr, 0, sizeof(servAddr));
   
servAddr.sin_family = PF_INET;
   
servAddr.sin_port = htons(8888);
   
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

   
//创建套接字
    int cliFd = socket(PF_INET, SOCK_STREAM, 0);
   
if(cliFd < 0)
   
{
       
perror("socket error!");
       
return -1;
   
}
   
printf("socket ok!\n");
   
//发起连接请求
    int ret = connect(cliFd, (struct sockaddr *)&servAddr, sizeof(servAddr));
   
if(ret < 0)
   
{
       
perror("connect error!");
       
close(cliFd);
       
return -1;
   
}
   
printf("connect ok!\n");
   
char buf[1024];
   
while(1)
   
{
       
memset(buf, 0, sizeof(buf));
       
printf("client:");
       
gets(buf);
       
if(0 == strcmp(buf, "quit"))
       
{
           
break;
       
}
       
//发送消息
        ret = send(cliFd, buf, sizeof(buf), 0);
       
if(ret < 0)
       
{
           
perror("send error!");
           
close(cliFd);
           
return -1;
       
}
       
printf("send ok!\n");

       
memset(buf, 0, sizeof(buf));
       
recv(cliFd, buf, sizeof(buf), 0);
       
printf("recv from server %s\n", buf);
   
}
   
//关闭套接字
    close(cliFd);
   
return 0;
}



























































   
























>>>多线程的并发服务器
    只要有客户端连接服务器,服务器就创建子线程与客户端通信
    由于在创建子线程时,以及销毁子线程时,比较浪费时间,一般可以使用线程池
    多线程存在的问题:存在资源竞争以及同步的问题
    总结:适合于客户端数量较少,但是长连接的情况

服务器代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>


void *threadFunc(void *arg)
{
   
int connFd = *((int *)arg);
   
char buf[1024] = {0};
   
while(1)
   
{
       
memset(buf, 0, sizeof(buf));
       
//接收消息
        int ret = recv(connFd, buf, sizeof(buf), 0);
       
if(ret < 0)
       
{
           
perror("recv error!");
           
close(connFd);
           
pthread_exit((void *)-1);
       
}
       
else if(0 == ret)
       
{
           
printf("client shutdown!\n");
           
break;
       
}
       
printf("recv from client: %s\n", buf);

       
memset(buf, 0, sizeof(buf));
        printf("server:");
        gets(buf);
        send(connFd, buf, sizeof(buf), 0);
    }
    close(connFd);
    pthread_exit(NULL);
}

int main()
{
    //定义结构体变量,作为bind函数的参数
    struct sockaddr_in servAddr;
    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = PF_INET;
    servAddr.sin_port = htons(8888);
    servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    //创建套接字
    //返回值为新创建的socket的文件描述符,失败-1
    int servFd = socket(PF_INET, SOCK_STREAM, 0);
    if(servFd < 0)
    {
        perror("socket error!");
        return -1;
    }
    printf("socket ok!\n");
    //绑定地址信息
    int ret = bind(servFd, (struct sockaddr *)&servAddr, sizeof(servAddr));
    if(ret < 0)
    {
        perror("bind error!");
        close(servFd);
        return -1;
    }
    printf("bind ok!\n");
    //创建监听队列
    ret = listen(servFd, 10);
    if(ret < 0)
    {
        perror("listen error!");
        close(servFd);
        return -1;
    }
    printf("listening.....\n");
    while(1)
    {
        //接收连接请求
        int connFd = accept(servFd, NULL, NULL);
        if(connFd < 0)
        {
            perror("accept error!");
            close(servFd);
            return -1;
        }
        printf("accept ok!\n");

        pthread_t th;
        while(pthread_create(&th, NULL, threadFunc, &connFd) < 0);
    }
    //关闭套接字
    close(servFd);
    return 0;
}

































































































































客户端代码:

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>


int main()
{
   
//定义结构体变量,该变量保存的服务器的地址信息,作为connect的参数
    struct sockaddr_in servAddr;
   
memset(&servAddr, 0, sizeof(servAddr));
   
servAddr.sin_family = PF_INET;
   
servAddr.sin_port = htons(8888);
   
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

   
//创建套接字
    int cliFd = socket(PF_INET, SOCK_STREAM, 0);
   
if(cliFd < 0)
   
{
       
perror("socket error!");
       
return -1;
   
}
   
printf("socket ok!\n");
   
//发起连接请求
    int ret = connect(cliFd, (struct sockaddr *)&servAddr, sizeof(servAddr));
   
if(ret < 0)
   
{
       
perror("connect error!");
       
close(cliFd);
       
return -1;
   
}
   
printf("connect ok!\n");
   
char buf[1024];
   
while(1)
   
{
       
memset(buf, 0, sizeof(buf));
       
printf("client:");
       
gets(buf);
       
if(0 == strcmp(buf, "quit"))
       
{
           
break;
       
}
       
//发送消息
        ret = send(cliFd, buf, sizeof(buf), 0);
       
if(ret < 0)
       
{
           
perror("send error!");
           
close(cliFd);
           
return -1;
       
}
       
printf("send ok!\n");

       
memset(buf, 0, sizeof(buf));
       
recv(cliFd, buf, sizeof(buf), 0);
       
printf("recv from server %s\n", buf);
   
}
   
//关闭套接字
    close(cliFd);
   
return 0;
}











































    
   






































>>>IO多路复用的并发服务器
    适合于客户端数量较多,但是短连接的情况。

    
    
    
    
    
    
    
    
    
    
    
    
    
    
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值