Tcp服务器

本文介绍了如何使用socket编程搭建TCP服务器,包括单进程、多进程及多线程服务器的实现方式,并探讨了TIME_WAIT状态的存在意义。

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

tcp服务器(socket)

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <wait.h>

void usage(char* proc)//此函数用来打印使用方法
{
    printf("usage: %s [local_ip] [port]\n",proc);
}

int startup(const char *op,int port)//启动监听套接字
{
    int sock = socket(AF_INET,SOCK_STREAM,0);//创建一个套接字

    if(sock<0)//创建失败返回
    {
        perror("socket");
        exit(-1);
    }

    struct sockaddr_in local;//定义一个ipv4的结构体
    local.sin_family = AF_INET;
    local.sin_port = htons(port);//端口
    local.sin_addr.s_addr = inet_addr(op);//ip

    if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)//绑定套接字
    {
        perror("bind");
        exit(-2);
    }

    if(listen(sock,5)<0)//开启监听
    {
        perror("listen");
        exit(-3);
    }
    return sock;//返回绑定且监听的文件描述符
}
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        return 1;
    }
    int sock = startup(argv[1],atoi(argv[2]));

    while(1)
    {
        //char buf[1024];
        struct sockaddr_in client;//输出型结构体
        socklen_t len = sizeof(client);
        int client_sock;
        if((client_sock = accept(sock,(struct sockaddr*)&client,&len)) < 0)
        {
            perror("accept");
            exit(-4);
        }
        printf("get new client...\n");//连接成功


        while(1)
        {
            char buf[1024];
            size_t i = 0;
            if((i=read(client_sock,buf,sizeof(buf)-1)) <= 0)
            {
                printf("read done...\n");
                exit(-5);
            }
            else
            {
                buf[i]=0;
                printf("client:%s",buf);
                write(client_sock,buf,sizeof(buf));
            }
        }
        //read(client_sock,buf,sizof(buf-1));
        //printf("client:%s\n",buf);
        close(client_sock);
    }
    close(sock);
    return 0;
}

上面是一个简单的tcp服务器,但是一次只能连一个,也就是阻塞式等待,下面改进一下,编写一个多进程服务器。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <wait.h>

void usage(char* proc)
{
    printf("usage: %s [local_ip] [port]\n",proc);
}

int startup(const char *op,int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);

    if(sock<0)
    {
        perror("socket");
        exit(-1);
    }

     struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    local.sin_addr.s_addr = inet_addr(op);

    if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
    {
        perror("bind");
        exit(-2);
    }

    if(listen(sock,5)<0)
    {
        perror("listen");
        exit(-3);
    }
    return sock;
}
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        return 1;
    }
    int sock = startup(argv[1],atoi(argv[2]));

    while(1)
    {
        //char buf[1024];
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        //client.sin_family = AF_INET;
        //client.sin_port = ntohs(atoi(argv[2]));
        //client.sin_addr.s_addr = inet_ntoa();
        int client_sock;
        if((client_sock = accept(sock,(struct sockaddr*)&client,&len)) < 0)
        {
            perror("accept");
            exit(-4);
        }
        printf("get new client...\n");

        pid_t pid = fork();
        if(pid < 0)
        {
            close(client_sock);
            exit(-5);
        }
        else if(pid == 0)
        {
            close(sock);
            if(fork() > 0)
            {
                exit(-1);
            }

            while(1)
            {
                char buf[1024];
                size_t i = 0;
                if((i=read(client_sock,buf,sizeof(buf)-1)) <= 0)
                {
                    printf("read done...\n");
                    exit(-5);
                }
                else
                {
                    buf[i]=0;
                    printf("client:%s",buf);
                    write(client_sock,buf,sizeof(buf));
                }
            }
        }
        else
        {
            close(client_sock);
        }
       /* while(1)
        {
            char buf[1024];
            size_t i = 0;
            if((i=read(client_sock,buf,sizeof(buf)-1)) <= 0)
            {
                printf("read done...\n");
                exit(-5);
            }
            else 
            {
                buf[i]=0;
                printf("client:%s",buf);
                write(client_sock,buf,sizeof(buf));
            }
        }*/
        //read(client_sock,buf,sizof(buf-1));
        //printf("client:%s\n",buf);
        //close(client_sock);
    }
    //close(sock);
    return 0;
}

多进程服务器编写完成,可是占用内存空间大,因此出现了多线程服务器,更加快速的访问服务器。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <wait.h>
#include <pthread.h>

void usage(char* proc)
{
    printf("usage: %s [local_ip] [port]\n",proc);
}

int startup(const char *op,int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);

    if(sock<0)
    {
        perror("socket");
        exit(-1);
    }

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    local.sin_addr.s_addr = inet_addr(op);

    if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
    {
        perror("bind");
        exit(-2);
    }

    if(listen(sock,5)<0)
    {
        perror("listen");
        exit(-3);
    }
    return sock;
}
    void* huddler(void* arg)
    {
        int new_fd =(int)arg;
            while(1)
            {
                char buf[1024];
                size_t i = 0;
                if((i=read(new_fd,buf,sizeof(buf)-1)) <= 0)
                {
                    printf("read done...\n");
                    exit(-5);
                }
                else
                {
                    buf[i]=0;
                    printf("client:%s",buf);
                    write(new_fd,buf,sizeof(buf));
                }
            }
    }
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        return 1;
    }
    int sock = startup(argv[1],atoi(argv[2]));

    while(1)
    {
        //char buf[1024];
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        //client.sin_family = AF_INET;
        //client.sin_port = ntohs(atoi(argv[2]));
        //client.sin_addr.s_addr = inet_ntoa();
        int client_sock;
        if((client_sock = accept(sock,(struct sockaddr*)&client,&len)) < 0)
        {
            perror("accept");
            exit(-4);
        }
        printf("get new client...\n");

        pthread_t id;
        pthread_create(&id,NULL,huddler,(void*)client_sock);
        pthread_detach(id);
    }
    //close(sock);
    return 0;
}

以上就是简单的tcp服务器。
当一个客户向服务器或者服务器向客户发起关闭连接的请求时,会进入time_wait等待时间,这时服务器如果先关闭,无法立即从新绑定套接字,会停留在time_wait时间,等待丢失或者没有收到的数据,这就是所谓的四次握手最后一次。

TIME_WAIT状态存在的理由

TCP/IP协议就是这样设计的,是不可避免的。主要有两个原因:
1)可靠地实现TCP全双工连接的终止
TCP协议在关闭连接的四次握手过程中,最终的ACK是由主动关闭连接的一端(后面统称A端)发出的,如果这个ACK丢失,对方(后面统称B端)将重发出最终的FIN,因此A端必须维护状态信息(TIME_WAIT)允许它重发最终的ACK。如果A端不维持TIME_WAIT状态,而是处于CLOSED 状态,那么A端将响应RST分节,B端收到后将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。
因而,要实现TCP全双工连接的正常终止,必须处理终止过程中四个分节任何一个分节的丢失情况,主动关闭连接的A端必须维持TIME_WAIT状态 。

2)允许老的重复分节在网络中消逝
TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个迟到的迷途分节到达时可能会引起问题。在关闭“前一个连接”之后,马上又重新建立起一个相同的IP和端口之间的“新连接”,“前一个连接”的迷途重复分组在“前一个连接”终止后到达,而被“新连接”收到了。为了避免这个情况,TCP协议不允许处于TIME_WAIT状态的连接启动一个新的可用连接,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个新TCP连接的时候,来自旧连接重复分组已经在网络中消逝。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值