八.网络套接字

讨论:跨主机的传输要注意的问题

  1. 字节序问题

大端:低地址放高字节
小端:低地址放低字节
主机字节序:host
网络字节序:network
解决_ to __: htons, htonl, ntohs, ntohl

  1. 对齐

逻辑地址号(0开始)能否整除sizeof(类型)
eg:

struct {
  int i;
  float f;
  char c;
}
解决:不对齐!

  1. 类型长度问题:
    int
    char
    解决:int32_t, uint32_t, int64_t, int8_t

1.SOCKET是什么

在这里插入图片描述
RETURN VALUE
On success, a file descriptor for the new socket is returned. On error, -1 is returned, and errno is set appropriately.

参数domain:

在这里插入图片描述
参数type:

在这里插入图片描述
SOCK_STREAM流式,SOCK_DGRAM报式

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain:
	AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
	AF_INET6 与上面类似,不过是来用IPv6的地址
	AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用
type:
	SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
	SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
	SOCK_SEQPACKET该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。
	SOCK_RAW socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
	SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序
protocol:0 表示使用默认协议。
返回值:
	成功:返回指向新创建的socket的文件描述符,失败:返回-1,设置errno


2.报式套接字

被动端:(先运行)

1.取得SOCKET
2.给SOCKET取得地址
3.收/发消息
4.关闭SOCKET

主动端:

1.取得SOCKET
2.给SOCKET取得地址(可省略)
3.发/收消息
4.关闭SOCKET

函数:

在这里插入图片描述
const struct sockaddr * addr不同domain不同(The rules used in name binding vary between address families. Consult the manual entries in Section 7 for detailed information. For AF_INET, see ip(7); for AF_INET6, see ipv6(7); for AF_UNIX, see unix(7); for AF_APPLETALK, see ddp(7); for AF_PACKET, see packet(7); for AF_X25, see x25(7); and for AF_NETLINK, see netlink(7).)
在这里插入图片描述


inet_pton
在这里插入图片描述
描述:
This function converts the character string src into a network address structure in the af address family, then copies the network address structure to dst. The af argument must be either AF_INET or AF_INET6. dst is written in network byte order.


在这里插入图片描述
recv用于流式传输(点对点一对一的,不需要知道对端地址)
recvfrom用于报式


在这里插入图片描述


在这里插入图片描述
send用于流式,sendto用于报式,原因同recv和recvfrom

proto.h

#ifndef PROTO_H__
#define PROTO_H__


#define   RCVPORT    "1989"  //端口
#define   NAMESIZE   11

#include<stdint.h>
struct msg_st
{
    uint8_t name[NAMESIZE];
    uint32_t math;
    uint32_t chinese;
}__attribute__((packed));//不要对齐


#endif

rcver.c

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


#define   IPSTRSIZE   40
int main() {
    int sd;
    struct sockaddr_in laddr, raddr; // man 7 ip看 Address format
    struct msg_st rbuf;     //消息结构体
    socklen_t raddr_len;
    char ipstr[IPSTRSIZE];
//     1.取得SOCKET
    sd = socket(AF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/);  // 报式
    if(sd < 0) {
        perror("socket()");
        exit(1);
    }
//     2.给SOCKET取得地址
    laddr.sin_family = AF_INET;
    laddr.sin_port = htons(atoi(RCVPORT));
    inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);//0.0.0.0可以匹配任何地址(被换成自己的地址)
    if(bind(sd,(void*)&laddr,sizeof(laddr)) < 0) {
        perror("bind()");
        exit(1);
    }
    raddr_len = sizeof(raddr);
//     3.收/发消息
    raddr_len = 0;//一定要初始化
    while(1) {
        recvfrom(sd, &rbuf, sizeof(rbuf),0, (void*)&raddr, &raddr_len);
        //This  function  converts the network address structure src in the af address family into a character string.
        inet_ntop(AF_INET, &raddr.sin_addr, ipstr, IPSTRSIZE);
        printf("---MESSAGE FROM %s:%d---\n", ipstr, ntohs(raddr.sin_port));
        printf("NAME = %s\n", rbuf.name);
        printf("MATH = %d\n", ntohl(rbuf.math));
        printf("CHINESE = %d\n", ntohl(rbuf.chinese));
    }
     
//     4.关闭SOCKET
    close(sd);
    exit(0);
}

snder.c

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

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

    int sd;
    struct msg_st sbuf;
    struct sockaddr_in raddr;
    if(argc < 2) {
        fprintf(stderr, "Usage...\n");
        exit(1);
    }
//      1.取得SOCKET
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sd < 0) {
        perror("socket()");
        exit(0);
    }
//      2.给SOCKET取得地址(可省略)
//      3.发/收消息
    strcpy(sbuf.name, "Alan");
    sbuf.math = htonl(rand() % 100);
    sbuf.chinese = 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, &sbuf, sizeof(sbuf), 0, (void*)&raddr, sizeof(raddr)) < 0) {
        perror("sendto()");
        exit(1);
    }
    puts("OK!");
//      4.关闭SOCKET
    close(sd);
    exit(0);
}

结果:
在这里插入图片描述

3.动态报式套接字

名字改成不定长的

proto.h

#ifndef PROTO_H__
#define PROTO_H__


#define   RCVPORT    "1989"  //端口

//name能存在的最大长度
#define   NAMEMAX    (512-8-8)     //512是udp包的推荐长度,第一个8是udp包的报头大小,第二个8是定长uint32_t math and chinese

#include<stdint.h>
struct msg_st
{ 
    uint32_t math;
    uint32_t chinese;
    uint8_t name[1];        //只是个占位符
}__attribute__((packed));//不要对齐


#endif

rcver.c

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


#define   IPSTRSIZE   40
int main() {
    int sd, size;
    struct sockaddr_in laddr, raddr; // man 7 ip看 Address format
    struct msg_st* rbufp;     //消息结构体
    socklen_t raddr_len;
    char ipstr[IPSTRSIZE];

    size = sizeof(struct msg_st) + NAMEMAX - 1;
    rbufp = malloc(size);
    if(rbufp == NULL) {
        perror("malloc");
        exit(1);
    }
//     1.取得SOCKET
    sd = socket(AF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/);  // 报式
    if(sd < 0) {
        perror("socket()");
        exit(1);
    }
//     2.给SOCKET取得地址
    laddr.sin_family = AF_INET;
    laddr.sin_port = htons(atoi(RCVPORT));
    inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);//0.0.0.0可以匹配任何地址(被换成自己的地址)
    if(bind(sd,(void*)&laddr,sizeof(laddr)) < 0) {
        perror("bind()");
        exit(1);
    }
    raddr_len = sizeof(raddr);
//     3.收/发消息
    raddr_len = 0;//一定要初始化
    while(1) {
        recvfrom(sd, rbufp, size,0, (void*)&raddr, &raddr_len);
        //This  function  converts the network address structure src in the af address family into a character string.
        inet_ntop(AF_INET, &raddr.sin_addr, ipstr, IPSTRSIZE);
        printf("---MESSAGE FROM %s:%d---\n", ipstr, ntohs(raddr.sin_port));
        printf("NAME = %s\n", rbufp->name);
        printf("MATH = %d\n", ntohl(rbufp->math));
        printf("CHINESE = %d\n", ntohl(rbufp->chinese));
    }
     
//     4.关闭SOCKET
    close(sd);
    exit(0);
}

snder.c

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

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

    int sd, size;
    struct msg_st* sbufp;
    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]);
    sbufp = malloc(size);
    if (sbufp == NULL) {
        perror("malloc");
        exit(1);
    }
    
//      1.取得SOCKET
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sd < 0) {
        perror("socket()");
        exit(0);
    }
//      2.给SOCKET取得地址(可省略)
//      3.发/收消息
    strcpy(sbufp->name, argv[2]);
    sbufp->math = htonl(rand() % 100);
    sbufp->chinese = 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, sbufp, size, 0, (void*)&raddr, sizeof(raddr)) < 0) {
        perror("sendto()");
        exit(1);
    }
    puts("OK!");
//      4.关闭SOCKET
    close(sd);
    exit(0);
}

在这里插入图片描述

4. 报式套接——多点通讯

多点通讯只能用报式实现,流式是点对点的。
多点通讯:

广播(全网广播,子网广播)
多播/组播

函数:

在这里插入图片描述
获取和设置socket属性

广播

proto.h

#ifndef PROTO_H__
#define PROTO_H__


#define   RCVPORT    "1989"  //端口

//name能存在的最大长度
#define   NAMEMAX    (512-8-8)     //512是udp包的推荐长度,第一个8是udp包的报头大小,第二个8是定长uint32_t math and chinese

#include<stdint.h>
struct msg_st
{ 
    uint32_t math;
    uint32_t chinese;
    uint8_t name[1];        //只是个占位符
}__attribute__((packed));//不要对齐


#endif

rcver.c

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


#define   IPSTRSIZE   40
int main() {
    int sd, size;
    struct sockaddr_in laddr, raddr; // man 7 ip看 Address format
    struct msg_st* rbufp;     //消息结构体
    socklen_t raddr_len;
    char ipstr[IPSTRSIZE];

    size = sizeof(struct msg_st) + NAMEMAX - 1;
    rbufp = malloc(size);
    if(rbufp == NULL) {
        perror("malloc");
        exit(1);
    }
//     1.取得SOCKET
    sd = socket(AF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/);  // 报式
    if(sd < 0) {
        perror("socket()");
        exit(1);
    }
    int val = 1;
    if(setsockopt(sd, SOL_SOCKET, SO_BROADCAST ,&val, sizeof(val)) < 0) {
        perror("setsockopt()");
        exit(1);
    }
//     2.给SOCKET取得地址
    laddr.sin_family = AF_INET;
    laddr.sin_port = htons(atoi(RCVPORT));
    inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);//0.0.0.0可以匹配任何地址(被换成自己的地址)
    if(bind(sd,(void*)&laddr,sizeof(laddr)) < 0) {
        perror("bind()");
        exit(1);
    }
    raddr_len = sizeof(raddr);
//     3.收/发消息
    raddr_len = 0;//一定要初始化
    while(1) {
        recvfrom(sd, rbufp, size,0, (void*)&raddr, &raddr_len);
        //This  function  converts the network address structure src in the af address family into a character string.
        inet_ntop(AF_INET, &raddr.sin_addr, ipstr, IPSTRSIZE);
        printf("---MESSAGE FROM %s:%d---\n", ipstr, ntohs(raddr.sin_port));
        printf("NAME = %s\n", rbufp->name);
        printf("MATH = %d\n", ntohl(rbufp->math));
        printf("CHINESE = %d\n", ntohl(rbufp->chinese));
    }
     
//     4.关闭SOCKET
    close(sd);
    exit(0);
}

snder.c

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

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

    int sd, size;
    struct msg_st* sbufp;
    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]);
    sbufp = malloc(size);
    if (sbufp == NULL) {
        perror("malloc");
        exit(1);
    }
    
//      1.取得SOCKET
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sd < 0) {
        perror("socket()");
        exit(1);
    }
    int val = 1;
    if(setsockopt(sd, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)) < 0) {
        perror("setsockopt()");
        exit(1);
    }
//      2.给SOCKET取得地址(可省略)
//      3.发/收消息
    memset(sbufp, '\0' , sizeof(*sbufp));
    strcpy(sbufp->name, argv[2]);
    sbufp->math = htonl(rand() % 100);
    sbufp->chinese = htonl(rand() % 100);
    raddr.sin_family = AF_INET;
    raddr.sin_port = htons(atoi(RCVPORT));
    
    inet_pton(AF_INET, "255.255.255.255", &raddr.sin_addr);     //全网广播
    if(sendto(sd, sbufp, size, 0, (void*)&raddr, sizeof(raddr)) < 0) {
        perror("sendto()");
        exit(1);
    }
    puts("OK!");
//      4.关闭SOCKET
    close(sd);
    exit(0);
}

多播

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值