• 基本套接字总结(@function)


    最近学习了下UNIX下的网络编程。为了以后查询方便,总结在这里。

    首先套接字的地址定义:

       IPv4地址和IPv6地址定义见<netinet/in.h>头文件定义。为了能够顺利转换不同的套接字内容,可以查看<sys/socket.h>中定义的通用套接字struct sockaddr;在使用过成中我们可以将struct sockaddr_in 和 sockaddr_in6直接强制转换成struct sockaddr.

    连接过程中我们需要人工指定对应网络地址。而不同的主机实现中存在不同的数据格式(big-endian OR little-endian),我们需要通过如下转换函数来保证数据转换过程中的正确性。

    函数列表如下:   

    #include <netinet/in.h>
    //from host byte order to network byte order
    uint16_t htons(uint16_t  host16bitvalue)
    //servaddr.sin_addr.s_addr = htonl(INADDR_ANY);   uint32_t htonl(uint32_t host32bitvalue)
    //from network byte order to host byte order uint16_t ntohs(uint16_t host16bitvalue) uint16_t ntohl(uint16_t host16bitvalue)

     地址转换函数使用:

    #include <arpa/inet.h>
    
    int inet_pton(int family, const char* strptr, void *addrptr);  //成功返回1,格式错误返回0,出错返回-1
    //示例::Inet_pton(AF_INET, argv[1], &serveraddr.sin_addr);
    const char* inet_ntop(int family, const void*addrptr, char *strptr, size_t len); //len参数指出缓存区的大小,避免出现溢出

    基本TCP套接字编程示例程序如下(这里我们read,write等系统IO操作来实现网络回射服务):

    其中涉及到的网络编程函数包括:socket(),bind(),bzero(),inet_pton(), listen(),connect(),accept()

    服务过程中socket()指定所要进行操作的网络服务是什么, socket(指定协议族, 操作类型, 采用协议)

    协议族包括:AF_INET:Pv4;  AF_INET6:IPv6;  AF_LOCAL:UNIX域协议;  AF_ROUTE:路由套接字;  AF_KEY:密钥套接字

    操作类型:SOCK_STREAM:字节流;  SOCK_DGRAM:数据报;  SOCK_SEQPACKET:有序分组;  SOCK_RAW:原始套接字

    采用协议:IPPROTO_TCP...._UDP...._SCTP

    需要注意的问题:

      Fork子进程后,connfd和listenfd的引用次数变成2,所以需要在子进程和父进程中同时关闭才能保证完全关闭。

      多个子进程并发后,会在服务器上产生大量的僵死进程,从而使得大量的服务器资源浪费。为此我们需要捕获僵死进程的信号SIGCHLD,并做相应的处理。因为UNIX中信号不排队的设计,采用wait处理完第一个僵死进程的信号后其余的进程信号丢失。从而清理不彻底。为此,这里采用waitpid函数进行处理。

    首先,服务器程序:

    #include "unp.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    void str_echo(int sockfd);
    Sigfunc* signal1(int signo, Sigfunc* func);
    void sig_child(int signo);
    int main(int argc, char** argv){
        int listenfd, connfd;
        char buff[MAXLINE];
        pid_t childpid;
        socklen_t clilen;
    
        struct sockaddr_in cliaddr, servaddr;
    
        //<sys/socket.h>
        //int socket(int family, int type, int protocal);
        listenfd = Socket(AF_INET, SOCK_STREAM, 0);
        bzero(&servaddr, sizeof(struct sockaddr_in));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(SERV_PORT);
    
        Bind(listenfd, (SA*) &servaddr, sizeof(servaddr));
    
        Listen(listenfd, LISTENQ);
    
    
        //working with the 
        signal1(SIGCHLD, sig_child);
    
        while(1){
            clilen = sizeof(cliaddr);
            connfd = Accept(listenfd, (SA*)&cliaddr, &clilen);
            if ((childpid = Fork()) == 0){
                Close(listenfd);
                str_echo(connfd);
                exit(0);
            }
            Close(connfd);
        }
    
        return 0;
    }
    
    
    void str_echo(int sockfd){
        ssize_t n;
        char buf[MAXLINE];
    again:
        while((n = read(sockfd, buf, MAXLINE) ) > 0)
            Write(sockfd, buf, n);
        if (n < 0 && errno == EINTR)
            goto again;
        else if (n < 0)
            err_sys("str_echo: read error");
    
    }
    
    Sigfunc* signal1(int signo, Sigfunc* func){
        struct sigaction act, oact;
        act.sa_handler = func;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        if ( signo == SIGALRM){
    #ifdef SA_INTERRUPT
            //设定中断,使该信号处理过程中能够中断:
            act.sa_flags |= SA_INTERRUPT;
    #endif
        }else{
    #ifdef    SA_RESTART
            //信号处理,设置SA_RESTART标志,使得内核对失败的
            //系统调用自动重启。
            act.sa_flags |= SA_RESTART;
    #endif
        }
        
        if (sigaction(signo, &act, &oact) < 0){
            return SIG_ERR;
        }
        return (oact.sa_handler);
    }
    
    
    void sig_child(int signo){
        pid_t pid;
        int stat;
        /***********************************************
    #inlcude <sys/wait.h>
            这里使用wait函数只能处理第一个返回的僵死进程,因为其UNIX系统信号实现
            中信号是不进行排队的。所以我们采用waitpid
            并且设定最后的选项为WNOHANG.表示内核在没有进程时不仅行阻塞。
            ***********************************************/
        //  --- pid = wait(&stat);
        while(( pid = waitpid(-1, &stat, WNOHANG)) > 0)
            printf("child %d terminated.
    ", pid);
        return;
    }

    客户端程序:

    #include "unp.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    int str_cli1(FILE *fp, int sockfd);
    
    int main(int argc, char** argv){
        int sockfd[5];
        struct sockaddr_in serveraddr;
        int i;
    
        if (argc < 2)
            err_quit("Use: clisocket <IPaddress>");
        for (i = 0; i <5; ++i){
    
            sockfd[i] = Socket(AF_INET, SOCK_STREAM,0);
    
            bzero(&serveraddr, sizeof(struct sockaddr_in));
            serveraddr.sin_family = AF_INET;
            serveraddr.sin_port = htons(SERV_PORT);
            Inet_pton(AF_INET, argv[1], &serveraddr.sin_addr);
    
            Connect(sockfd[i], (SA*)&serveraddr, sizeof(serveraddr));
    
        }
    
        str_cli(stdin, sockfd[0]);
        exit(0);
    }
    
    
    int str_cli1(FILE *fp, int sockfd){
        char sendline[MAXLINE], recvline[MAXLINE];
    
        while(Fgets(sendline, MAXLINE, fp) != NULL){
            Writen(sockfd, sendline, strlen(sendline));
    
            if (Readline(sockfd, recvline, MAXLINE) == 0)
                err_quit("str_cli: server terminated prematurely");
            Fputs(recvline, stdout);
        }
    }
  • 相关阅读:
    python 中文乱码问题
    PHP读取CSV文件把数据插入到数据库,本地没有问题,阿里云测试服务器不行
    UTF-8 Unicode ANSI网页编码的区别
    php 读取csv 乱码
    zend studio(Eclipse)和PyDev搭建Python开发环境
    php 如何解决“您访问的域名有误或网页不存在”
    mysql数据去除重复及相关优化(转)
    Git的简单使用教程
    PHP mysql基础操作
    PHP两个文件的相对路径
  • 原文地址:https://www.cnblogs.com/BeDPS/p/3693088.html
Copyright © 2020-2023  润新知