关闭

UNP-基本TCP编程-2(复用技术)

标签: 编程tcp技术
132人阅读 评论(0) 收藏 举报
分类:

tcp 协议基本信息:

tcp报头

选项子段:(tcp/ip : p192)

  • 选项表结束:
  • 无操作:
  • 最大报文长度
  • 窗口扩大因子:

TCP连接的建立与中止:

– – 连接的建立:

22:22:46.510758 IP (tos 0x0, ttl 64, id 29789, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.1.86.56132 > 192.168.1.80.8068: Flags [S], cksum 0x8425 (incorrect -> 0xdf97), 
      seq 4036226398, win 29200, options [mss 1460,sackOK,TS val 10656003 ecr 0,nop,wscale 7], length 0
22:22:46.511781 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.1.80.8068 > 192.168.1.86.56132: Flags [S.], cksum 0x1ac4 (correct), 
    seq 519747379, ack 4036226399, win 14480, options [mss 1460,sackOK,TS val 3745757 ecr 10656003,nop,wscale 6], length 0
22:22:46.511843 IP (tos 0x0, ttl 64, id 29790, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.1.86.56132 > 192.168.1.80.8068: Flags [.], cksum 0x841d (incorrect -> 0x8139), seq 1, ack 1, win 229, options [nop,nop,TS val 10656004 ecr 3745757], length 0
  • 最大报文长度:
    • 当一个连接建立是, 双方告知双方的 MSS 来协商最大窗口选项
  • 连接请求队列:

    • 等待连接的一段有一个固定长度的连接队列, 该队列中的连接已被TCP接受,但还没有被应用层所接受。
    • 应用层只有在三次握手重第三个报文段受到后才会知道这个新的连接,
    • 如果对于新的连接,连接队列中没有空间,TCP将不理会受到的SYN,也不发送任何报文段
    • 当用户层接受一个连接时, 他将从队列中移出;

    说明:
    TCP 服务器无法使客户进程的主动打开失效。当一个新的客户连接传递给服务器的应用程序时,TCP的 三次握手链接已经结束

  • Nagle 算法:

    • 该算法应对小分组增加拥赛出现的问题
    • 该算法要求一个TCP链接上最多只能有一个未被确认的未完成的小分组,该分组的确认到达之前不能发送其他的的小分组,相反, TCP收集这些少量的分组,并在确认到来时以一个分组的方式发出去;
  • RTT 的計算
    ERR = M – A
    a ← A + 1/8 Err
    D ← D + 1/4(Err| -D)
    RTO = A + 4D

  • 数据的交互:
    慢启动, 快重传, 拥赛避免策略 p235
  • 坚持计时器
    当对方的通告窗口为0是的测试窗口的策略(TCP/IP) 245

客户端

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>

void err_que(const char *msg)
{
    puts(msg);
    exit(0);
}

void err_sys(const char *msg)
{
    printf("%s:%s\n", msg, strerror(errno));
    exit(errno);
}

int main(int argc, char *argv[])
{
    int                                         sockfd;
    int                                         recv_len;
    int                                         fsize;
    int                                         index;
    int                                         cursize;
    int                                         file_fd;
    int                                         read_len;
    char                                        *file_name;
    char                                        filemsg[128];
    char                                        strbuff[128];
    struct sockaddr_in          serviceaddr;
    struct stat                         file_msg;
    struct timespec                 timestart, timeend;

    if(argc != 3)
        err_que("client Ipaddress filename");

    file_name = argv[2] + strlen(argv[2]) -1;
    while(*(file_name-1) != '/')
        file_name--;

    if(stat(file_name, &file_msg) != -1)
    {
        if(!S_ISREG(file_msg.st_mode))
            err_que("file is exist in locale and it isn't a regeular file");

        while(1)
        {
                write(STDOUT_FILENO, "file is exist are you replace it ?? (y|n) :\n", strlen("file is exist are you replace it ?? (y|n) :\n"));
                switch (getchar()) {
                    case 'y':
                    case 'Y':
                                        break;
                    case 'n':
                    case 'N':
                                        exit(0);
                                        break;
                    default:
                                    while(getchar()!='\n');
                                    continue;
                }
                break;
        }
    }
    else if(errno != ENOENT)
        err_sys("stat file error");

    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        err_sys("socket error");


    memset(&serviceaddr, 0, sizeof(serviceaddr));
    serviceaddr.sin_family = AF_INET;
    serviceaddr.sin_port    = htons(8068);
    if(inet_pton(AF_INET, argv[1], (void *)&serviceaddr.sin_addr.s_addr) == -1)
        err_sys("inet_pton error");

    // 链接服务器
    if(connect(sockfd, (struct sockaddr *)&serviceaddr, sizeof(serviceaddr)) == -1)
        err_sys("connect error");

    if(send(sockfd, argv[2], strlen(argv[2]), 0) == -1)
        err_sys("send error");

    memset(filemsg, 0, sizeof(filemsg));
    if(recv(sockfd, filemsg, sizeof(filemsg), 0) == -1)
        err_sys("recv error");

    if(!strncmp(filemsg, "file is not exist", strlen("file is not exist")))
            err_que("file is not exist in service");
    if(!strncmp(filemsg, "file read false", strlen("file read false")))
                err_que("file read false");

    sscanf(filemsg, "file size : %d", &fsize);

    if((file_fd = open(file_name, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH)) == -1)
        err_sys("file open error");

    write(STDOUT_FILENO,"File is transport now :\n", strlen("File is transport now :\n"));
    write(STDOUT_FILENO,"==========================================================================================\n",
                             strlen("==========================================================================================\n"));
    if(clock_gettime(CLOCK_REALTIME, &timestart) == -1)
        err_sys("clock_gettime error");

    cursize = 0;    // 当前已经读取的大小
    index   = 0;    // 当前的点数

    while(read_len=recv(sockfd, strbuff, sizeof(strbuff), 0))
    {
        int i;

        if(read_len == -1)
            err_sys("read_error");


        cursize += read_len;

        for(i =0; i < (int)((double)cursize/fsize * 90)-index; i++)
            write(STDOUT_FILENO, "#", 1);
        index = (int)((double)cursize/fsize * 90);

        if(write(file_fd, strbuff, read_len) == -1)
            err_sys("send error");
    }
    if(clock_gettime(CLOCK_REALTIME, &timeend) == -1)
        err_sys("clock_gettime error");

    printf("\nUsetime : %d:%d\n", timeend.tv_sec - timestart.tv_sec, timeend.tv_nsec-timestart.tv_nsec);
    close(sockfd);
    return 0;
}

服务器端

#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/epoll.h>
#include <sys/resource.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>

#define LOGEFILE "./.saligia_ftp_log"       // 日志文件
#define LOCKFILE "./.saligia_ftp_lock"  // 文件互斥锁

// 测试是否有相同的进程开启
void test_service(void);
// 开启守护进程
void daemon_set(int stdfd);
// 向客户端发送数据请求
void *serv_to_client(void *msg);
// 获取当地时间
void get_time(char *msg);
// 中止信号处理
void sigint_chose(int signo);

void err_sys(const char *msg)
{
    char err_msg[128];
    memset(err_msg, 0, sizeof(err_msg));
    sprintf(err_msg, "%s:%s\t", msg, strerror(errno));
    get_time(err_msg);
    write(STDOUT_FILENO, err_msg, strlen(err_msg));

    exit(errno);
}
void err_que(const char *msg)
{
    char err_msg[128];

    memset(err_msg, 0, sizeof(err_msg));
    sprintf(err_msg, "%s\t", err_msg);
    get_time(err_msg);
    write(STDOUT_FILENO, err_msg, strlen(err_msg));
    exit(0);
}

typedef struct{
    char file_name[128];
    int  client_sock;
}CLIMSG, *PCLIMSG;

int main(int argc, char *argv)
{
    int                                     logfd;
    int                                     service_fd;
    int                                     epoll_fd;
    int                                     epoll_len;
    int                                     index;
    int                                     sockopton;
    pthread_t                           pthclient;
    int                                     clientaddrlen;
    int                                     clientsockfd;
    char                                    err_msg[128];
    char                                    log_msg[128];
    char                                    file_name[128];
    char                                    ip_addr[INET_ADDRSTRLEN];
    struct sockaddr_in      service_addr;
    struct sockaddr_in      client_addr;
    struct epoll_event      set_event;
    struct epoll_event      listen_events[10];
    struct timespec             nowtime;
    struct tm                           *showtime;
    struct stat                     filemsg;
    pthread_attr_t              pthread_msg;

    //创建日至记录文件
    if((logfd = open(LOGEFILE, O_RDWR|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR)) == -1)
        err_sys("open error");

    daemon_set(logfd);

    // 创建服务器socket
    if((service_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
        err_sys("socket error");
    // 创建监听池来接受数据
    if((epoll_fd = epoll_create(10)) == -1)
        err_sys("epoll error");

    // 绑定套接字
    memset(&service_addr, 0, sizeof(service_addr));
    service_addr.sin_family         = AF_INET;
    service_addr.sin_port           = htons(8068);
    service_addr.sin_addr.s_addr    = INADDR_ANY;

    sockopton = 1;
    if(setsockopt(service_fd, SOL_SOCKET, SO_REUSEADDR, &sockopton, sizeof(sockopton)) == -1)
        err_sys("setsockopt error");

    if(bind(service_fd, (struct sockaddr*)&service_addr, sizeof(service_addr)) == -1)
        err_sys("bind error");

    if(listen(service_fd, 10) == -1)
        err_sys("listen error");

    // 将 service 套接字加入监听池
    memset(&set_event, 0, sizeof(set_event));
    set_event.events    = EPOLLIN|EPOLLOUT;
    set_event.data.fd   = service_fd;

    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, service_fd, &set_event) == -1)
        err_sys("epoll_ctl error");

    while(1)
    {
        // 监听套接字事件
        if((epoll_len = epoll_wait(epoll_fd, listen_events, 10, 10000000)) == -1)
            err_sys("epoll_wait error");

        for(index=0; index<epoll_len; index++)
        {
            //来自 LISTEN 套接字的事件
            if(listen_events[index].data.fd == service_fd)
            {
                if(listen_events[index].events & EPOLLERR)
                {
                    memset(err_msg, 0, sizeof(err_msg));
                    sprintf(err_msg,"listen_events error : %s\n", strerror(errno));
                    if(write(STDOUT_FILENO, err_msg, strlen(err_msg)) == -1)
                        err_sys("write error");
                }
                else if(listen_events[index].events & EPOLLIN)
                {
                    clientaddrlen=sizeof(client_addr);

                    if((clientsockfd = accept(service_fd, (struct sockaddr *)&client_addr, &clientaddrlen)) == -1)
                            err_sys("accept error");

                    memset(log_msg, 0, sizeof(log_msg));
                    sprintf(log_msg, "%-15s:%05d\t\t%-30s\t",
                        inet_ntop(AF_INET, (void *)&client_addr.sin_addr.s_addr, ip_addr, INET_ADDRSTRLEN),
                        ntohs(client_addr.sin_port), "client connect");
                    get_time(log_msg);

                    if(write(STDOUT_FILENO, log_msg, strlen(log_msg)) == -1)
                        err_sys("write error");

                    set_event.events = EPOLLIN | EPOLLERR;
                    set_event.data.fd = clientsockfd;

                    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, clientsockfd, &set_event) == -1)
                        err_sys("epoll_ctl error");
                }
            }
            else // 普通用户的连接请求
            {
                int clientfd = listen_events[index].data.fd;
                if(listen_events[index].events & EPOLLERR)
                {
                    memset(log_msg, 0, sizeof(log_msg));
                    sprintf(log_msg, "client link error\t");
                    get_time(log_msg);
                    if(write(STDOUT_FILENO, log_msg, strlen(log_msg)) == -1)
                        err_sys("write error");

                    if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL,clientfd , &listen_events[index]) == -1)
                            err_sys("epoll_ctl error");

                    close(clientfd);
                }
                else if(listen_events[index].events & EPOLLIN)
                {
                    memset(file_name, 0, sizeof(file_name));
                    if(recv(clientfd, file_name, sizeof(file_name), 0) == -1)
                            err_sys("recv error");

                    // 记录用户请求文件信息
                    clientaddrlen = sizeof(client_addr);
                    if(getpeername(clientfd, (struct sockaddr *)&client_addr, &clientaddrlen) == -1)
                        err_sys("getpeername error");

                    if(inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_addr, INET_ADDRSTRLEN) == NULL)
                        err_sys("inet_ntop error");

                    memset(log_msg, 0, sizeof(log_msg));
                    sprintf(log_msg, "%-15s:%05d\t\t%-30s\t",ip_addr, ntohs(client_addr.sin_port),file_name);
                    get_time(log_msg);

                    if(write(STDOUT_FILENO, log_msg, strlen(log_msg)) == -1)
                            err_sys("write error");

                    if(stat(file_name, &filemsg) == -1)
                    {
                        if(errno = ENOENT)
                        {
                            send(clientfd, "file is not exist", strlen("file is not exist"), 0);
                            if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL,clientfd , &listen_events[index]) == -1)
                                err_sys("epoll_ctl error");
                            close(clientfd);
                        }
                        else
                            err_sys("stat error");
                    }
                    else if(access(file_name, R_OK) == -1)
                    {
                        if(errno == EACCES)
                        {
                            send(clientfd, "file read false", strlen("file read false"), 0);
                            if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL,clientfd , &listen_events[index]) == -1)
                                err_sys("epoll_ctl error");
                            close(clientfd);
                        }
                        else
                        err_sys("access error");
                    }

                    else
                    {
                        char SND_MSG[128];
                        memset(SND_MSG, 0, sizeof(SND_MSG));
                        sprintf(SND_MSG, "file size : %d", filemsg.st_size);
                        if(send(clientfd, SND_MSG, strlen(SND_MSG), 0) == -1)
                            err_sys("send error");

                        PCLIMSG climsg = (PCLIMSG)malloc(sizeof(CLIMSG));

                        memset(climsg, 0, sizeof(CLIMSG));
                        strcpy(climsg->file_name, file_name);
                        climsg->client_sock=clientfd;

                        if(pthread_attr_init(&pthread_msg) == -1)
                            err_sys("pthread_attr_init error");
                        if(pthread_attr_setdetachstate(&pthread_msg, PTHREAD_CREATE_DETACHED) == -1)
                            err_sys("pthread_attr_setdetached error");
                        if(pthread_create(&pthclient, &pthread_msg, serv_to_client, climsg) != 0)
                            err_sys("pthread_create error");

                        if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL,clientfd , &listen_events[index]) == -1)
                                err_sys("epoll_ctl error");
                    }
                }
            }
        }
    }

    return 0;
}
void test_service(void)
{
    int                     lockfd;
    struct flock    lockmsg;

    if((lockfd = open(LOCKFILE, O_RDWR)) == -1)
        err_sys("file open error");

    memset(&lockmsg, 0, sizeof(lockmsg));
    lockmsg.l_type      = F_WRLCK;
    lockmsg.l_whence    = SEEK_SET;
    lockmsg.l_start     = 0;
    lockmsg.l_len       = 0;

    if(fcntl(lockfd, F_SETLK, &lockmsg) == -1)
    {
        if(errno == EAGAIN)
            exit(0);
        else
            err_sys("fcntl F_SETLK error");
    }

}
void daemon_set(int stdfd)
{
    int                         pid;
    int                         index;
    char                        log_msg[128];
    struct rlimit   filerlimit;
    sigset_t                sigblock;

    // 屏蔽终端字
    sigemptyset(&sigblock);
    sigaddset(&sigblock, SIGHUP);
    if(sigprocmask(SIG_BLOCK, &sigblock, NULL) == -1)
        err_sys("sigprocmask error");

    // 关闭所有的以打开的文件描述符
    if(getrlimit(RLIMIT_NOFILE, &filerlimit) == -1)
        err_sys("getrlimit error");
    for(index = 0; index < filerlimit.rlim_cur; index++)
        if(index != stdfd)
            close(index);

    // 重定义标准输入输出流
    if(fcntl(stdfd, F_DUPFD, 0) == -1)
        err_sys("fcntl error");
    if(fcntl(stdfd, F_DUPFD, 1) == -1)
        err_sys("fcntl error");
    if(fcntl(stdfd, F_DUPFD, 2) == -1)
        err_sys("fcntl error");

    if((pid = fork()) == -1)
        err_sys("fork error");

    if(pid != 0)
        exit(0);

    setsid();

    if((pid = fork()) == -1)
        err_sys("fork error");
    if(pid != 0)
        exit(0);

    test_service();

    memset(log_msg, 0, sizeof(log_msg));
    sprintf(log_msg, "service is opening\t");
    get_time(log_msg);

    if(write(STDOUT_FILENO, log_msg, strlen(log_msg)) == -1)
        err_sys("write error");
}
void *serv_to_client(void *msg)
{
    char                                    msg_buf[128];
    char                                    sysmsg[128];
    int                                     req_fd;
    int                                     read_len;
    int                                     clientaddrlen;
    sigset_t                        sigmask;
    struct timespec             starttime, endtime;

    PCLIMSG climsg = (PCLIMSG) msg;

    sigemptyset(&sigmask);
    sigaddset(&sigmask, SIGPIPE);


    if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) != 0)
        err_sys("pthread_sigprocmask error");

    if((req_fd = open(climsg->file_name, O_RDONLY)) == -1)
        err_sys("open error");

    if(clock_gettime(CLOCK_REALTIME, &starttime) == -1)
        err_sys("clock_gettime error");

    while(read_len = read(req_fd, msg_buf, sizeof(msg_buf)))
    {
        if(read_len == -1)
            err_sys("read error");
        if(send(climsg->client_sock, msg_buf, read_len, 0) == -1)
            break;
    }

    if(clock_gettime(CLOCK_REALTIME, &endtime) == -1)
        err_sys("clock_gettime error");

    memset(sysmsg, 0, sizeof(sysmsg));
    if(read_len == 0)
        sprintf(sysmsg, "%-40s\t file transport success :\t\t %d:%d\n",
                        climsg->file_name, endtime.tv_sec-starttime.tv_sec, endtime.tv_nsec-starttime.tv_nsec);
    else
        sprintf(sysmsg, "%-40s\t file transport false :\t\t %d:%d\n",
                    climsg->file_name, endtime.tv_sec-starttime.tv_sec, endtime.tv_nsec-starttime.tv_nsec);

    write(STDOUT_FILENO, sysmsg, strlen(sysmsg));

    close(req_fd);
    close(climsg->client_sock);
    free(climsg);

    return NULL;
}

void get_time(char *msg)
{
    struct timespec timenow;
    struct tm           *showtime;
    char                        *p;

    if(msg == NULL)
        err_que("time buff is null");

    p = msg + strlen(msg);

    if(clock_gettime(CLOCK_REALTIME, &timenow) == -1)
        err_sys("clock_gettime error");

    if((showtime = localtime(&timenow.tv_sec)) == NULL)
        err_que("localtime error");

    sprintf(p, "%02d-%02d  %02d:%02d:%02d\n", showtime->tm_mon+1, showtime->tm_mday,
                                showtime->tm_hour, showtime->tm_min, showtime->tm_sec);
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    我的微信
    个人资料
    • 访问:12809次
    • 积分:819
    • 等级:
    • 排名:千里之外
    • 原创:72篇
    • 转载:4篇
    • 译文:0篇
    • 评论:2条
    我的 QQ
    QQ 邮箱