zoukankan      html  css  js  c++  java
  • socket编程(二)

    备份自Github pages socket编程(二) pusidun's blog

    流协议与粘包

    争议:粘包并非专业术语,该数据包应指应用层的包。常常由于应用层未提供数据边界,而TCP作为流式传输协议,造成客户端无法区分数据包起始所造成的现象。TCP粘包为绝对的错误说法,TCP不存在包的概念。

    产生原因:

    1.SQ_SNDBUF套接字本身有缓冲区(发送缓冲区,接收缓冲区)

    2.tcp传送的网络数据最大值MSS大小限制

    3.链路层也有MTU(最大传输单元)大小限制,如果数据包大于>MTU要在IP层进行分片,导致消息分割。

    4.tcp的流量控制和拥塞控制,也可能导致粘包

    5.tcp延迟发送机制等等

    结论:TCP/IP协议,在传输层没有处理粘包问题,必须由程序员处理

    粘包处理方案--本质上是要在应用层维护消息与消息的边界

    1.定包长

    2.包尾加 (比如ftp协议)

    3.包头加上包体长度

    4.更复杂的应用层协议

    例子:定义packet结构体

    typedef struct _packet
    {
        int len; //定义包体长度
        char buf[1024]; //定义包体
    } Packet;
    

    readn.c

    writen.c

    read、write与recv、send

    #include <sys/types.h> 
    #include <sys/socket.h>
    int recv(int sockfd,void *buf,int len,int flags)
     
    int send(int sockfd,void *buf,int len,int flags)
    
    /*
    前面的三个参数和read,write相同,第四个参数能够是0或是以下的组合:
     
    ______________________________________________________________
     
    | MSG_DONTROUTE | 不查找路由表 |
     
    | MSG_OOB       | 接受或发送带外数据 |
     
    | MSG_PEEK      | 查看数据,并不从系统缓冲区移走数据 |
     
    | MSG_WAITALL   | 等待任何数据 |
     
    |————————————————————–|
     
     
    MSG_DONTROUTE:
    是send函数使用的标志.这个标志告诉IP协议.目的主机在本地网络上面,没有必要查找路由表.这个标志一般用网络诊断和路由程式里面.
     
    MSG_OOB:
    表示能够接收和发送带外的数据.关于带外数据我们以后会解释的.
     
    MSG_PEEK:
    是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清楚系统缓冲区的内容.
    这样下次读的时候,仍然是相同的内容.一般在有多个进程读写数据时能够使用这个标志.
     
    MSG_WAITALL
    是recv函数的使用标志,表示等到任何的信息到达时才返回.
    使用这个标志的时候recv回一直阻塞,直到指定的条件满足,或是发生了错误.1
    */
    

    getsockname、getpeername

    //getsockname函数用于获取与某个套接字关联的本地协议地址
    //getpeername函数用于获取与某个套接字关联的外地协议地址
    //对于这两个函数,如果函数调用成功,则返回0,如果调用出错,则返回-1。
    #include<sys/socket.h>
    int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
    int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen); 
    
    /*
    使用这两个函数,我们可以通过套接字描述符来获取自己的IP地址和连接对端的IP地址,
    如在未调用bind函数的TCP客户端程序上,可以通过调用getsockname()函数获取由内核赋予该连接的本地IP地址和本地端口号,
    还可以在TCP的服务器端accept成功后,通过getpeername()函数来获取当前连接的客户端的IP地址和端口号。
    */
    

    gethostname、gethostbyname、gethostbyaddr

    /*
    gethostname() : 返回本地主机的标准主机名。
    gethostbyname(): 通过主机名称获取主机的信息
    gethostbyaddr(): 通过一个IPv4的地址来获取主机信息
    
    返回值
    gethostname()  : 函数成功,则返回0。如果发生错误则返回-1。错误号存放在外部变量errno中。
    gethostbyname(): 返回hostent结构体类型指针 
    gethostbyaddr(): 返回hostent结构体类型指针
    */
    #include <netdb.h>
    #include <unistd.h>
    int              gethostname(char *name, size_t len);
    struct hostent*  gethostbyname(const char *name);
    struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family);
    
    /*
    gethostname() : 
    接收缓冲区name,其长度必须为len字节或是更长,存获得的主机名。
    接收缓冲区name的最大长度
     
     
    gethostbyname(): 
    传入值是域名或者主机名,例如"www.google.cn"等等。传出值,是一个hostent的结构。
     
     
    gethostbyaddr(): 
    addr:指向网络字节顺序地址的指针。
    len: 地址的长度,在AF_INET类型地址中为4。
    type:地址类型,应为AF_INE
    */
    

    僵进程与SIGCHLD信号

    一个进程执行了exit系统调用退出时会向父进程发送SIGCHLD信号,而其父进程并没有为它收尸(调用wait或waitpid来获得它的结束状态,如进程ID、终止状态等等)的进程。

    设置僵死状态的目的是维护子进程的信息,以便父进程在以后某个时候获取。

    如何避免僵尸进程:

    1、通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。
    2、父进程调用wait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞。waitpid可以通过传递WNOHANG使父进程不阻塞立即返回。
    3、如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。
    4、通过两次调用fork。父进程首先调用fork创建一个子进程然后waitpid等待子进程退出,子进程再fork一个孙进程后退出。这样子进程退出后会被父进程等待回收,而对于孙子进程其父进程已经退出所以孙进程成为一个孤儿进程,孤儿进程由init进程接管,孙进程结束后,init会等待回收。

    wait

    //成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHIL
    #include <sys/types.h> 
    #include <sys/wait.h>
    pid_t wait(int *status);
    
    /*
    进程一旦调用了wait,就立即阻塞自己.
    wait系统调用会使父进程暂停执行,直到它的一个子进程结束为止。
    返回的是子进程的PID,它通常是结束的子进程
    状态信息允许父进程判定子进程的退出状态,即从子进程的main函数返回的值或子进程中exit语句的退出码。
    如果status不是一个空指针,状态信息将被写入它指向的位置
    */
    

    waitpid

    //返回值:如果成功返回等待子进程的ID,失败返回-1
    #include <sys/types.h> 
    #include <sys/wait.h>
    pid_t waitpid(pid_t pid, int *status, int options);
    /*
    status:如果不是空,会把状态信息写到它指向的位置,与wait一样
    
    options:允许改变waitpid的行为,最有用的一个选项是WNOHANG,它的作用是防止waitpid把调用者的执行挂起
     
    ----------------------------------------------------------------------------
    |-对于waitpid的p i d参数的解释与其值有关:
    |-
    |-pid == -1 等待任一子进程。于是在这一功能方面waitpid与wait等效。
    |-
    |-pid > 0 等待其进程I D与p i d相等的子进程。
    |-
    |-pid == 0 等待其组I D等于调用进程的组I D的任一子进程。换句话说是与调用者进程同在一个组的进程。
    |-
    |-pid < -1 等待其组I D等于p i d的绝对值的任一子进程
    ----------------------------------------------------------------------------
    */
    

    wait与waitpid区别

    在一个子进程终止前, wait 使其调用者阻塞,而waitpid 有一选择项,可使调用者不阻塞。

    waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的特定进程。

    实际上wait函数是waitpid函数的一个特例。waitpid(-1, &status, 0);

  • 相关阅读:
    大型web系统分布式架构
    与MSN聊天的PowerTalk两个示例
    PowerTalk的四个示例代码
    PowerTalk在十月份左右会有新的版本
    PowerTalk控件 制作 即时通信 聊天室 产品咨询系统 支持与MSN的控件
    PowerTalk有些对不住大家
    自动生成实体sql工具的IDEvs2005工具(源代码+程序)
    C#字符串类快速编译器
    小菜编程成长记系列
    一道简单的编程题,不过您做对了吗?
  • 原文地址:https://www.cnblogs.com/pusidun/p/14836505.html
Copyright © 2011-2022 走看看