socket高级编程

设置套接字函数:

#include<sys/socket.h>

int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t* optlen);

//sockfd要设置的目的套接字
//level套接字的控制层次
//optname optval optlen是三个相关的参数,通过不同的搭配可以设置不同的功能

应用:

1.数据收发时限设置
struct timeva timeout;
timeout.tv_sec=5;
timeout.tv_usec=0;

//接受时限
setsockopt(serversocket, SQL_SOCKET,SO_RCVTIMEO, (char*)&timeout,sizeof(timeout));

//发送时限
setsockopt(serversocket, SQL_SOCKET,SO_SNDTIMEO, (char*)&timeout,sizeof(timeout));
2.修改收发缓冲区
//接收缓冲区
int opt=1024*1024;
setsockopt(serversocket, SQL_SOCKET, SO_RCVBUF, (const char*)&opt,sizeof(opt));


//发送缓冲区
setsockopt(serversocket, SQL_SOCKET, SO_SNDBUF, (const char*)&opt,sizeof(opt));
3.广播设置
int bBroadcast=1;
setsockopt(seversocket, SQL_SOCKET, SO_BROADCAST,(cosnt char*)&bBroadcast,sizeof(bBroadcast));
4.直接数据复制
  为了提升系统性能,在发送或接受数据时,可以主动设置数据不经历由缓冲区到套接字缓存区的拷贝。

int opt=0;

setsockopt(serversocket, SQL_SOCKET,SO_SNDBUF,(char*)&opt,sizeof(opt));

setsockopt(serversocket, SQL_SOCKET,SO_RCVBUF,(char*)&opt,sizeof(opt));

Select技术:

#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#define SERVER_PORT 5555
#define QUEUE_LENGTH 5
#define BUF_SIZE 200

int main(int argc, char **argv)
{
    int server_socket,new_socket;
    struct sockaddr_in server_addr,client_addr;
    socklen_t sin_size;
    int client_socket[QUEUE_LENGTH];
    int conn_num;
    int yes=1;
    char buf[BUF_SIZE];
    int ret;
    int i;
    //创建套接字
    if((server_socket=socket(AF_INET,SOCK_STREAM,0))<0){
        perror("Socket");
        return 0;
    }
    //设置为可重复使用
    if(setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int))==-1){
        perror("setsockopt");
        return 0;
    }
    //设置服务器地址信息设置
    server_addr.sin_family=AF_INET;                    //TCP
    server_addr.sin_port=htons(SERVER_PORT);
    server_addr.sin_addr.s_addr=INADDR_ANY;            //本地IP地址

    memset(server_addr.sin_zero,'\0',sizeof(server_addr.sin_zero));

    //绑定套接字与地址信息
    if(bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1){
        perror("setsockopt");
        return 0;
    }

    //侦听
    if(listen(server_socket,5)==-1){
        perror("setsockopt");
        return 0;
    }

    printf("listen port : %d\n",SERVER_PORT);

    fd_set clientfdset;
    int maxsock;
    struct timeval tv;

    conn_num=0;
    sin_size=sizeof(client_addr);
    maxsock=server_socket;

    while(1){
        //初始化,清空并添加服务器套接字到集合
        FD_ZERO(&clientfdset);
        FD_SET(server_socket,&clientfdset);

        //设置超时时间
        tv.tv_sec=15;
        tv.tv_usec=0;

        //添加连接的客户端到集合
        for(i=0;i<QUEUE_LENGTH;i++){
            if(client_socket[i]!=0)
                FD_SET(client_socket[i],&clientfdset);
        }
        //select模式
        ret=select(maxsock+1,&clientfdset,NULL,NULL,&tv);
        if(ret<0){
            perror("select");
            break;
        }
        else if(ret==0){
            printf("waiting timeout\n");
            continue;
        }

        //检查集合内是否已经存在
        for(i=0;i<conn_num;i++){
            if(FD_ISSET(client_socket[i],&clientfdset)){
                ret=recv(client_socket[i],buf,sizeof(buf),0);

                if(ret<=0){
                    printf("client[%d] close\n",i);
                    close(client_socket[i]);
                    FD_CLR(client_socket[i],&clientfdset);
                    client_socket[i]=0;
                }
                else{
                    printf("client[%d] msg: %s\n",i,buf);
                    send(client_socket[i],buf,sizeof(buf),0);
                }
            }
        }

        if(FD_ISSET(server_socket,&clientfdset)){
            new_socket=accept(server_socket,(struct sockaddr*)&client_addr,&sin_size);
            if(new_socket<=0){
                perror("accept");
                continue;
            }
            if(conn_num<QUEUE_LENGTH){
                client_socket[conn_num++]=new_socket;
                printf("new client[%d] %s: %d\n",conn_num,inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
                if(new_socket>maxsock)
                    maxsock=new_socket;
            }
            else{
                send(new_socket,"sorry overload!",sizeof("sorry overload!"),0);
                close(new_socket);
                break;
            }
        }
    }

    for(i=0;i<QUEUE_LENGTH;i++){
        if(client_socket[i]!=0)
            close(client_socket[i]);
    }

}

原始套接字技术:

原始套接字是一种套接字底层技术,它工作在网络层。利用原始套接字可以完成如下功能。
设置网卡为混杂模式,嗅探当前网路流经本网卡的所有数据包。
构造各种数据包(IP,ICMP,TCP,UDP等),并进行发送。
进行新协议的验证。
原始套接字可用于木马中的通信模块,伪造IP地址,拒绝服务攻击,数据包嗅探。

原始套接字的创建:
int rawsock=socket(AF_INET, SOCK_RAW, htons(ETH_P_IP));
//可以获取IP层的所有数据报文

htons参数的可选值及其意义
协议码 协议名
IPPROTO_ICMP    ICMP协议
ETH_P_IP    IP协议
IPPROTO_TCP TCP协议
IPPROTO_UDP UDP协议
IPPROTO_IPV6    IPv6协议
IPPROTO_EGP EGP协议
数据发送:
在原始套接字中,执行数据发送前要条用setsocketopt函数进行套接字的首部设定:

int opt;
setsockopt(sockfd,IPPROTO_IP, IP_HDRINCL, &opt, sizeof(opt));

例子:

//利用原始套接字实现一个简单的采集网络数据包,并进行反向解析IP,MAC地址
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <linux/if_ether.h>
#include <linux/in.h>

#define BUFFER_MAX 2048

int main(int argc, char **argv)
{
    int rawsock;
    char buffer[BUFFER_MAX];
    char *ethhead;
    char *iphead;
    char *phead;

    //创建原始套接字
    if((rawsock=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP)))<0){
        printf("error:create raw socket!\n");
        exit(0);
    }

    long framecount =0;

    while(1){
        int readnum = recvfrom(rawsock,buffer,2048,0,NULL,NULL);

        if(readnum<42){
            printf("error:header is incomplete!\n");
            exit(0);
        }

        ethhead=(char*)buffer;
        phead=ethhead;
        int ethernetmask=0XFF;
        framecount++;

        printf("---------------AnalysisiPacket[%d]---------------\n",framecount);
        printf("MAC:");
        int i=6;
        for(;i<=11;i++)
            printf("%.2X:",phead[i]&ethernetmask);
        printf("------->");
        for(i=0;i<=5;i++)
            printf("%.2X:",phead[i]&ethernetmask);
        printf("\n");

        iphead=ethhead+14;
        phead=iphead+12;

        printf("IP:");
        for(i=0;i<=3;i++){
            printf("%d",phead[i]&ethernetmask);
            if(i!=3)
                printf(".");
        }
        printf("------->");
        for(i=4;i<=7;i++){
            printf("%d",phead[i]&ethernetmask);
            if(i!=7)
                printf(".");
        }
        printf("\n");

        int prototype=(iphead+9)[0];
        phead=iphead+20;

        printf("Protocol:");
        switch(prototype){
        case IPPROTO_ICMP:
            printf("ICMP\n");
            break;
        case IPPROTO_IGMP:
            printf("IGMP\n");
            break;
        case IPPROTO_IPIP:
            printf("IP");
            break;
        case IPPROTO_TCP:
            printf("TCP|source port: %u |",(phead[0]<<8)&0XFF00|phead[1]&0XFF);
            printf("destport: %u\n",(phead[2]<<8)&0XFF00|phead[3]&0XFF);
            break;
        case IPPROTO_UDP:
            printf("UDP|source port: %u |",(phead[0]<<8)&0XFF00|phead[1]&0XFF);
            printf("destport: %u\n",(phead[2]<<8)&0XFF00|phead[3]&0XFF);
            break;
        case IPPROTO_RAW:
            printf("RAW\n");
            break;
        default:
            printf("Unkown\n");    
        }
        printf("-----------------end--------------------");
    }

    return 0;
}

广播技术:

  ARP(Address Resolution Protocol)和NTP(Network Time Protocol)都属于广播通信。

  ARP是局域网中的地址解析协议,利用这个协议,可以找出IP地址到MAC地址的映射关系。当主机A准备与主机B通信时,如果只知道主机B的IP地址,则主机A向整个全网发送一个ARP请求,询问IP地址为XXXX的主机,如果主机B收到就会产生回应。

  NTP是网络时间协议。在支持广播的局域网中设置NTP协议,可以使NTP服务器每隔一个固定的时间间隔,就向全网发送时间信息,客户端在收到时间信息后进行更新处理。

原理解析

  要进行广播通信,首先要理解广播地址。在IP地址中,如果最后一个数字是255,则一定是一个广播地址。

网络广播地址:网络广播地址在没有进行子网划分的网络内广播,由于当强的网络均涉及子网划分,故此种地址很少存在
受限广播地址:以255.255.255.255组成的广播地址,在当前路由器均不转发此类广播
子网广播地址:子网广播地址是一种常用的广播方式,它是指在一个具体的子网内进行广播,比如192.168是网络ID,那么192.168.1.255就是子网192.168.1的广播
全部子网广播地址:是指所有子网络的广播,以上一个为例,全部子网广播地址是192.168.255.255

广播要采用UDP的方式,具体流程如下:

  • 创建UDP套接字
  • 设置套接字属性为SO_BROADCAST,设置为广播地址
  • 设置广播地址为INADDR_BROADCAST,同时也要指定发送端口
  • 进行数据收发操作

    例子:
    //bserver.c
    #include <sys/types.h>
    #include <stdio.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <string.h>
    #include <netdb.h>
    #include <errno.h>
    
    #define BUFFSIZE 200
    #define PORT 5050
    
    int main(int argc, char **argv)
    {
        int serversocket;
        struct sockaddr_in serveraddress,clientaddress;
    
        int so_broadcast=1;
    
    
        if((serversocket=socket(AF_INET,SOCK_DGRAM,0))<0){
            perror("socket");
            return 0;
        }
    
        if(setsockopt(serversocket,SOL_SOCKET,SO_BROADCAST,&so_broadcast,sizeof(so_broadcast))<0){
            perror("setsockopt");
            return 0;
        }
    
        serveraddress.sin_family=AF_INET;
        serveraddress.sin_port=htons(INADDR_ANY);
        serveraddress.sin_addr.s_addr=htonl(INADDR_BROADCAST);
    
        if(bind(serversocket,(struct sockaddr*)&serveraddress,sizeof(struct sockaddr))<0){
            perror("bind");
            return 0;
        }
    
        clientaddress.sin_family=AF_INET;
        clientaddress.sin_port=htons(PORT);
        clientaddress.sin_addr.s_addr=htonl(INADDR_BROADCAST);
    
        while(1){
            char buf[BUFFSIZE];
            printf("please input your word:");
            scanf("%s",buf);
            if(sendto(serversocket,buf,strlen(buf),0,(struct sockaddr*)&clientaddress,sizeof(clientaddress))<0){
                perror("sendto");
                return 0;
            }
            else
                printf("send msg: %s\n",buf);
        }
    
        return 0;
    }
    
    //bclient.c
    #include <sys/types.h>
    #include <stdio.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <string.h>
    #include <netdb.h>
    #include <errno.h>
    
    int main(int argc, char **argv)
    {
        int clientsocket;
        struct sockaddr_in serveraddress,clientaddress;
    
        clientsocket=socket(AF_INET,SOCK_DGRAM,0);
    
        serveraddress.sin_family=AF_INET;
        serveraddress.sin_port=htons(5050);
        serveraddress.sin_addr.s_addr=htonl(INADDR_ANY);
    
        int opt=1;
        if(setsockopt(clientsocket,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))<0){
            perror("setsockopt");
            return 0;
        }
    
        if(bind(clientsocket,(struct sockaddr*)&serveraddress,sizeof(struct sockaddr))!=0){
            perror("bind");
            return 0;
        }
    
        char buf[200];
    
        while(1){
            memset(buf,0,200);
            int size=0;
            size=recvfrom(clientsocket,buf,200,0,(struct sockaddr*)&serveraddress,sizeof(serveraddress));
            buf[size]='\0';
            printf("IP:%s msg:%s\n",inet_ntoa(clientaddress.sin_addr),buf);
    
            if(strcmp(buf,"quit")==0){
                printf("system quit!\n");
                close(clientsocket);
                return 0;
            }
        }
    
        return 0;
    }
    

组播技术:

  组播可以实现小范围内的互联,在发送者和每一个接受者之间时间点对多点的网络连接,是广播通信的一种变种。

  根据IP地址的规定,D类地址为组播地址,其网络号为固定的1110,第4到31位定义了某一特殊的组播地址,范围为244.0.0.0~239.255.255.255。其中244.0.0.0~244.0.0.255的地址,它们大多是为了特殊的目的保留的,不建议使用。

套接字的基本属性:组播参数对应5个参数,通过setsockopt设置

//加入组播
int setsockopt(client_socket,IPPROTO_IP,IP_ADD_MEMBERSHIP,&multiaddress,sizeof(multiaddress))

//退出组播
int setsockopt(client_socket,IPPROTO_IP,IP_DROP_MEMBERSHIP,&multiaddress,sizeof(multiaddress))

//这里有一个重要的参数multiaddress,结构:
struct ip_mreq{
    struct in_addr imr_multiaddr;        //组播地址
    struct in_addr imr_interface;          //IPv4地址
}

主要流程:

服务器端设置一个多播地址,创建一个多播组。
客户端指定多播地址,加入多播。
程序结束后,退出多播。
例子:

//memberServer.c

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
    int server_socket;
    struct sockaddr_in address;

    //创建UDP
    server_socket=socket(AF_INET,SOCK_DGRAM,0);
    if(server_socket<0){
        perror("socket");
        return 0;
    }

    //初始化多播地址
    memset(&address,0,sizeof(address));
    address.sin_family=AF_INET;
    address.sin_port=htons(5555);
    address.sin_addr.s_addr=inet_addr("224.0.1.100");

    //发送信息
    while(1){
        char buf[200];
        printf("input your word:");
        scanf("%s",buf);
        if(sendto(server_socket,buf,sizeof(buf),0,(struct sockaddr*)&address,sizeof(address))<0){
            perror("sendto");
            return 0;
        }
    }

    return 0;
}

//memberClient.c

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
    struct ip_mreq mreq;
    int serveraddress_len;
    int client_socket;
    struct sockaddr_in serveraddress;

    //初始化地址
    memset(&serveraddress,0,sizeof(serveraddress));
    serveraddress.sin_family=AF_INET;
    serveraddress.sin_port=htons(5555);
    serveraddress.sin_addr.s_addr=htonl(INADDR_ANY);

    if((client_socket=socket(AF_INET,SOCK_DGRAM,0))<0){
        perror("client");
        return 0;
    }

    //绑定SOCKET
    if(bind(client_socket,(struct sockaddr*)&serveraddress,sizeof(serveraddress))<0){
        printf("bind");
        return 0;
    }

    int opt=1;
    if(setsockopt(client_socket,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))<0){
        printf("setsockopt1");
        return 0;
    }

    //加入多播
    mreq.imr_multiaddr.s_addr=inet_addr("244.0.1.100");
    mreq.imr_interface.s_addr=htonl(INADDR_ANY);

    if(setsockopt(client_socket,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))<0){
        perror("setsockopt2");
        return 0;
    }

    while(1){
        char buf[200];
        serveraddress_len=sizeof(serveraddress);
        if(recvfrom(client_socket,buf,200,0,(struct sockaddr*)&serveraddress,(socklen_t *)serveraddress_len)<0){
            perror("recvfrom");
        }
        printf("msg from server: %s\n",buf);

        if(strcmp(buf,"quit")==0){
            if(setsockopt(client_socket,IPPROTO_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(mreq))<0){
                perror("setsokopt3");
            }
            close(client_socket);
            return 0;
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: SocketCAN是Linux内核的一个模块,它提供了用于控制CAN总线通信的标准套接字接口。使用SocketCAN编程可以方便地与CAN总线进行通信,并实现数据的发送和接收。 通过使用SocketCAN编程,可以使用C、C++等编程语言,使用套接字接口访问CAN总线,以实现CAN数据的接收和发送。例如,可以通过调用SocketCAN提供的接口函数,创建CAN帧,设置CAN帧的标识符和数据,然后通过套接字发送该帧到CAN总线上。同时,可以使用SocketCAN提供的接口函数,监听CAN总线上的CAN帧,并在收到CAN帧时获取CAN帧的数据和标识符。 SocketCAN编程不仅可以在应用程序使用,也可以在开发CAN总线驱动程序时使用。例如,在Linux内核实现一个CAN总线驱动程序时,可以使用SocketCAN提供的接口函数,轻松地与CAN总线进行通信。 总之,SocketCAN编程是一种方便快捷地实现CAN总线通信的方法,可以帮助开发人员实现高效的CAN数据传输,并且可以应用于多种开发场景。 ### 回答2: SocketCAN是Linux用于与CAN总线进行通信的标准API。它提供了一种与CAN硬件通信的方式,允许开发人员使用C和C++语言在Linux操作系统上开发CAN通信应用程序。SocketCAN编程可以通过socket的方式与CAN设备进行通信,类似于TCP/IP网络,通过套接字(socket)建立连接,实现数据的发送与接收。 在SocketCAN编程,需要先安装SocketCAN驱动,并配置CAN设备的接口。可以通过使用ip命令来设置CAN接口,包括设备驱动名称、波特率、数据位率等。然后,使用SocketCAN API编写程序与CAN总线进行通信。SocketCAN API提供了一系列函数,如socket、connect、bind、sendto、recvfrom等,开发人员只需要使用这些函数就可以完成CAN总线通信。 SocketCAN编程可以在Linux系统上进行,支持多种编程语言,如C/C++、Python、Java等。与传统CAN编程相比,SocketCAN编程更加灵活,可以方便地对CAN硬件进行访问和配置。同时,SocketCAN还支持CAN FD协议,可以更快地传输大容量数据。 总之,SocketCAN编程是在Linux系统上使用SocketCAN API与CAN总线进行通信的一种方式,可以通过socket建立连接,发送和接收CAN消息,方便地开发CAN通信应用程序。 ### 回答3: SocketCAN是针对Linux内核的CAN总线子系统所开发的一套高级接口。它提供了一组简单易用、可移植、高效的网络编程接口,可实现对CAN总线的控制和管理。SocketCAN的API接口与Linux的套接字API相似,可以帮助开发者进行CAN总线的开发和测试。 SocketCAN编程需要先安装CAN总线控制器并下载相应的驱动程序。接下来就可以使用SocketCAN提供的函数和命令来配置CAN总线参数和操作CAN总线。例如,可以使用socket()函数创建CAN套接字以进行通信,使用bind()函数来绑定CAN套接字到特定的CAN总线接口上。 SocketCAN在CAN总线各种协议和速率上都表现出一定的性能优势,它可以批量和快速地处理大量的CAN数据帧,大大提供了CAN总线数据帧的传输效率和稳定性。此外,SocketCAN还支持多种CAN协议,包括ISO 15765-2,J1939,CANopen等,能够适应多种不同的CAN应用场景。 总的来说,SocketCAN编程是Linux系统下CAN总线开发的首选,它方便简单、易于掌握、兼容性好,能够为开发者提供高效、稳定的CAN总线通信服务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值