博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux高级编程day10 笔记
阅读量:5731 次
发布时间:2019-06-18

本文共 8826 字,大约阅读时间需要 29 分钟。

一.TCP的编程模型

 回顾:
  UDP模型的UML图
  TCP模型的UML图
 案例1:
  TCP的服务器(在案例中使用浏览器作为客户程序)  
 socket建立服务器的文件描述符号缓冲
 bind把IP地址与端口设置到文件描述符号中
 listen负责根据客户连接的不同IP与端口,负责生成对应的文件描述符号及其信息
 accept一旦listen有新的描述符号产生就返回,否则阻塞。

View Code
//tcpserver.c#include 
#include
#include
#include
#include
#include
#include
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
  
 案例5:
  TCP数据发送的分析:
    基本数据int short long float  double
    结构体数据struct
    建议使用MSG_WAITALL
    字符串数据以及文件数据等不固定长度的数据怎么发送?
    
  制定数据包:
     头:大小固定(数据大小)
     体:大小变化(数据)  
 案例6:
   使用TCP传送文件
   定义文件数据包.
     int 数据大小;
     char[]数据
       
   传递文件名
   传递数据(循环)
   传递0长度的数据表示文件结束
代码如下:

View Code
//demo1Client.c//发送端的代码#include 
#include
#include
#include
#include
#include
#include
#include
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");}

 

View Code
//demo1server.c//接收服务器的代码#include 
#include
#include
#include
#include
#include
#include
#include
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.在子进程中,接收服务器传递其他客户聊天信息

View Code
//chatclient.c//聊天程序客户端#include 
#include
#include
#include
#include
#include
#include
#include
#include
WINDOW*winfo,*wmsg;int fd;int r;struct sockaddr_in dr;int isover=1;int initSocket(); //初始化:创建描述符,绑定IPvoid 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}
View Code
//chatserver.c//聊天程序服务器端#include 
#include
#include
#include
#include
#include
#include
#include
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.客户退出,服务器也能够正确结束客户连接.

转载于:https://www.cnblogs.com/tangzhengyue/archive/2012/07/30/2615551.html

你可能感兴趣的文章
<JavaScript语言精粹>-读书笔记(一)
查看>>
NPM教程
查看>>
Java学习笔记(40)——Java集合12之fail-fast
查看>>
Centos 配置IP的方式
查看>>
Go 的吉祥物,萌不萌
查看>>
【iOS】AFN网络请求通过获取cookies保持会话
查看>>
Java 的swing.GroupLayout布局管理器的使用方法和实例
查看>>
Android中Activity和Fragment的生命周期的对比
查看>>
C++Primer_笔记_异常处理
查看>>
分区交换 alter table exchange partition 在线表 历史表交换
查看>>
思科三层交换 HSRP 热备 配置方法
查看>>
zabbix详解:(二)添加被监控机器
查看>>
设计模式单列
查看>>
人像模式的灯光效果?iPhone 8开挂袭来
查看>>
Linux下MongoDB安装与配置
查看>>
DSL配置(PPPOA)
查看>>
WEBRTC执行流程
查看>>
Spring Boot 入门系列
查看>>
Spring Cloud版——电影售票系统<六>使用 Spring Cloud Config 统一管理微服务配置
查看>>
Java not support java EE1.3
查看>>