欢迎使用CSDN-markdown编辑器

来源:互联网 发布:c语言产生0 1的随机数 编辑:程序博客网 时间:2024/06/04 00:56

网络编程笔记

  • 网络编程笔记
    • 网络编程基础结构体
      • 1. 相关结构体:
      • 2. 网络字节序
      • 3. 字节操纵函数
      • 4. 判断一个文件描述符是不是套接字
    • 基本TCP套接字编程
      • 1. 相关函数
        • 1. socket函数(创建一个套接字)
        • 2. connect函数
        • 3. bind函数(给套接字绑定地址和端口)
        • 4. listen函数(开始在地址上监听相关端口)
        • 5. accept函数
        • 6. fork和exec
        • 7. getsockname和getpeername
      • 2.posix 信号处理
        • 1. SIGHCLD信号(处理僵尸进程)
        • 2.信号集
        • 3.信号处理函数signal和sigaction
        • 4.SIG_CHLD 信号处理
      • 3. 实例 (并发服务器)

网络编程基础结构体

1. 相关结构体:

  • IPv4结构体
struct in_addr{    in_addr_t s_addr;   /*32 bit IPV4 address*/}/*头文件<netinet/in.h>*/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地址转换函数
    //头文件<arpa/inet.h>     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);

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);

3. 字节操纵函数

    /*第一组起源于4.2bsd*/    //头文件<string.h>    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 提供*/    //头文件<string.h>    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);

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

  • 通过fstat获取该文件的st_mode字段并与上S_IFSOCK判断
#include <stdio.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/stat.h>#include <string.h>#include <arpa/inet.h>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,失败返回0int isfdtype(int fd,int fdtype);

基本TCP套接字编程

1. 相关函数

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

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

2. connect函数

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

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

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

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

//返回0成功-1出错#include <sys/socket.h>int listen(int sockfd,int backlog);

5. accept函数

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

6. fork和exec

#include <unistd.h>//返回值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[])

7. getsockname和getpeername

/**    getsockname 用于获取服务器端套接字的信息    getperrname 用于获取客户端套接字的细信息*///返回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)

2.posix 信号处理

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

  1. 在子进程结束时如果父进程未等待它,将产生一个僵尸进程,若在子进程结束时父进程已经结束,则不会产生
    僵尸进程,该进程会变成孤儿进程,进而被托管给init。
    为了避免产生僵尸进程,占用系统资源,我们需要在父进程中调用wait或waitpid来为子进程收尸。
  2. 当进程正常终止或异常终止时,内核就会向其父进程发送SIGCHLD信号,因为子进程终止是一个异步事件,所
    以通知也是异步通知。
  3. 在调用wait和waitpid时进程可能会发生一下集中情况
    • 如果所有的子进程都还在运行,则阻塞。
    • 如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回
    • 如果它没有任何子进程则立即返回错误
  4. 在服务器变成中我们不能在父进程中调用wait或waitpid函数,因为会造成父进程阻塞。所以我们只能
    监听异步信号SIGCHLD

2.信号集

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

//信号集操作函数#include<signal.h>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

3.信号处理函数signal和sigaction

因为signal的语义与函数具体实现有关,所以应该使用sigaction函数代替signal。
一般有一些特殊的信号处理回调函数
+ SIG_IGN 忽略该信号(SIGKILL 和 SIGSTOP 不可以忽略)
+ SIG_DFL 系统默认处理
+ SIG_ERR 错误信号

#include <signal.h>//成功返回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函数已声明一个替换栈,则此信号递送给替换栈上的进程

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);    }}

3. 实例 (并发服务器)

#include <stdio.h>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <errno.h>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);        }    }}
0 0