即日起在codingBlog上分享您的技术经验即可获得积分,积分可兑换现金哦。

欢迎运用CSDN-markdown编辑器

编程语言 u014181657 15℃ 0评论
本文目录
[隐藏]

1.网络编程笔记

1.1.网络编程基础结构体

1.1.1.1. 相关结构体:

  • IPv4结构体
struct in_addr{
    in_addr_t s_addr;   /*32 bit IPV4 address*/
}
/*头文件*/
struct sockaddr_in{
    uint8_t sin_len;
    sa_family_t sin_family;
    in_port_t sin_port;
    struct in_addr sin_addr;
    char sin_zero[8];
}
  • 通用ipv4结构体
struct sockaddr{
   uint8_t sa_len;
   sa_family_t sa_family;
   char sa_data[14];
}
  • 32位二进制ipv4地址与字符格式的ipv4地址转换函数
    //头文件
     char * inet_ntoa(struct in_addr in);
     int inet_aton(const char*cp,struct in_addr *inp);
     /*下面两个函数已经废弃*/
     in_addr_t inet_addr(const char*cp);
     in_addr_t inet_network(const char*cp);
     /*下面一对函数即支持ipv4也支持ipv6*/
    int inet_pton(int af,const char *src,void *dst);
    const char * int_ntop(int af,const void *src,char*dst,socklen_t size);

1.1.2.2. 网络字节序

  • 计算机内存存储中有两种字节序,大端字节序和小端字节序,将低字节存储在起始位置称为小端,


    将高字节存储在起始位置称为大端
  • 字节序相关函数
    uint16_t htons(uint16_t host16bitvalue);
    uint32_t htonl(uint32_t host32bitvalue);

    uint16_t ntohs(uint16_t net16bitvalue);
    uint32_t ntohl(uint32_4 net32bitvalue);

1.1.3.3. 字节操纵函数

    /*第一组起源于4.2bsd*/
    //头文件
    void bzero(void * dest,size_t nbytes);
    void bcopy(const void *src,void  *dest,size_t nbytes);
    //返回0相等,非0不相等
    int bcmp(const void *ptr1,const void * ptr2,size_t nbytes);

    /*第二组ANSI C 提供*/
    //头文件
    void * memset(void *dest,size_t len);
    void * memcpy(void * dest,const void *src,size_t len);
    int memcmp(const void *ptr1,const void *ptr2,size_t len);

1.1.4.4. 判断一个文件描述符是不是套接字

  • 通过fstat获取该文件的st_mode字段并与上S_IFSOCK判断
#include 
#include 
#include 
#include 
#include 
#include 
int main() {
    struct sockaddr_in servaddr;
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8090);
    int fd= socket(AF_INET,SOCK_STREAM,0);
    struct stat mystat;
    if(fstat(fd,&mystat)==0){
        if(S_ISSOCK(mystat.st_mode)){
            printf("is sock");
        }
    }
    return 0;
}
  • 通过调用isfdtype函数
//成功返回1,失败返回0
int isfdtype(int fd,int fdtype);

1.2.基本TCP套接字编程

1.2.1.1. 相关函数

1.2.1.1.1. socket函数(创建一个套接字)

//非负关键字成功,-1出错
#include 
int socket(int family,int type,int protocol);

1.2.1.2.2. connect函数

//返回0成功,-1出错
#include 
int connect(int sockfd,const struct sockaddr*servaddr,socklen_t addrlen);

1.2.1.3.3. bind函数(给套接字绑定地址和端口)

//返回0成功-1出错
#include 
int bind(int sockfd,const struct sockaddr * servaddr,socklen_t addrlen)

1.2.1.4.4. listen函数(开始在地址上监听相关端口)

//返回0成功-1出错
#include 
int listen(int sockfd,int backlog);

1.2.1.5.5. accept函数

//返回非负值成功,-1出错
#include 
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t addrlen);

1.2.1.6.6. fork和exec

#include 
//返回值0子进程,>0父进程,-1 出错
pid_t fork(void);

int execl(const char *pathname,const char * arg0,...)
int execv(const char *pathname,char* const argv[])
int execle(const char *pathname,const char *arg0,...,NULL,char * const envp[])
int execve(cconst char *pathname,char * const argv[],char* const envp[])
int execlp(const char * filename,const char *arg0,...)
int execvp(const char *filename,char * const argv[])

1.2.1.7.7. getsockname和getpeername

/**
    getsockname 用于获取服务器端套接字的信息
    getperrname 用于获取客户端套接字的细信息
*/
//返回0成功,-1出错
#include 
int getsockname(int sockfd,struct sockaddr *localaddr,socklen_t * addrlen)
int getpeername(int sockfd,struct sockaddr *peeraddr,socklen_t * addrlen)

1.2.2.2.posix 信号处理

1.2.2.1.1. SIGHCLD信号(处理僵尸进程)

  1. 在子进程结束时如果父进程未等待它,将产生一个僵尸进程,若在子进程结束时父进程已经结束,则不会产生


    僵尸进程,该进程会变成孤儿进程,进而被托管给init。


    为了避免产生僵尸进程,占用系统资源,我们需要在父进程中调用wait或waitpid来为子进程收尸。
  2. 当进程正常终止或异常终止时,内核就会向其父进程发送SIGCHLD信号,因为子进程终止是一个异步事件,所


    以通知也是异步通知。
  3. 在调用wait和waitpid时进程可能会发生一下集中情况

    • 如果所有的子进程都还在运行,则阻塞。
    • 如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回
    • 如果它没有任何子进程则立即返回错误
  4. 在服务器变成中我们不能在父进程中调用wait或waitpid函数,因为会造成父进程阻塞。所以我们只能


    监听异步信号SIGCHLD

1.2.2.2.2.信号集

当需要表示多个信号时,就需要信号集了。


信号集类型 sigset_t

//信号集操作函数
#include
int sigemptyset(sigset_t *set)
int sigfiilset(sigset_t * set)
int sigaddset(sigset *set,int signo);
int sigdelset(sigset_t *set,int signo);
                                                //成功返回0,-1出错
int sigismember(const sigset_t *set,int signo)
                                                //若真返回1,假返回0

1.2.2.3.3.信号处理函数signal和sigaction

因为signal的语义与函数具体实现有关,所以应该使用sigaction函数代替signal。


一般有一些特殊的信号处理回调函数


+ SIG_IGN 忽略该信号(SIGKILL 和 SIGSTOP 不可以忽略)


+ SIG_DFL 系统默认处理


+ SIG_ERR 错误信号

#include 
//成功返回0,-1出错
void (*signal(int signo,void (*fun)(int))(int)
int sigaction(int signo,const struct sigaction * restrict act,struct sigaction * restrict oact)
struct sigaction{
    void (*sa_handler)(int); //信号处理回调函数
    sigset_t sa_mask;   //信号掩码,设置阻塞信号。
    int sa_flags;
    void (*sa_sigaction)(int,siginfo_t *,void *);
}

sa_flags 的取值

选项 说明
SA_INTERRUPT 由此信号中断的系统调用不自动重启动
SA_NOCLD_STOP 若signo是SIGCHLD时若是作业控制则不产生该信号
SA_NOCLDWAIT 若signo是SIGCHLD时,则当子进程终止时不产生僵尸进程,若调用wait将返回-1,发生ECHCLD错误
SA_NODEFER 当捕捉到该信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号(除非sa_mask设置了该信号)
SA_ONSTACK 当用sigaltstack函数已声明一个替换栈,则此信号递送给替换栈上的进程

1.2.2.4.4.SIG_CHLD 信号处理

当SIG_CHLD信号到达时,应该在signal处理函数中调用wait


或者waitpid避免产生僵尸进程,但是wait存在缺陷,当n个> SIG_CHLD信号同时到达时,可能触发1-n次wait函数,导致


有些子进程成为僵尸进程。

//一个信号处理函数
void sid_chld(int signo){
    pid_t pid;
    int stat;
    while((pid= waitpid(-1,&stat,WNOHANG))>0){
        printf("child process %d terminated\n",pid);
    }
}

1.2.3.3. 实例 (并发服务器)

#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char * argv[]){
    int sockfd=0,connfd=0;
    pid_t pid;
    struct sockaddr_in servaddr,cliaddr,localaddr,perraddr;
    bzero(&servaddr,sizeof(servaddr));
    bzero(&cliaddr,sizeof(cliaddr));
    bzero(&localaddr,sizeof(localaddr));
    bzero(&perraddr,sizeof(perraddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(8090);
    if((sockfd = socket(AF_INET,SOCK_STREAM,0))<0){
        printf("socket error,%s",strerror(errno));
        _exit(-1);
    };
    socklen_t len = sizeof(servaddr);
    if(bind(sockfd,(struct sockaddr*)&servaddr,len)<0){
        printf("bind error,%s",strerror(errno));
        _exit(-1);
    }
    if(listen(sockfd,5)<0){
        printf("listen error,%s",strerror(errno));
        _exit(-1);
    }
    len = sizeof(localaddr);
    if(getsockname(sockfd,(struct sockaddr*)&localaddr,&len)==0){
        char buf[16];
        bzero(buf,sizeof(buf));
        printf("绑定地址%s,绑定端口%d",inet_neta(localaddr.sin_addr.s_addr,buf,sizeof(buf)),ntohs(localaddr.sin_port));
    }
    for(;;){
        len = sizeof(cliaddr);
       if((connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len))<0){
           printf("accept error,%s",strerror(errno));
           _exit(-1);
       }
        if((pid = fork())==0){
            len = sizeof(localaddr);
            if(getpeername(connfd,(struct sockaddr*)&perraddr,&len)==0){
                char buf[16];
                bzero(buf,sizeof(buf));
                printf("绑定地址%s,绑定端口%d",inet_neta(localaddr.sin_addr.s_addr,buf,sizeof(buf)),ntohs(localaddr.sin_port));
            }
            printf("子进程正在处理链接并执行了ls");
            execlp("/bin/ls","ls -al",NULL);
        }else if(pid <0){
            printf("fork error,%s",strerror(errno));
            _exit(-1);
        }else{
            close(connfd);
        }

    }
}

转载请注明:CodingBlog » 欢迎运用CSDN-markdown编辑器

喜欢 (0)or分享 (0)
发表我的评论
取消评论

*

表情