Linux——TCP编程通信及编程模型

来源:互联网 发布:php输出乱码问题 编辑:程序博客网 时间:2024/06/06 08:31
一.TCP的编程模型
    回顾:

        UDP模型的UML图(一对多)


        TCP模型的UML图(一对一)


    案例1:
    TCP的服务器(在案例中使用浏览器作为客户程序)        
    socket建立服务器的文件描述符号缓冲
    bind把IP地址与端口设置到文件描述符号中
    listen负责根据客户连接的不同IP与端口,负责生成对应的文件描述符号及其信息

    accept一旦listen有新的描述符号产生就返回,否则阻塞。

tcp_service.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;//总的(服务器)fdint cfd;//客户fdint a;struct sockaddr_in sadr;//服务器地址struct sockaddr_in cadr;//每一个连接者的地址socklen_t len;int r;char buf[1024];//1.socketserverfd=socket(AF_INET,SOCK_STREAM,0);if(serverfd==-1) printf("1:%m\n"),exit(-1);printf("建立服务器socket成功!\n");//2.bindsadr.sin_family=AF_INET;sadr.sin_port=htons(9999);inet_aton("192.168.1.116",&sadr.sin_addr);r=bind(serverfd,(struct sockaddr*)&sadr,sizeof(sadr));if(r==-1) printf("2:%m\n"),exit(-1);printf("服务器地址绑定成功!\n");//3.listen只要有一个新的客户连上来或发送数据 listen就为该客户分配一个内存节点r=listen(serverfd,10);if(r==-1) printf("3:%m\n"),exit(-1); printf("监听服务器成功!\n");//4.accept把listen分配内存的描述符取出来 可以用循环接收多个节点        //这里只接收一个节点(代理客户描述符号)用于测试                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);//4字节缓冲填满之后才返回 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);}






二.TCP通信特点(相对于UDP)
    案例3:
        有连接:只要连接后,发送数据不用指定IP与端口
        数据无边界:TCP数据流,非数据报文.
        描述符号双工:

        数据准确:TCP协议保证数据完全正确

tcp_client.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 fd;struct sockaddr_in addr;int r;int i=0;//1.socketfd=socket(AF_INET,SOCK_STREAM,0);if(fd==-1) printf("1:%m\n"),exit(-1);printf("建立socket成功!\n");//2.connectaddr.sin_family=AF_INET;addr.sin_port=htons(9999);inet_aton("192.168.1.116",&addr.sin_addr);r=connect(fd,(struct sockaddr*)&addr,sizeof(addr));if(r==-1) printf("2:%m\n"),exit(-1);printf("连接服务器成功!\n");for(i=0;i<20;i++){send(fd,"hello",5,0);//数据没有边界的话 一个数据(结构体)可能分多次接收      //不一定接收20次 如果用UDP就是接收20次}}


    案例4:
        使用TCP发送数据注意:
            不要以为固定长的数据,一定接收正确,要求使用MSG_WAITALL来保证
        
    案例5:
        TCP数据发送的分析:
            基本数据int short long float  double
            结构体数据struct
            建议使用MSG_WAITALL
            字符串数据以及文件数据等不固定长度的数据怎么发送?
                
        制定数据包:
            头:大小固定(数据大小)
            体:大小变化(数据)        
    案例6:
        使用TCP传送文件
        定义文件数据包.
                int 数据大小;
                char[]数据
                        
        传递文件名
        传递数据(循环)

        传递0长度的数据表示文件结束

domo1Client.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(){int sfd;int ffd;int size;int r;int  len;char buf[128];struct sockaddr_in dr;char filename[]="udp_a.c"; //1.建立socketsfd=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.1.116",&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;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(){int sfd,cfd,ffd;int r;int len;char buf[128];char filename[100];struct sockaddr_in dr;//1.建立服务器socketsfd=socket(AF_INET,SOCK_STREAM,0);if(sfd==-1) printf("1:%m\n"),exit(-1);printf("建立服务器成功!\n");//2.绑定IP地址与端口dr.sin_family=AF_INET;dr.sin_port=htons(9988);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"),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);if(cfd==-1) printf("4:%m\n"),close(sfd),exit(-1);printf("开始接收文件!\n");//5.接收文件名r=recv(cfd,&len,sizeof(len),MSG_WAITALL);printf("文件名长度:%d\n",len);r=recv(cfd,filename,len,MSG_WAITALL);filename[len]=0;printf("传递的文件名是:%s\n",filename);//6.创建文件ffd=open(filename,O_RDWR|O_CREAT,0666);//7.循环接收文件数据while(1){r=recv(cfd,&len,sizeof(len),MSG_WAITALL);if(len==0) break;r=recv(cfd,buf,len,MSG_WAITALL);write(ffd,buf,len);}close(ffd);close(cfd);close(sfd);printf("接收文件完毕!\n");}
          

三.TCP服务器编程模式
        TCP的服务器端维护多个客户的网络文件描述符号.
        对服务器多个客户描述符号同时做读操作,是不可能.需要多任务模型完成.
        多任务模型?
        1.多进程
        2.IO的异步模式(select模式/poll模式)
        3.多线程模式        
        4.多进程池
        5.线程池

四.综合应用--多进程应用
        1.怎样使用多进程

        2.多进程的缺陷,以及怎么解决


服务器端

建立socket
            绑定地址
            监听
            循环接收客户连接
            为客户创建子进程
            在子进程接收该客户的数据,并且广播

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.建立服务器 socketfds=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.1.116");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);}
 客户端
            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.<span style="font-size:18px;">建立socket            绑定地址            监听            循环接收客户连接            为客户创建子进程            在子进程接收该客户的数据,并且广播</span>h>#include <curses.h>#include <signal.h>WINDOW*winfo,*wmsg;int fd;int r;struct sockaddr_in dr;int isover=1;int initSocket();void initUI(); void destroy();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.1.116");r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));if(r==-1){close(fd);return -1;}return 0;}       

           

总结:
        1.TCP的四大特点
        2.TCP的数据接收:固定长与变长数据的接收
        3.TCP的服务器多进程处理
            问题:多进程由于进程资源结构独立.
            新进程的文件描述符号的上下文环境在老进程无法访问
0 0
原创粉丝点击