ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

LinuxC应用开发学习笔记(十六)--Socket套接字

2022-01-15 09:05:30  阅读:171  来源: 互联网

标签:raddr Socket -- int exit 接字 include sin sd


网络套接字socket

基本功:两门语言,一门脚本语言。使用拿来主义先实现功能。

讨论:跨主机的传输需要注意的问题。
1、字节序问题:大端存储:低地址处放高字节
小端存储:X86 低地址处放低字节
0x00 00 00 05
大 05 00 00 00
小 00 00 00 05
区分主机字节序和网络字节序:
主机字节序:host
网络字节序:network
解决办法:to _s: htons ,htonl, ntohs,ntohl
2 对齐:
struct
{
int i;
float;
char ch;
} #占用12字节。
解决办法是不对齐。

3 类型长度问题:
int
char
解决:int32_t , uint32_t , int64_t , int8_t , uint8_t
4 socket 是什么?
在协议层和传输方式中间的介质。
SOCK_STREAM 有序的 可靠的(只要能接收到包,一定能保证数据是正确的) 双工的 基于连接的(点对点,一对一,每个人专用的) 字节流传输()。
SOCK_DGRAM 分组为单位,无连接的,不可靠的,和流式相反。
SOKC_SEQPACKET 安全、可靠、双工、基于连接、有序可靠的报式传输。

报式套接字:
程序猿能不能完成网络程序,看程序猿能不能完成报式套接字。
被动端(先运行):
1、取得SOCKET
2、给SOCKET取得地址
3、收/发消息
4、关闭SOCKET
主动端:
1、取得SOCKET
2、给SOCKET取得地址(可以省略)
3、收/发消息
4、关闭SOCKET
不同的协议族绑定的地址的结构体是不一样的。
socket();
bind();
sendto();
rcvfrom();
inet_pton();
inet_ntop();
setsockopt(int scokfd,int level,int optname,void *optval,socklen_t optlen);
getsockopt();

报式套接字基本实现

头文件.h

#ifndef PROTO_H__
#define PROTO_H__

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define RCVPORT "1989"

#define NAMESIZE 11


struct msg_st
{
    u_int8_t  name[NAMESIZE];
    u_int32_t math;
    u_int32_t chinese;

};//字节不对齐
//__attribute_((packed));
#pragma pack()



#endif

发送.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include "proto.h"

int main(int argc,char **argv)
{
    int sd;

    struct msg_st sendbuf;

    //远端地址,不是本端地址,是发送给谁
    struct sockaddr_in raddr;

    if (argc < 2)
    {
        fprintf(stderr,"Usage...\n");
        exit(1);
    }
    


    sd = socket(AF_INET,SOCK_DGRAM,0);
    if (sd<0)
    {
        perror("socket()");
        exit(1);
    }
    

//    bind();

    strcpy(sendbuf.name,"xiaohong");
    sendbuf.chinese = htonl(rand()%100);
    sendbuf.math = htonl(rand()%100); 

    raddr.sin_family = AF_INET;
    raddr.sin_port = htons(atoi(RCVPORT));
    inet_pton(AF_INET,argv[1],&raddr.sin_addr);

    if(sendto(sd,&sendbuf,sizeof(sendbuf),0,(void *)&raddr,sizeof(raddr)) < 0)
    {
        perror("sendto()");
        exit(1);
    }

    puts("ok!");

    close(sd);

    exit(0);
}

接收.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "proto.h"

#define IPSTRSIZE 40 

int main()
{
    //APV4 默认支持报式套接字来实现,写0,相当于IPPRPOTO_UDP 
    int sd;

    struct sockaddr_in laddr,raddr;

    struct msg_st rcvbuf;

    socklen_t raddr_len;

    char ipstr[IPSTRSIZE];
    

    sd = socket(AF_INET,SOCK_DGRAM,0);
    if (sd<0)
    {
        perror("socket()");
        exit(1);
    }
    
    laddr.sin_family = AF_INET;
    //主机转网络,2个字节
    laddr.sin_port = htons(atoi(RCVPORT));


    //网络IP地址转换成大整数
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);

    if (bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
    {
        perror("bind()");
        exit(1);
    }


    /*!!!!!!重要*/
    raddr_len = sizeof(raddr);

    while (1)
    {
        recvfrom(sd,&rcvbuf,sizeof(rcvbuf),0,(void *)&raddr,&raddr_len);
                                                         //网络转主机,2个字节

        //大整数转换成点分式,argc 2 是 待转换的地址
        inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);

        printf("---MESSAGE FROM %s:%d---\n",ipstr,ntohs(raddr.sin_port));

        printf("NAME = %s \n",rcvbuf.name);

        printf("MATH = %d \n",ntohl(rcvbuf.math));

        printf("CHINESE = %d \n",ntohl(rcvbuf.chinese));


        
    }
    

    

    close(sd);

    exit(0);
}

改进版本-发送.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include "proto.h"

int main(int argc,char **argv)
{
    int sd,size;

    struct msg_st *sendbufp;

    //远端地址,不是本端地址,是发送给谁
    struct sockaddr_in raddr;

    if (argc < 3)
    {
        fprintf(stderr,"Usage...\n");
        exit(1);
    }
    
    if (strlen(argv[2])>NAMEMAX)
    {
        fprintf(stderr,"Name is too long!\n");
        exit(1);
    }
    
    size = sizeof(struct msg_st) + strlen(argv[2]);  
    sendbufp = malloc(size);
    if (sendbufp == NULL)
    {
        perror("malloc()");
        exit(1);
    }
    

    sd = socket(AF_INET,SOCK_DGRAM,0);
    if (sd<0)
    {
        perror("socket()");
        exit(1);
    }
    

    strcpy(sendbufp->name,argv[2]);
    sendbufp->chinese = htonl(rand()%100);
    sendbufp->math = htonl(rand()%100); 

    raddr.sin_family = AF_INET;
    raddr.sin_port = htons(atoi(RCVPORT));
    inet_pton(AF_INET,argv[1],&raddr.sin_addr);

    if(sendto(sd,sendbufp,size,0,(void *)&raddr,sizeof(raddr)) < 0)
    {
        perror("sendto()");
        exit(1);
    }

    puts("ok!");

    close(sd);

    exit(0);
}

改进版本-接收.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "proto.h"

#define IPSTRSIZE 40 

int main()
{
    //APV4 默认支持报式套接字来实现,写0,相当于IPPRPOTO_UDP 
    int sd,size;

    struct sockaddr_in laddr,raddr;

    struct msg_st *rcvbufp;

    socklen_t raddr_len;

    char ipstr[IPSTRSIZE];
    
    size = sizeof(struct msg_st) + NAMEMAX -1;  
    rcvbufp = malloc(size);
    if (rcvbufp == NULL)
    {
        perror("malloc()");
        exit(1);
    }

    sd = socket(AF_INET,SOCK_DGRAM,0);
    if (sd<0)
    {
        perror("socket()");
        exit(1);
    }
    
    laddr.sin_family = AF_INET;
    //主机转网络,2个字节
    laddr.sin_port = htons(atoi(RCVPORT));


    //网络IP地址转换成大整数
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);

    if (bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
    {
        perror("bind()");
        exit(1);
    }


    /*!!!!!!重要*/
    raddr_len = sizeof(raddr);

    while (1)
    {
        recvfrom(sd,rcvbufp,size,0,(void *)&raddr,&raddr_len);
                                                         //网络转主机,2个字节

        //大整数转换成点分式,argc 2 是 待转换的地址
        inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);

        printf("---MESSAGE FROM %s:%d---\n",ipstr,ntohs(raddr.sin_port));

        printf("NAME = %s \n",rcvbufp->name);

        printf("MATH = %d \n",ntohl(rcvbufp->math));

        printf("CHINESE = %d \n",ntohl(rcvbufp->chinese));
 
    }

    close(sd);

    exit(0);
}
多点通讯(广播(全网广播、子网广播)、多播/组播):

广播.h

#ifndef PROTO_H__
#define PROTO_H__

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define MTGROUP "224.2.2.2" 

#define RCVPORT "1989"

#define NAMESIZE 11


struct msg_st
{
    u_int8_t  name[NAMESIZE];
    u_int32_t math;
    u_int32_t chinese;

};//字节不对齐
//__attribute_((packed));
#pragma pack()



#endif

广播接收.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <net/if.h> 
#include "proto.h"

#define IPSTRSIZE 40 

int main()
{
    //APV4 默认支持报式套接字来实现,写0,相当于IPPRPOTO_UDP 
    int sd;

    struct sockaddr_in laddr,raddr;

    struct msg_st rcvbuf;

    socklen_t raddr_len;

    char ipstr[IPSTRSIZE];
    
    sd = socket(AF_INET,SOCK_DGRAM,0);
    if (sd<0)
    {
        perror("socket()");
        exit(1);
    }

    
    struct ip_mreqn mreq;

    inet_pton(AF_INET,MTGROUP,&mreq.imr_multiaddr);
    inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);
    mreq.imr_ifindex = if_nametoindex("eth0");

    if (setsockopt(sd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))<0)
    {
        perror("setsockopt()");
        exit(1);
    }
    
    laddr.sin_family = AF_INET;
    //主机转网络,2个字节
    laddr.sin_port = htons(atoi(RCVPORT));

    //网络IP地址转换成大整数
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);

    if (bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
    {
        perror("bind()");
        exit(1);
    }

    /*!!!!!!重要*/
    raddr_len = sizeof(raddr);

    while (1)
    {
        recvfrom(sd,&rcvbuf,sizeof(rcvbuf),0,(void *)&raddr,&raddr_len);
                                                         //网络转主机,2个字节
        //大整数转换成点分式,argc 2 是 待转换的地址
        inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);

        printf("---MESSAGE FROM %s:%d---\n",ipstr,ntohs(raddr.sin_port));

        printf("NAME = %s \n",rcvbuf.name);

        printf("MATH = %d \n",ntohl(rcvbuf.math));

        printf("CHINESE = %d \n",ntohl(rcvbuf.chinese));
     
    }

    close(sd);

    exit(0);
}

广播发送.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/if.h> 
#include <unistd.h>
#include <string.h>
#include "proto.h"

int main(int argc,char **argv)
{
    int sd;

    struct msg_st sendbuf;

    struct ip_mreqn mreq;

    //远端地址,不是本端地址,是发送给谁
    struct sockaddr_in raddr;

    sd = socket(AF_INET,SOCK_DGRAM,0);
    if (sd<0)
    {
        perror("socket()");
        exit(1);
    }
    
    
    //设置为多播模式,对当前socket属性做了一个更改

    
    
    inet_pton(AF_INET,MTGROUP,&mreq.imr_multiaddr);
    inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);
    mreq.imr_ifindex = if_nametoindex("eth0");

    if(setsockopt(sd,IPPROTO_IP,IP_MULTICAST_IF,&mreq,sizeof(mreq)) < 0)
    {
        perror("setsockopt()");
        exit(1);
    }

    strcpy(sendbuf.name,"xiaohong");
    sendbuf.chinese = htonl(rand()%100);
    sendbuf.math = htonl(rand()%100); 

    raddr.sin_family = AF_INET;
    raddr.sin_port = htons(atoi(RCVPORT));
    inet_pton(AF_INET,MTGROUP,&raddr.sin_addr);

    if(sendto(sd,&sendbuf,sizeof(sendbuf),0,(void *)&raddr,sizeof(raddr)) < 0)
    {
        perror("sendto()");
        exit(1);
    }

    puts("ok!");

    close(sd);

    exit(0);
}

组播的实现

组播发送.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/if.h> 
#include <unistd.h>
#include <string.h>
#include "proto.h"

int main(int argc,char **argv)
{
    int sd;

    struct msg_st sendbuf;

    struct ip_mreqn mreq;

    //远端地址,不是本端地址,是发送给谁
    struct sockaddr_in raddr;

    sd = socket(AF_INET,SOCK_DGRAM,0);
    if (sd<0)
    {
        perror("socket()");
        exit(1);
    }
    
    
    //设置为多播模式,对当前socket属性做了一个更改

    
    
    inet_pton(AF_INET,MTGROUP,&mreq.imr_multiaddr);
    inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);
    mreq.imr_ifindex = if_nametoindex("eth0");

    if(setsockopt(sd,IPPROTO_IP,IP_MULTICAST_IF,&mreq,sizeof(mreq)) < 0)
    {
        perror("setsockopt()");
        exit(1);
    }

    strcpy(sendbuf.name,"xiaohong");
    sendbuf.chinese = htonl(rand()%100);
    sendbuf.math = htonl(rand()%100); 

    raddr.sin_family = AF_INET;
    raddr.sin_port = htons(atoi(RCVPORT));
    inet_pton(AF_INET,MTGROUP,&raddr.sin_addr);

    if(sendto(sd,&sendbuf,sizeof(sendbuf),0,(void *)&raddr,sizeof(raddr)) < 0)
    {
        perror("sendto()");
        exit(1);
    }

    puts("ok!");

    close(sd);

    exit(0);
}

组播接收.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <net/if.h> 
#include "proto.h"

#define IPSTRSIZE 40 

int main()
{
    //APV4 默认支持报式套接字来实现,写0,相当于IPPRPOTO_UDP 
    int sd;

    struct sockaddr_in laddr,raddr;

    struct msg_st rcvbuf;

    socklen_t raddr_len;

    char ipstr[IPSTRSIZE];
    
    sd = socket(AF_INET,SOCK_DGRAM,0);
    if (sd<0)
    {
        perror("socket()");
        exit(1);
    }

    
    struct ip_mreqn mreq;

    inet_pton(AF_INET,MTGROUP,&mreq.imr_multiaddr);
    inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);
    mreq.imr_ifindex = if_nametoindex("eth0");

    if (setsockopt(sd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))<0)
    {
        perror("setsockopt()");
        exit(1);
    }
    
    laddr.sin_family = AF_INET;
    //主机转网络,2个字节
    laddr.sin_port = htons(atoi(RCVPORT));

    //网络IP地址转换成大整数
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);

    if (bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
    {
        perror("bind()");
        exit(1);
    }

    /*!!!!!!重要*/
    raddr_len = sizeof(raddr);

    while (1)
    {
        recvfrom(sd,&rcvbuf,sizeof(rcvbuf),0,(void *)&raddr,&raddr_len);
                                                         //网络转主机,2个字节
        //大整数转换成点分式,argc 2 是 待转换的地址
        inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);

        printf("---MESSAGE FROM %s:%d---\n",ipstr,ntohs(raddr.sin_port));

        printf("NAME = %s \n",rcvbuf.name);

        printf("MATH = %d \n",ntohl(rcvbuf.math));

        printf("CHINESE = %d \n",ntohl(rcvbuf.chinese));
     
    }

    close(sd);

    exit(0);
}

流式套接字

流式套接字基础版

客户端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <netinet/ip.h> /* superset of previous */
#include "proto.h"

int main(int argc,char**argv)
{
    int sd;

    struct sockaddr_in raddr;

    long long stamp;

    FILE * fp;

    if (argc < 2)
    {   
        fprintf(stderr,"Usage...\n");
        exit(1);
    }
    

    sd = socket(AF_INET,SOCK_STREAM,0/*默认IPPROTO_TCP,IPPRORO_SCTP*/);    //套接字
    if (sd<0)
    {
        perror("socket()");
        exit(1); 
    }
    //bind();

    raddr.sin_family = AF_INET;
    raddr.sin_port = htons(atoi(SERVERPORT));//点分式转换成大整数
    inet_pton(AF_INET,argv[1],&raddr.sin_addr);
    //连接成功
    if (connect(sd,(void*)&raddr,sizeof(raddr))<0)
    {
        perror("connect()");
        exit(1);
    }
    
    //文件系统精髓
    fp = fdopen(sd,"r+");
    if (fp == NULL)
    {
        perror("fdopen()");
        exit(1);
    }

    if (fscanf(fp,FMT_STAMP,&stamp)<1)
    {
        fprintf(stderr,"Bad format!\n");
    }
    else
    {
        fprintf(stdout,"stamp = %lld\n",stamp);
    }
   
    fclose(fp);
    //rcve();
    //close();

    exit(0);
}
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <netinet/ip.h> 
#include "proto.h"

#define  IPSTRSIZE 40 

#define  BUFSIZE   1024 


/*每次有人和我正常连接我做的事情,是把当前返回打时戳发送出去*/
static void server_job(int sd)
{
    char buf[BUFSIZE];
    int len;
     
    len =  sprintf(buf,FMT_STAMP,(long long)time(NULL)) ;
    
    if (send(sd,buf,len,0)<0)//发送
    {
        perror("send()");
        exit(1);
    }

}

int main()
{
   
    int sd,newsd;

    char ipstr[IPSTRSIZE];

    socklen_t raddr_len;

    struct sockaddr_in localaddr,rmaddr;


    sd = socket(AF_INET,SOCK_STREAM,0/*默认IPPROTO_TCP,IPPRORO_SCTP*/);    //套接字
    if (sd<0)
    {
        perror("socket()");
        exit(1); 
    }



    int val = 1;
    /*为了解决随时可以使用用过的地址*/
    if(setsockopt(sd, SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0)
    {
        perror("setsockopt()");
        exit(1);
    }

    localaddr.sin_family = AF_INET;//协议族
    inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr);//指定的IP地址转换成大整数,存放在localaddr.sin_addr
    localaddr.sin_port   = htons(atoi(SERVERPORT));//端口
    //绑定我端的端口和我端的IP
    if (bind(sd,(void*)&localaddr,sizeof(localaddr)) < 0) //连接
    {
        perror("bind()");
        exit(1);
    }
    

    if(listen(sd,200)<0)     //监听,第二个参数是全连接队列的大小
    {
        perror("listen()");
        exit(1);
    }

    raddr_len = sizeof(rmaddr);

    while (1)
    {   
        //防止前功尽弃,使用newsd
        newsd = accept(sd,(void *)&rmaddr,&raddr_len);
        if(newsd<0)   //接受连接
        {
            perror("accept()");
            exit(1);
        }

        inet_ntop(AF_INET,&rmaddr.sin_addr,ipstr,IPSTRSIZE);//将大整数转换成点串,存放在ipstr中 

                                        /*网上接收来的数据进行转换*/
        printf("Client: %s :%d\n",ipstr,ntohs(rmaddr.sin_port));

        server_job(newsd);

        close(newsd);
    }
    
    //一定执行不到。。。
    close(sd);

    exit(0);
}
协议端
#ifndef  __PROTO_H__
#define  __PROTO_H__

#define SERVERPORT "1989"

#define FMT_STAMP "%lld\n"  //发出时戳的格式 



#endif 

流式套接字增强版

客户端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <netinet/ip.h> /* superset of previous */
#include "proto.h"

int main(int argc,char**argv)
{
    int sd;

    struct sockaddr_in raddr;

    long long stamp;

    FILE * fp;

    if (argc < 2)
    {   
        fprintf(stderr,"Usage...\n");
        exit(1);
    }
    

    sd = socket(AF_INET,SOCK_STREAM,0/*默认IPPROTO_TCP,IPPRORO_SCTP*/);    //套接字
    if (sd<0)
    {
        perror("socket()");
        exit(1); 
    }
    //bind();

    raddr.sin_family = AF_INET;
    raddr.sin_port = htons(atoi(SERVERPORT));//点分式转换成大整数
    inet_pton(AF_INET,argv[1],&raddr.sin_addr);
    //连接成功
    if (connect(sd,(void*)&raddr,sizeof(raddr))<0)
    {
        perror("connect()");
        exit(1);
    }
    
    //文件系统精髓
    fp = fdopen(sd,"r+");
    if (fp == NULL)
    {
        perror("fdopen()");
        exit(1);
    }

    if (fscanf(fp,FMT_STAMP,&stamp)<1)
    {
        fprintf(stderr,"Bad format!\n");
    }
    else
    {
        fprintf(stdout,"stamp = %lld\n",stamp);
    }
   
    fclose(fp);
    //rcve();
    //close();

    exit(0);
}
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <netinet/ip.h> 
#include "proto.h"

#define  IPSTRSIZE 40 

#define  BUFSIZE   1024 


/*每次有人和我正常连接我做的事情,是把当前返回打时戳发送出去*/
static void server_job(int sd)
{
    char buf[BUFSIZE];
    int len;
     
    len =  sprintf(buf,FMT_STAMP,(long long)time(NULL)) ;
    
    if (send(sd,buf,len,0)<0)//发送
    {
        perror("send()");
        exit(1);
    }

}

int main()
{
   
    int sd,newsd;

    pid_t pid;

    char ipstr[IPSTRSIZE];

    socklen_t raddr_len;

    struct sockaddr_in localaddr,rmaddr;


    sd = socket(AF_INET,SOCK_STREAM,0/*默认IPPROTO_TCP,IPPRORO_SCTP*/);    //套接字
    if (sd<0)
    {
        perror("socket()");
        exit(1); 
    }



    int val = 1;
    /*为了解决随时可以使用用过的地址*/
    if(setsockopt(sd, SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0)
    {
        perror("setsockopt()");
        exit(1);
    }

    localaddr.sin_family = AF_INET;//协议族
    inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr);//指定的IP地址转换成大整数,存放在localaddr.sin_addr
    localaddr.sin_port   = htons(atoi(SERVERPORT));//端口
    //绑定我端的端口和我端的IP
    if (bind(sd,(void*)&localaddr,sizeof(localaddr)) < 0) //连接
    {
        perror("bind()");
        exit(1);
    }
    

    if(listen(sd,200)<0)     //监听,第二个参数是全连接队列的大小
    {
        perror("listen()");
        exit(1);
    }

    raddr_len = sizeof(rmaddr);

    while (1)
    {   
        //防止前功尽弃,使用newsd
        newsd = accept(sd,(void *)&rmaddr,&raddr_len);
        if(newsd<0)   //接受连接
        {
            perror("accept()");
            exit(1);
        }

        //父进程accept给子进程
        pid = fork();
        if (pid<0)
        {
            perror("fork()");
            exit(1);
        }
        if (pid == 0)  //子进程干活
        {

            //子进程只用newsd 不用sd
            close(sd);

            inet_ntop(AF_INET,&rmaddr.sin_addr,ipstr,IPSTRSIZE);//将大整数转换成点串,存放在ipstr中 

                                /*网上接收来的数据进行转换*/
            printf("Client: %s :%d\n",ipstr,ntohs(rmaddr.sin_port));

            server_job(newsd);

            close(newsd);
        
            exit(0);        //干完活跑路
        }
            //父进程只用sd 不用newsd
            close(newsd);
    }
    
    //一定执行不到。。。
    close(sd);

    exit(0);
}
协议端
#ifndef  __PROTO_H__
#define  __PROTO_H__

#define SERVERPORT "1989"

#define FMT_STAMP "%lld\n"  //发出时戳的格式 



#endif 

静态进程池

客户端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <netinet/ip.h> /* superset of previous */
#include "proto.h"

int main(int argc,char**argv)
{
    int sd;

    struct sockaddr_in raddr;

    long long stamp;

    FILE * fp;

    if (argc < 2)
    {   
        fprintf(stderr,"Usage...\n");
        exit(1);
    }
    

    sd = socket(AF_INET,SOCK_STREAM,0/*默认IPPROTO_TCP,IPPRORO_SCTP*/);    //套接字
    if (sd<0)
    {
        perror("socket()");
        exit(1); 
    }
    //bind();

    raddr.sin_family = AF_INET;
    raddr.sin_port = htons(atoi(SERVERPORT));//点分式转换成大整数
    inet_pton(AF_INET,argv[1],&raddr.sin_addr);
    //连接成功
    if (connect(sd,(void*)&raddr,sizeof(raddr))<0)
    {
        perror("connect()");
        exit(1);
    }
    
    //文件系统精髓
    fp = fdopen(sd,"r+");
    if (fp == NULL)
    {
        perror("fdopen()");
        exit(1);
    }

    if (fscanf(fp,FMT_STAMP,&stamp)<1)
    {
        fprintf(stderr,"Bad format!\n");
    }
    else
    {
        fprintf(stdout,"stamp = %lld\n",stamp);
    }
   
    fclose(fp);
    //rcve();
    //close();

    exit(0);
}
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <netinet/ip.h> 
#include "proto.h"

#define  IPSTRSIZE 40 

#define  BUFSIZE   1024 

#define  PROCNUM   4 


static void server_loop(int sd);

/*每次有人和我正常连接我做的事情,是把当前返回打时戳发送出去*/
static void server_job(int sd)
{
    char buf[BUFSIZE];
    int len;
     
    len =  sprintf(buf,FMT_STAMP,(long long)time(NULL)) ;
    
    if (send(sd,buf,len,0)<0)//发送
    {
        perror("send()");
        exit(1);
    }

}



int main()
{ 
    int sd,i;

    struct sockaddr_in localaddr;

    pid_t pid;

    sd = socket(AF_INET,SOCK_STREAM,0/*默认IPPROTO_TCP,IPPRORO_SCTP*/);    //套接字
    if (sd<0)
    {
        perror("socket()");
        exit(1); 
    }

    int val = 1;
    /*为了解决随时可以使用用过的地址*/
    if(setsockopt(sd, SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0)
    {
        perror("setsockopt()");
        exit(1);
    }

    localaddr.sin_family = AF_INET;//协议族
    inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr);//指定的IP地址转换成大整数,存放在localaddr.sin_addr
    localaddr.sin_port   = htons(atoi(SERVERPORT));//端口
    //绑定我端的端口和我端的IP
    if (bind(sd,(void*)&localaddr,sizeof(localaddr)) < 0) //连接
    {
        perror("bind()");
        exit(1);
    }
    

    if(listen(sd,200)<0)     //监听,第二个参数是全连接队列的大小
    {
        perror("listen()");
        exit(1);
    }


    for (i = 0; i < PROCNUM; i++)
    {
        pid = fork();
        if (pid<0)
        {
            perror("fork()");
            exit(1);
        }

        if (pid == 0)
        {
            server_loop(sd);//子进程永远干活
            exit(0);
        }
        for (i = 0; i < PROCNUM; i++)
        {
        wait(NULL);
        }
        close(sd);

        exit(0);
    
    }

}   





static void server_loop(int sd)
{
    socklen_t raddr_len;

    struct sockaddr_in localaddr,rmaddr;

    int newsd;

    char ipstr[IPSTRSIZE];

    raddr_len = sizeof(rmaddr);

    while (1)
    {   
        //防止前功尽弃,使用newsd
        newsd = accept(sd,(void *)&rmaddr,&raddr_len);
        if(newsd<0)   //接受连接
        {
            perror("accept()");
            exit(1);
        }

        inet_ntop(AF_INET,&rmaddr.sin_addr,ipstr,IPSTRSIZE);//将大整数转换成点串,存放在ipstr中 

                                        /*网上接收来的数据进行转换*/
        printf("[%d] Client: %s :%d\n",getpid(),ipstr,ntohs(rmaddr.sin_port));

        server_job(newsd);

        close(newsd);
    } 
    //一定执行不到。。。
    close(sd);
}
协议端
#ifndef  __PROTO_H__
#define  __PROTO_H__

#define SERVERPORT "1989"

#define FMT_STAMP "%ld\n"  //发出时戳的格式 

#endif 

动态进程池

与静态线程池差别仅仅在服务器端

服务器端(难点*****)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <sys/mman.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "proto.h"


//最小的空闲个数
#define MINSPARESERVER 5
#define MAXSPARESERVER 10
#define MAXCLIENTS     20  

#define SIG_NOTIFY     SIGUSR2  //预留出来的定义行为的信号    

#define  LINEBUFSIZE 80 

#define  IPSTRSIZE 40 

#define  BUFSIZE   1024 

enum 
{
    STATE_IDEL = 0,
    STATE_BUSY   
};

struct sever_st
{
    pid_t pid;
    int state;
    //int reuse;
};

//定义动态线程池,存放在堆上
static struct sever_st *serverpool;

static int idle_count = 0,busy_count = 0;

static int sd;

static void server_job(int pos)
{
    int ppid,client_sd,len;

    time_t stamp;

    char linebuf[LINEBUFSIZE];
    
    char ipstr[IPSTRSIZE];

    ppid =  getppid();

    socklen_t raddr_len;

    struct sockaddr_in raddr;

    while (1)
    {

        serverpool[pos].state = STATE_IDEL;

        kill(ppid,SIG_NOTIFY);  //通知父进程看一眼
        
        client_sd = accept(sd,(void *)&raddr,&raddr_len);

        if (client_sd<0)
        {
            if (errno != EINTR || errno != EAGAIN)
            {
                perror("accept()");
                exit(1);
            }  
        }

        //设置当前状态为忙态
        serverpool[pos].state = STATE_BUSY;

        kill(ppid,SIG_NOTIFY);  //通知父进程看

        //inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);

        //测试程序
        //printf("[%d]client:%s:%d",getpid(),ipstr,ntohs(raddr.sin_port));

        stamp = time(NULL);
        //写入到字符串中
        len = snprintf(linebuf,LINEBUFSIZE,FMT_STAMP,stamp);

        send(client_sd,linebuf,len,0);
        /* if error */
        
        sleep(3);

        close(client_sd);  
    }
}




//工作代码
static int add_1_server(void)
{
    int slot;

    pid_t pid;
    //到达最大值
    if (idle_count + busy_count >= MAXCLIENTS)
        return -1;

    for (slot = 0; slot < MAXCLIENTS; slot++)
        if (serverpool[slot].pid == -1)
            break;

    serverpool[slot].state = STATE_IDEL;

    pid = fork();
    if (pid<0)
    {
        perror("fork()");
        exit(1);
    }

    if (pid == 0)
    {
        server_job(slot);//子进程永远干活
        exit(0);
    }
    else
    {
        serverpool[slot].pid = pid;
        idle_count ++;
    }
    return 0;
}





static int del_1_server(void)
{
    int i;
    if (idle_count == 0)
        return -1;

    for ( i = 0; i < MAXCLIENTS; i++)
    {
        if (serverpool[i].pid != -1 && serverpool[i].state == STATE_IDEL)
        {
            kill(serverpool[i].pid,SIGTERM);//杀掉进程
            serverpool[i].pid = -1;
            idle_count --;
            break;
        }   
    }
    return 0;
}


static void usr2_handlers(int s)
{
    return;
}

//遍历状态池
static int scanf_pool(void)
{
    int i,busy = 0,idle = 0;
    for ( i = 0; i < MAXCLIENTS; i++)
    {
        if (serverpool[i].pid == -1)
            continue;
        if (kill(serverpool[i].pid,0))//检测serverpool的i空间 pid是否存在
        {
            serverpool[i].pid = -1;
            continue;
        }

        if (serverpool[i].pid == STATE_IDEL)//检测serverpol的i空间 pid是否存在
        {
            idle++;
        }
        else if (serverpool[i].pid == STATE_BUSY)
        {
            busy++;
        }
            else
            {
                fprintf(stderr,"Unknown statae.\n");
                //_exit(1);
                abort();
            }
    }
    
    idle_count = idle;

    busy_count = busy;
    
    return 0;
}





int main()
{ 
    int sd,i,len,val = 1;

    struct sockaddr_in localaddr;

    pid_t pid;

    struct sigaction sa,osa;

    sigset_t set,oset;

    //让子进程自行消亡,
    sa.sa_handler = SIG_IGN;//父进程阻塞去收尸,是这个信号去通知的,所以先把它忽略掉,防止父进程收尸
    sigemptyset(&sa.sa_mask);//没有信号同用一个信号处理函数
    sa.sa_flags = SA_NOCLDWAIT; //阻止子进程变成僵尸状态,免去收僵尸的困扰
    sigaction(SIGCHLD,&sa,&osa);//对一个信号来做一个行为的指定,把之前的行为放到osa中。


    sa.sa_handler = usr2_handlers;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIG_NOTIFY,&sa,&osa);

    //阻塞信号
    sigemptyset(&set);//集合清成空集
    sigaddset(&set,SIG_NOTIFY);
    //把set阻塞住,别的状态保存在oset中
    sigprocmask(SIG_BLOCK,&set,&oset);


    //自动分配空间,读写,共享,匿名映射, 也就相当于malloc
    serverpool = mmap(NULL,sizeof(struct sever_st)*MAXCLIENTS,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
    if (serverpool == MAP_FAILED)
    {
        perror("mmap()");
        exit(1);
    }

    //初始化
    for (int i = 0; i < MAXCLIENTS; i++)
    {
        serverpool[i].pid = -1;
    }

    sd = socket(AF_INET,SOCK_STREAM,0/*默认支持流式套接字IPPROTO_TCP,IPPRORO_SCTP*/);    //套接字
    if (sd<0)
    {
        perror("socket()");
        exit(1); 
    }
  
    /*为了解决随时可以使用用过的地址*/
    if(setsockopt(sd, SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0)
    {
        perror("setsockopt()");
        exit(1);
    }

    int val = 1;
    /*为了解决随时可以使用用过的地址*/
    if(setsockopt(sd, SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0)
    {
        perror("setsockopt()");
        exit(1);
    }

    localaddr.sin_family = AF_INET;//协议族
    inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr);//指定的IP地址(点分式)转换成大整数,存放在localaddr.sin_addr
    localaddr.sin_port   = htons(atoi(SERVERPORT));//端口
    //绑定我端的端口和我端的IP
    if (bind(sd,(void*)&localaddr,sizeof(localaddr)) < 0) //连接
    {
        perror("bind()");
        exit(1);
    }
    
    if(listen(sd,100)<0)     //监听,第二个参数是全连接队列的大小
    {
        perror("listen()");
        exit(1);
    }

    for (i = 0; i < MINSPARESERVER; i++)
    {
        add_1_server();
    }
    

   
    while (1)
    {
        sigsuspend(&oset);//信号驱动程序

        scanf_pool();

        //control the pool;

        if (idle_count > MAXSPARESERVER)
        {
            for (i = 0; i < (idle_count - MAXSPARESERVER); i++)
                del_1_server();//一次减少一个sever端口
        }
        else if(idle_count<MINSPARESERVER)
        {
            for (i = 0; i < (MINSPARESERVER - idle_count); i++)
                add_1_server();//一次增加一个sever端口    
        }
        


        //printf the pool
        for ( i = 0; i < MAXCLIENTS; i++)
        {
            if (serverpool[i].pid == -1)
            {
                putchar(' ');
            }
            else if(serverpool[i].state == STATE_IDEL)
            {
                putchar('.');
            }
            else
            {
                putchar('x');
            }
            putchar('\n');      
        }  
    }
    
    //恢复信号
    sigprocmask(SIG_SETMASK,&oset,NULL);

}   


标签:raddr,Socket,--,int,exit,接字,include,sin,sd
来源: https://blog.csdn.net/m0_46152793/article/details/122449266

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有