TCP服务器的单进程版本,多进程版本,多线程版本。

实现TCP服务器的单进程版本,多进程版本,多线程版本。
所需API如下:
1 创建socket文件描述符(TCP/UDP, 客户端+服务器)

   int socket(int domain, int type, int protocol);

•对于IPv4, family参数指定为AF_INET
•对于TCP协议,type参数指定为SOCK_STREAM,表示面向字节流的传输协议
•protocol参数指定为0即可

2 绑定端口号(TCP/UPD ,服务器)

int bind(int socket, const struct sockaddr* address, socklen_t address_len)

服务器程序监听的网络地址和端口号通常固定不变,
服务器需要调用bind绑定一个固定的网络地址和端口号
•struct sockaddr *是⼀个通用指针类型,address参数实际上可以接受多种协议的sockaddr结构体,第三个参数address_len指定结构体的⻓度

3 开始监听socket(TCP , 服务器)

int  listen(int socket, int backlog)

listen()声明socket处于监听状态,最多允许有backlog个客户端处于连接等待状态
如果接收更多的连接请求就忽略,设置一般不会太大(为5)

4 接收请求(TCP 服务器)

int accept (int socket, struct sockaddr* address, socklen_t* address_len);

三次握手后服务器调用accept接收连接
如果调用accept时还没有客户端的连接请求,就阻塞等待直到客户端连接上来

adress是一个输出型参数,accept()返回时传出客户端的地址和端口号
如果给adddress参数传NULL, 表示不关心客户端地址

•address_len参数是⼀个传⼊传出参数(value-result argument),传⼊的是调⽤者提供的,缓冲区address的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际⻓度(有可能没有占满调⽤者提供的缓冲区)

5 建立连接(TCP, 客户端)

int connect(int sockfd, const struct sockaddr*addr, socklen_t addrlen);
客户端调用connect连接服务器
•connect和bind的参数形式⼀致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址

客户端

/////////////////////////////
//客户端处理
//1 从标准输入读入字符串
//2 把读入的字符串发送给服务器
//3 尝试从服务器读取响应数据
//4 把响应结果打印到标准输出上
///////////////////////////

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

int main(int argc, char* argv[])
{
    if(argc!=3)
    {
        printf("usage ./server [ip][port]\n");
        return 1;
    }
    // 1 创建socket
    int fd=socket(AF_INET, SOCK_STREAM,0);
    if(fd<0)
    {
        perror("socket");
        return 1;
    }
    //2 获取连接
    sockaddr_in server_addr;
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=inet_addr(argv[1]);
    server_addr.sin_port=htons(atoi(argv[2]));

    int ret=connect(fd, (sockaddr*)&server_addr, sizeof(server_addr));
    if(ret<0)
    {
        perror("connect");
        return 1;
    }
    // 进入循环
    while(1)
    {
        //a从标准输入读入字符串
        char buf[1024]={0};
        ssize_t read_size=read(0,buf,sizeof(buf)-1);
        if(read_size<0)
        {
            perror("read");
            return 1;
        }
        if(read_size==0)
        {
            printf("read done!\n");
            return 0;
        }
        buf[read_size]='\0';
        //b)把读入的字符串发送给服务器
        write(fd, buf, strlen(buf));
        //c)尝试从服务器读取响应数据
        char buf_resp[1024]={0};
        read_size =read(fd, buf_resp, sizeof(buf_resp)-1);
        if(read_size<0)
        {
            perror("read_size");
            return 1;
        }
        if(read_size==0)
        {
            printf("server close socket!\n");
            return 0;
        }
        buf_resp[read_size]='\0';
        //d)把响应结果打印到标准输出上
        printf("server resp: %s\n",buf_resp);
    }
    return 0;
}

服务端单进程版


//服务器基本流程
//1.从socket中读取数据(Request)
//2,根据(Request)计算生成Response
//3.把Response 写回客户端
//目前实现的是 echo_server, 计算生成Response的步骤就省略了

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

void ProcessConnect(int new_sock)
{
    //完成一次连接的处理
    //需要循环的来处理客户福安发送发数据

    while(1)
    {
        //子进程
        //a)从客户端读取数据
        char buf[1024]={0};
        ssize_t read_size=read(new_sock,buf,sizeof(buf)-1);
        if(read_size<0)
        {
            perror("read");
            continue;
        }
        if(read_size==0)
        {
            //TCP中,如果read的返回值是0,说明对端关闭了连接
            printf("[client %d] disconnect!\n",new_sock);
            close(new_sock);
            return ;
        }
        buf[read_size]='\0';
        //b) 根据请求计算响应(省略)
        printf("[client %d] %s\n", new_sock, buf);
        //c) 把响应写回客户端
        write(new_sock, buf,strlen(buf));
    }
}

//  ./server [ip] [port]
int main( int argc, char* argv[] )
{
    if(argc!=3)
    {
        printf("Usage ./server [ip] [port]");
        return 1;
    }
    //1.创建 socket
    int server_sock=socket(AF_INET, SOCK_STREAM, 0);
    if(server_sock<0)
    {
        perror("socket");
        return 1;
    }
    //2绑定端口号
    sockaddr_in server;
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=inet_addr(argv[1]);
    server.sin_port=htons(atoi(argv[2]));

    int ret=bind(server_sock, (sockaddr*)&server,sizeof(server));
    if(ret<0)
    {
        perror("bind");
        return 1;
    }
    //3使用listen允许服务器被客户端连接
    ret=listen(server_sock,5);
    if(ret<0)
    {
        perror("listen");
        return 1;
    }
    //4服务器初始化完成,进入事件循环
    printf("Server Init ok!\n");
    while(1)
    {
        sockaddr_in peer;
        socklen_t len=sizeof(peer);
        int new_sock=accept(server_sock, (sockaddr*)&peer, &len);
        if(new_sock<0)
        {
            perror("accept");
            continue;
        }
        printf("[client %d] connect!\n", new_sock);
        ProcessConnect(new_sock);
    }
    return 0;
}

服务端多进程版

/
//服务器基本流程(多进程)
//1 从sock中读取数据(请求)
//2 根据请求计算生成(回应)
//3 把回应写到客户端
//由于目前实现的是echo_server,省略请求的计算

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

typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

void ProcessConnect(int new_sock, sockaddr_in* peer)
{
    while(1)
    {
        int ret=fork();
        if(ret<0)
        {
            perror("fork");
            return ;
        }
        if(ret>0)
        {
             //父进程,考虑僵尸进程的问题,wait,waitpid是不行的
             //简单方案是忽略 SIGCHLD信号
             signal(SIGCHLD, SIG_IGN);
             //文件描述符需要父子进程都关闭
             close(new_sock);
             return ;
        }
        //子进程  
        //a 从客户端读取数据
        char buf[1024]={0};
        ssize_t read_size=read(new_sock, buf, sizeof(buf)-1);
        if(read_size<0)
        {
            perror("read");
            continue;
        }
        if(read_size==0)
        {
            //TCP中,read的返回值为0说明对端关闭了连接
            printf("[client %s: %d] disconnect!\n", inet_ntoa(peer->sin_addr),ntohs(peer->sin_port));
            close(new_sock);
            // 注意!!!、
            // 不能直接让函数返回,让子进程直接退出,
            // 如果是函数返回,子进程也会尝试进行accept
            // 这样的动作是没有必要的,父进程已经负责了accept
            //子进程只要把对应的客户端服务好就行了
            exit(0);
        }
        buf[read_size]='\0';
        //根据请求计算响应(忽略)
        printf("[client %s:%d] %s\n",inet_ntoa(peer->sin_addr),ntohs(peer->sin_port), buf);
        //把响应写回到客户端
        write(new_sock, buf,strlen(buf));
    }
}

//  ./server [ip] [port]
int main(int argc, char* argv[])
{
    if(argc!=3)
    {
        printf("usage ./server[ip] [port]\n");
        return 1;
    }
    //1.创建 socket
    int sock=socket(AF_INET, SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        return 1;
    }
    //2 绑定端口号
    sockaddr_in server;
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=inet_addr(argv[1]);
    server.sin_port=htons(atoi(argv[2]));

    int ret=bind(sock, (sockaddr*)&server, sizeof(server));
    if(ret<0)
    {
        perror("bind");
        return 1;
    }

    //3 使用listen允许服务器被客户端连接
    ret=listen(sock, 5);
    if(ret<0)
    {
        perror("bind");
        return 1;
    }
    //4 服务器初始化完成,进入事件循环
    printf("Server Init OK!\n");
    while(1)
    {
        sockaddr_in peer;
        socklen_t len=sizeof(peer);
        int new_sock=accept(sock,(sockaddr*)&peer, &len);
        if(new_sock<0)
        {
            perror("accept");
            continue;
        }
        printf("[clien %s:%d] connect!\n", inet_ntoa(peer.sin_addr),ntohs(peer.sin_port));
        ProcessConnect(new_sock, &peer);
    }
    return 0;
}

这里写图片描述
服务端多线程版

//服务器基本流程
//从socket中读取数据(请求)
//2 根据请求计算生成  (回应)
//3 把回应写入到客户端
//由于目前实现的是  echo_server。省略 回应的步骤
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>

typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

void* ThreadEntry(void* arg)
{
    int new_sock=(int)arg;
    while(1)
    {
        //a 从客户端读取数据
        char buf[1024]={0};
        ssize_t read_size=read(new_sock, buf, sizeof(buf)-1);
        if(read_size<0)
        {
            perror("read");
            continue;
        }
        if(read_size==0)
        {
            //TCP中说明,对端关闭了连接
            printf("[client %lu] disconnetc!\n", new_sock);
            close(new_sock);
            return NULL;
        }
        buf[read_size]='\0';
        //b 根据请求计算响应(忽略)
        printf("[client %lu] %s \n", new_sock, buf);
        //c 把响应写回到客户端
        write(new_sock, buf,strlen(buf));
    }
    return NULL;
}

void ProcessConnect(int new_sock)
{
    //创建线程完成和客户端的交互
    //
    //完成一次连接的处理
    //需要循环的处理客户端发送来的数据
    pthread_t tid;
    pthread_create(&tid, NULL,ThreadEntry,(void*)new_sock);
    pthread_detach(tid);
}
int main(int argc, char* argv[])
{
    if(argc!=3)
    {
        printf("usage ./server [ip] [port]\n");
        return 1;
    }

    //1 创建socket
    int listen_sock=socket(AF_INET,SOCK_STREAM,0);
    if(listen_sock<0)
    {
        perror("socket");
        return 1;
    }

    //2 绑定端口号
    sockaddr_in server;
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=inet_addr(argv[1]);
    server.sin_port=htons(atoi(argv[2]));
    int ret=bind(listen_sock, (sockaddr*)&server, sizeof(server));
    if(ret<0)
    {
        perror("bind");
        return 1;
    }

    //3 使用listen 允许服务器被客户端连接
    ret =listen(listen_sock, 5);
    if(ret<0)
    {
        perror("listen");
        return 1;
    }
    //4 服务器初始化完成,进入事件循环
    printf("Server Init OK!\n");
    while(1)
    {
        sockaddr_in peer;
        socklen_t len=sizeof(peer);
        int new_sock=accept(listen_sock, (sockaddr*)&peer, &len);
        if(new_sock<0)
        {
            perror("accept");
            continue;
        }
        printf("[client %d] connect!\n", new_sock);
        ProcessConnect(new_sock);
    }
    return 0;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值