• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0
            一.TCP的編程模型
             回顧:
              UDP模型的UML圖
              TCP模型的UML圖
             案例1:
              TCP的服務器(在案例中使用瀏覽器作為客戶程序)  
             socket建立服務器的文件描述符號緩沖
             bind把IP地址與端口設置到文件描述符號中
             listen負責根據客戶連接的不同IP與端口,負責生成對應的文件描述符號及其信息
             accept一旦listen有新的描述符號產生就返回,否則阻塞。 

            //tcpserver.c
            #include <stdio.h>
            #include <stdlib.h>
            #include <string.h>
            #include <unistd.h>
            #include <sys/socket.h>
            #include <netinet/in.h>
            #include <arpa/inet.h>
            main()
            {
                int serverfd;
                int cfd;
                int a;
                struct sockaddr_in sadr;
                struct sockaddr_in cadr;
                socklen_t len;
                int r;
                char buf[1024];
                //1.socket
                serverfd=socket(AF_INET,SOCK_STREAM,0);
                if(serverfd==-1) printf("1:%m\n"),exit(-1);
                printf("建立服務器socket成功!\n");
                //2.bind
                sadr.sin_family=AF_INET;
                sadr.sin_port=htons(9999);
                inet_aton("192.168.180.92",&sadr.sin_addr);
                r=bind(serverfd,
                        (struct sockaddr*)&sadr,sizeof(sadr));
                if(r==-1) printf("2:%m\n"),exit(-1);
                printf("服務器地址綁定成功!\n");
                
                //3.listen
                r=listen(serverfd,10);
                if(r==-1) printf("3:%m\n"),exit(-1);
                printf("監聽服務器成功!\n");
                
                //4.accept
                len=sizeof(cadr);
                cfd=accept(serverfd,
                        (struct sockaddr*)&cadr,&len); //每接受一個新的連接,就會返回一個新的文件描述符,來分辨是哪個連接。
                printf("有人連接:%d,IP:%s:%u\n",
                        cfd,inet_ntoa(cadr.sin_addr),
                        ntohs(cadr.sin_port));        
                
                //5.處理代理客戶描述符號的數據
                while(1)
                {
                    r=recv(cfd,&a,4,MSG_WAITALL);        
                    if(r>0)
                    {
                        //buf[r]=0;
                        printf("::%d\n",a);
                    }
                    
                    if(r==0)
                    {
                        printf("連接斷開!\n");
                        break;
                    }
                    if(r==-1)
                    {
                        printf("網絡故障!\n");
                        break;
                    }
                }
                close(cfd);
                close(serverfd);
            }

            案例2:
               每個客戶的代理描述符號的通信

            二.TCP通信特點(相對于UDP)
             案例3:
              有連接:主要連接后,發送數據不用指定IP與端口
              數據無邊界:TCP數據流,非數據報文.
              描述符號雙工:
              數據準確:TCP協議保證數據時完全正確
             案例4:
              使用TCP發送數據注意:
                不要以為固定長的數據,一定接收正確,要求使用MSG_WAITALL(必須等待得到指定緩存長度recv才返回)
              
             案例5:
              TCP數據發送的分析:
                定長數據:
                  基本數據int short long float  double
                  結構體數據struct
                  建議使用MSG_WAITALL
                
                不定長數據:
                  字符串數據以及文件數據等不固定長度的數據怎么發送?   
                  制定數據包:
                    頭:大小固定(數據大小)
                    體:大小變化(數據)  
             案例6:
               使用TCP傳送文件
               定義文件數據包.
                 int 數據大小;
                 char[]數據
                   
               傳遞文件名
               傳遞數據(循環)
               傳遞0長度的數據表示文件結束
            代碼如下:


            //demo1Client.c
            //發送端的代碼
            #include <stdio.h>
            #include <stdlib.h>
            #include <unistd.h>
            #include <string.h>
            #include <sys/socket.h>
            #include <netinet/in.h>
            #include <arpa/inet.h>
            #include <fcntl.h>
            main()
            {
                //1. 建立socket
                
            //2. 連接到服務器
                
            //3. 打開文件
                
            //4. 發送文件名
                
            //5. 循環發送文件
                
            //6. 讀取到文件尾,發送0數據包
                int sfd; //socket描述符
                int ffd; //文件描述符
                int size;   //讀取和發送文件的長度
                int r;  //函數返回值
                
                int  len;  //要發送的文件名的長度
                char buf[128]; //數據的緩存
                
                struct sockaddr_in dr;  //網絡地址
                char filename[]="udp_a.c";  //文件名
                
                
            //1.建立socket
                sfd=socket(AF_INET,SOCK_STREAM,0);
                if(sfd==-1) 
                    printf("1:%m\n"),exit(-1);
                printf("socket成功!\n");
                //2.連接到服務器
                dr.sin_family=AF_INET;
                dr.sin_port=htons(9988);
                inet_aton("192.168.180.92",&dr.sin_addr);
                r=connect(sfd,(struct sockaddr*)&dr,sizeof(dr));
                if(r==-1) 
                    printf("2:%m\n"),close(sfd),exit(-1);    
                printf("connect成功!\n");
                //3.打開文件
                ffd=open(filename,O_RDONLY);
                if(ffd==-1) 
                    printf("3:%m\n"),close(sfd),exit(-1);
                printf("open文件成功!\n");
                //4.發送文件名
                len=strlen(filename);    
                r=send(sfd,&len,sizeof(len),0);//發送文件名長度(告訴流,文件名占多長)
                r=send(sfd,filename,len,0);//發送文件名 
                if(r==-1)
                printf("4:%m\n"),close(ffd),close(sfd),exit(-1);
                printf("發送文件名成功!\n");
                //5.循環發送數據
                while(1)
                {
                    size=read(ffd,buf,128);
                    if(size==-1) break//read錯誤,跳出循環
                    if(size==0) break;  //讀到文件尾,跳出循環
                    if(size>0)
                    {
                        //先發送數據的長度,再發送數據
                        
            //發送數據長度
                        r=send(sfd,&size,sizeof(size),0);
                        if(r==-1) break;
                        r=send(sfd,buf,size,0);//發送數據
                        if(r==-1) break;
                    }
                }
                //6.讀取到文件尾,發送0數據包
                size=0;
                r=send(sfd,&size,sizeof(size),0);
                close(ffd);
                close(sfd);
                printf("OK!\n");
            }

            //demo1server.c
            //接收服務器的代碼
            #include <stdio.h>
            #include <stdlib.h>
            #include <unistd.h>
            #include <string.h>
            #include <sys/socket.h>
            #include <netinet/in.h>
            #include <arpa/inet.h>
            #include <fcntl.h>
            main()
            {
                //1. 建立服務器socket
                
            //2. 綁定IP地址與端口
                
            //3. 監聽
                
            //4. 接收連接
                
            //5. 接收文件名
                
            //6. 創建文件
                
            //7. 循環接收文件數據
                
                int sfd, cfd, ffd;
                int r;
                int len;
                char buf[128];  //發送端定義的緩存大小是128,這里最好不要小于128
                char filename[100];
                struct sockaddr_in dr;
                //1. 建立服務器socket
                sfd = socket(AF_INET, SOCK_STREAM, 0);
                if(sfd == -1)
                    printf("1:%m\n"), exit(-1);
                printf("socket服務器創建成功!\n");
                //2. 綁定IP地址與端口
                dr.sin_family = AF_INET;
                dr.sin_port = htons(9988);
                inet_aton("192.168.180.92", &dr.sin_addr); //dr.sin_addr.s_addr = inet_addr("192.168.180.92");注意區別
                r = bind(sfd, (struct sockaddr*)&dr, sizeof(dr));
                if(r == -1)
                    printf("2:%m"), close(sfd), exit(-1);
                printf("綁定地址成功!\n");
                //3. 監聽
                r = listen(sfd, 10);
                if(r == -1)
                    printf("3:%m\n"), close(sfd), exit(-1);
                printf("監聽成功!\n");
                //4. 接收連接
                cfd = accept(sfd, 0, 0); //這里我們不關心發送端的IP等信息,所以后面兩個參數都為0。這里返回一個新的描述符,代表接收的連接
                if(cfd == -1)
                    printf("4:%m\n"), close(sfd), exit(-1);
                printf("開始接收文件!\n");
                //5. 接收文件名
                r = recv(cfd, &len, sizeof(len), MSG_WAITALL);  //接收文件名的長度
                r = recv(cfd, filename, len, MSG_WAITALL);  //根據文件名長度,接收文件名
                filename[r] = 0;  //在文件名后面加結束符
                
            //6. 創建文件
                ffd = open(filename, O_CREAT|O_RDWR, 0666);   //如果文件存在,直接覆蓋.不要和發送文件放在同一個目錄運行,會覆蓋發送文件
                if(ffd == -1)
                    printf("6:%m\n"), close(sfd), close(cfd), exit(-1);
                printf("創建文件成功!\n");
                //7. 循環接收文件數據
                while(1)
                {
                    r = recv(cfd, &len, sizeof(len), MSG_WAITALL);
                    if(len == 0)
                        break//長度為0,表示文件傳送完畢的信號
                    r = recv(cfd, buf, len, MGS_WAITALL);
                    r = write(ffd, buf, len);
                }
                close(ffd);
                close(cfd);
                close(sfd);
                printf("接收數據完畢!\n");
            }

            PS:UDP面向無連接,TCP面向連接,所以推薦UDP不用connect,直接sendto, 而TCP則先連接,然后send,而不是sendto。

            三.TCP服務器編程模式
              TCP的服務器端維護多個客戶的網絡文件描述符號.
              對服務器多個客戶描述符號同時做讀操作,是不可能.需要多任務模型完成.
              多任務模型?
              1.多進程
              2.IO的異步模式(select模式/poll模式)
              3.多線程模式  
              4.多進程池
              5.線程池

            四.綜合應用--多進程應用
              1.怎樣使用多進程
              2.多進程的缺陷,以及怎么解決

            小例子:用TCP寫一個聊天程序
               客戶端
                 2.1.建立socket
                 2.2.連接服務器
                 2.3.創建CURSES界面
                 2.4.創建子進程
                 2.5.在父進程中,輸入,發送聊天信息
                 2.6.在子進程中,接收服務器傳遞其他客戶聊天信息


            //chatclient.c
            //聊天程序客戶端
            #include <stdio.h>
            #include <stdlib.h>
            #include <string.h>
            #include <unistd.h>
            #include <sys/socket.h>
            #include <netinet/in.h>
            #include <arpa/inet.h>
            #include <curses.h>
            #include <signal.h>
            WINDOW*winfo,*wmsg;
            int fd;
            int r;
            struct sockaddr_in dr;
            int isover=1;

            int initSocket();   //初始化:創建描述符,綁定IP
            void initUI();   //初始化curses界面
            void destroy();  //清理:釋放UI, 關閉網絡
            void handle(int s)
            {
                int status;
                wait(&status);
                destroy();
                exit(-1);    
            }
            main()
            {    
                //printf("網絡初始化成功!\n");
                initUI();
                r=initSocket();    
                if(r==-1) exit(-1);
                signal(SIGCHLD,handle);
                if(fork())
                {
                    //父進程,輸入,發送
                    char buf[256];
                    while(1)
                    {
                        mvwgetstr(wmsg,1,1,buf);
                        //buf[r]=0;
                        send(fd,buf,strlen(buf),0);            
                        //wclear(wmsg);
                        
            //box(wmsg,0,0);
                        refresh();
                        wrefresh(wmsg);
                        wrefresh(winfo);
                    }
                }
                else
                {
                    
                    //子進程,接收,顯示
                    char buf[256];
                    int line=1;
                    while(1)
                    {            
                        r=recv(fd,buf,255,0);
                        if(r==-1) break;
                        if(r==0) break;
                        buf[r]=0;
                        mvwaddstr(winfo,line,1,buf);
                        line++;            
                        if(line>=(LINES-3))
                        {
                            wclear(winfo);
                            line=1;
                            box(winfo,0,0);                
                        }
                        
                        
                        wmove(wmsg,1,1);
                        touchwin(wmsg);
                        refresh();
                        wrefresh(winfo);            
                        wrefresh(wmsg);
                    }
                    exit(-1);
                }
                    
                destroy();
            }
            void destroy()
            {
                close(fd);
                endwin();
            }
            void initUI()
            {
                initscr();
                winfo=derwin(stdscr,(LINES-3),COLS,0,0);
                wmsg=derwin(stdscr,3,COLS,LINES-3,0);
                keypad(stdscr,TRUE);
                keypad(wmsg,TRUE);
                keypad(winfo,TRUE);
                box(winfo,0,0);
                box(wmsg,0,0);
                refresh();
                wrefresh(winfo);
                wrefresh(wmsg);
            }

            int initSocket()
            {
                fd=socket(AF_INET,SOCK_STREAM,0);
                if(fd==-1) return -1;
                    
                dr.sin_family=AF_INET;
                dr.sin_port=htons(9989);
                dr.sin_addr.s_addr=inet_addr("192.168.180.92");
                r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));
                if(r==-1)
                {
                    close(fd);
                    return -1;
                }
                return 0;  //fd是全局變量,不用返回。初始化成功,返回0
            }

            //chatserver.c
            //聊天程序服務器端
            #include <stdio.h>
            #include <stdlib.h>
            #include <string.h>
            #include <unistd.h>
            #include <sys/socket.h>
            #include <netinet/in.h>
            #include <arpa/inet.h>
            #include <sys/mman.h>
            int sfd;
            int *fds;//存放所有客戶代理描述符號
            int idx=0;//客戶在數組中下標
            struct sockaddr_in dr;
            int r;
            main()
            {
                //1. 建立服務器socket
                
            //2. 綁定地址
                
            //3. 監聽
                
            //4. 循環接收客戶連接
                
            //5. 建立一個子進程
                
            //6. 子進程任務:接收客戶數據并且廣播
                
                
                
            //1.建立服務器 socket
                fds=mmap(0,4*100,PROT_READ|PROT_WRITE,
                    MAP_ANONYMOUS|MAP_SHARED,0,0);
                bzero(fds,sizeof(fds));
                sfd=socket(AF_INET,SOCK_STREAM,0);
                if(sfd==-1) printf("1:%m\n"),exit(-1);    
                printf("socket OK!\n");
                //2.綁定地址
                dr.sin_family=AF_INET;
                dr.sin_port=htons(9989);
                dr.sin_addr.s_addr=inet_addr("192.168.180.92");
                r=bind(sfd,(struct sockaddr*)&dr,sizeof(dr));
                if(r==-1) printf("2:%m\n"),exit(-1);
                printf("bind ok!\n");
                //3.監聽
                r=listen(sfd,10);
                if(r==-1) printf("3:%m\n"),exit(-1);
                printf("listen ok!\n");
                //4.循環接收客戶連接
                while(1)
                {
                    fds[idx]=accept(sfd,0,0);
                    if(fds[idx]==-1) break;
                    printf("有客戶連接:%d\n",fds[idx]);
                    //5.建立一個子進程
                    if(fork())
                    {
                        idx++;
                        continue;
                    }
                    else
                    {        
                        //6.子進程任務:接收客戶數據并且廣播
                        char buf[256];
                        int i;
                        printf("開始接收客戶數據:%d\n",fds[idx]);
                        while(1)
                        {
                            //接收客戶數據
                            r=recv(fds[idx],buf,255,0);
                            printf("%d\n",r);
                            if(r==0)
                            {
                                printf("有客戶退出\n");
                                close(fds[idx]);
                                fds[idx]=0;
                                break;                    
                            }
                            if(r==-1)
                            {
                                printf("網絡故障\n");
                                close(fds[idx]);
                                fds[idx]=0;
                                break;
                            }
                            buf[r]=0;
                            printf("來自客戶的數據:%s\n",buf);
                            //廣播
                            for(i=0;i<100;i++)
                            {
                                if(fds[i]>0)
                                {
                                    send(fds[i],buf,r,0);
                                }    
                            }
                        }
                        exit(0);
                    }
                }
                close(sfd);    
            }

            總結:
               建立socket
               綁定地址
               監聽
               循環接收客戶連接
               為客戶創建子進程
               在子進程接收該客戶的數據,并且廣播

            總結:
              1.TCP的四大特點
              2.TCP的數據接收:固定長與變長數據的接收
              3.TCP的服務器多進程處理
                 問題:多進程由于進程資源結構獨立.
                     新進程的文件描述符號的環境在老進程無法訪問?

            作業:
              思考:
                有什么編程技巧可以解決進程的文件描述符號的一致?
                
              作業:
                完成TCP的聊天程序.
                  1.數據能運行
                  2.處理僵死進程
                  3.服務器退出,客戶也能正常結束
                  4.客戶退出,服務器也能夠正確結束客戶連接.

            曰曰摸天天摸人人看久久久| 亚洲中文字幕久久精品无码喷水| 久久国产精品-久久精品| 久久精品国产一区| 区久久AAA片69亚洲| 国产精品久久久久无码av| 久久无码人妻精品一区二区三区| 伊人久久大香线蕉av不变影院| 伊人久久综在合线亚洲2019| 亚洲av成人无码久久精品| 精品欧美一区二区三区久久久| 精品一二三区久久aaa片| 精品久久久久久无码中文字幕| 久久亚洲精品无码AV红樱桃| 深夜久久AAAAA级毛片免费看| 精品综合久久久久久97超人 | 激情五月综合综合久久69| 久久人人添人人爽添人人片牛牛| 青青国产成人久久91网| 亚洲午夜久久久久妓女影院 | 亚洲精品无码久久久久去q | 久久精品国产色蜜蜜麻豆| 国产精品99久久久久久猫咪 | 一本久久精品一区二区| 久久国产成人亚洲精品影院| 久久久久久久99精品免费观看| 久久精品国产网红主播| 色综合久久久久久久久五月| 一本色综合网久久| 久久综合噜噜激激的五月天| 久久99久久99精品免视看动漫| 久久国产亚洲精品| 久久99久久99精品免视看动漫| 久久99这里只有精品国产| 色天使久久综合网天天| 欧美激情精品久久久久久久九九九 | 亚洲国产欧美国产综合久久| 亚洲国产香蕉人人爽成AV片久久| 婷婷久久综合九色综合九七| 无码8090精品久久一区| 久久久久波多野结衣高潮|