TCP套接字(server/client实现)

套接字

  • 概念:源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器和服务。
  • 分类:
    • 流套接字(SOCK_STREAM)
    • 数据报套接字(SOCK_DGRAM)
    • 原始套接字(SOCK_RAW)

TCP套接字(流套接字)

  • TCP套接字工作流程:
    • 首先,服务器端启动进程,调用Socket创建一个基于TCP协议的流套接字描述符。
    • 其次,服务进程调用bind命名套接字,将套接字描述符绑定到本地地址和本地端口上。
    • 再次,服务器端调用listen,开始侦听客户端的Socket连接请求。
    • 接下来,客户端创建套接字描述符,并且调用connect向服务器端提交连接请求。服务器端接收到客户端连接请求后,调用accept,接受并创建一个新的套接字描述符与客户端建立连接,然后原套接字描述符继续侦听客户端的连接请求。
    • 客户端与服务器端新套接字进行数据传输,调用write或send向对方发送数据,调用read或recv接收数据。
  • 代码
server.c
#include
#include
#include
#include
#include
#include
#include
#include
#include



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

int start_up(const char* _ip,int _port)
{
    int sk = socket(AF_INET,SOCK_STREAM,0);
    if(sk < 0)
    {
        perror("socket");
        exit(2);
    }

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(_port);
    local.sin_addr.s_addr = inet_addr(_ip);

    if(bind(sk,(struct sockaddr *)&local,sizeof(local)) < 0)
    {
        perror("bind");
        exit(3);
    }

    if(listen(sk,10) < 0)
    {
        perror("listen");
        exit(4);
    }
    return sk;
}
void* handlerquest(void*arg)
{
// close(listen_sk);
    int new_sk = (int)arg;
    while(1)
    {
        char buf[1024];
        ssize_t s = read(new_sk,buf,sizeof(buf)-1);
        if(s > 0)
        {
            buf[s] = 0;
            printf("client:%s\n",buf);
            write(new_sk,buf,strlen(buf));
        }
        else
        {
            close(new_sk);
            printf("client quit ...\n");
            break;
        }
    }
}

int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        return 1;
    }

    int listen_sk = start_up(argv[1],atoi(argv[2]));
    while(1)
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);

        int new_sk = accept(listen_sk,(struct sockaddr*)(&client),&len);
        if(new_sk < 0)
        {
            perror("accept");
            continue;
        }

        printf("Get a new client %s : %d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
//多线程
        pthread_t id;
        pthread_create(&id,NULL,handlerquest,(void*)new_sk);
        pthread_detach(id);
//多进程
    // pid_t id = fork();

    // if(id < 0)
    // {
    // close(new_sk);
    // }
    // else if(id == 0)
    // {
    // close(listen_sk);
    // if(fork()<0)
    // {
    // exit(0);
    // }

    // while(1)
    // {
    // char buf[1024];
    // ssize_t s = read(new_sk,buf,sizeof(buf)-1);
    // if(s > 0)
    // {
    // buf[s] = 0;
    // printf("client:%s\n",buf);
    // write(new_sk,buf,strlen(buf));
    // }
    // else
    // {
    // close(new_sk);
    // printf("client quit ...\n");
    // break;
    // }
    // }
    // close(new_sk);
    // }
    // else
    // {
    // close(new_sk);
    // }

//普通版
    // while(1)
    // {
    // char buf[1024];
    // ssize_t s = read(new_sk,buf,sizeof(buf)-1);
    // if(s > 0)
    // {
    // buf[s] = 0;
    // printf("client:%s\n",buf);
    // write(new_sk,buf,strlen(buf));
    // }
    // else
    // {
    // close(new_sk);
    // printf("client quit ...\n");
    // break;
    // }
    // }
    }
    return 0;
}
client.c
#include
#include
#include
#include
#include
#include
#include


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


int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        return 1;
    }
    int sk = socket(AF_INET,SOCK_STREAM,0);
    if(sk <0 )
    {
        perror("socket");
        return 1;
    }

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));
    server.sin_addr.s_addr = inet_addr(argv[1]); 

    if(connect(sk,(struct sockaddr*)&server,sizeof(server))<0)
    {
        perror("connect");
        exit(1);
    }

    char buf[1024];
    while(1)
    {
        printf("Please Enter#:");
        fflush(stdout);

        ssize_t s = read(0,buf,sizeof(buf)-1);
        if(s > 0)
        {
            buf[s-1] = 0;
            write(sk,buf,strlen(buf));
            s = read(sk,buf,sizeof(buf)-1);
            if(s > 0)
            {
                buf[s] = 0;
                printf("server echo# %s\n",buf);
            }
        }
    }
    return 0;
}
makefile
.PHONY:all
all:tcp_server tcp_client

tcp_client:tcp_client.c
    gcc -o $@ $^ 
tcp_server:tcp_server.c
    gcc -o $@ $^ -lpthread
.PHONY:clean
clean:
    rm -f tcp_client tcp_serve
服务器退出,客户端不退出,当再次运行服务器(与之前的端口号相同),就会出现绑定失败。
  • 原因:
    主动关闭的一方在发送最后一个ack 后就会进入TIME_WAIT状态 停留2MSL(max segment lifetime)时间。这个TCP/IP必不可少的,也就是“解决”不了的。也就是TCP/IP设计者本来是这么设计的。主要有两个原因
    • 防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
    • 可靠的关闭TCP连接
      在主动关闭方发送的最后一个ack(fin) ,有可能丢失,这时被动方会重新发 fin, 如果这时主动方处于CLOSED 状态 ,就会响应rst 而不是ack。所以主动方要处于TIME_WAIT 状态,而不能是CLOSED 。
      TIME_WAIT 并不会占用很大资源的,除非受到攻击。还有,如果一方send 或recv 超时,就会直接进入CLOSED 状态
      [http://blog.csdn.net/acs713/article/details/28427181]

你可能感兴趣的:(网络,webbench总结)