【Linux】linux网络基础--socket第一部分网络套接字、UDP的socket编程

1 网络套接字

1.1 port端口号

本质:端口号是一个2字节16位的整数,范围[0,65535];

作用:端口号用来表示一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理。一个端口号只能被一个进程所占用,一个进程可以占用多个端口号(前提是在同一台机器中)。

知名端口:1~1023(轻易不要绑定)

     oracle:1521;mysql:3306;

     http协议:使用的是80端口;https协议:使用的是443端口。

1.2 网络数据的五元组信息(源IP、源端口、目的IP、目的端口、协议)

源IP地址:标识网络数据从哪台主机发出

源端口:标识网络数据从“源IP”对应这台主机的哪个进程产生

目的IP:标识网络数据要去往哪一台主机

目的端口:当通过目的IP,找到目的主机之后,通过目的端口找到对应进程

协议:双方传输数据的时候,使用什么协议(一般指UDP/TCP)

在网络当中传输的数据,都是包含五元信息组的!

1.3 网络字节库

小端字节库:低位放在低地址;

大端字节库:低位放在高地址。

网络字节序:规定网络传输数据的时候采用大端字节序进行传输;

主机字节序:指的是机器本身的字节序,如果是大端,则主机字节序为大端,反之则为小端。

1.4 主机字节序和网络字节序互相转换

主机字节序转化成为网络字节序

ip:uint32_t
    uint32_t htonl(uint32_t hostlong);  //hostlong:ip地址
port:uint16_t
    uint16_t htons(uint16_t hostshort);  //hostshort:端口

网络字节序转化为主机字节序

ip:uint32_t
    uint32_t ntohl(uint32_t netlong);  //netlong:ip地址
port:uint16_t
    uint16_t ntohs(uint16_t netshort);  //netshort:端口

1.5 TCP和UDP协议特性和区别

UDP:

无连接:UDP双方在发送数据之前,是不需要进行沟通的,只需要知道对方的IP和端口就好(可能对方进程还没准备好),就可以发送。

不可靠:不保证UDP的数据是可靠,有序的到达对方。

面向数据报:UDP和应用层/网络层递交数据的时候,都是整条数据进行交付的。

TCP:

面向链接:TCP双方在发送数据之前会先建立连接。(1、确保对方能正常通信;2、沟通双方发送后续数据的细节。)

可靠传输:TCP保证传输的数据时可靠的、有序的到达对端的。

面向字节流:1、对于传输的数据没有明显的边界;2、对于接收方而言,可以按照任意的字节进行接收。

2 udp-socket编程

2.1 编程流程

客户端 - 服务端

创建套接字的含义:

将进程和网卡进行绑定,进程可以从网络协议栈当中接受或者发送数据。

绑定地址信息的含义:

给进程绑定IP,绑定端口,为了在网络当中标识一台主机和一个进程。 

2.2 编程接口

2.2.1 创建套接字的接口

#include<sys/socket.h>

int socket(int domain,int yype,int protocol);

//参数:

//domain:地质域--选择一个具体协议组进行沟通,对于我们而言,udp/tcp可以认为在指定网络层使用什么协议。
//AF_UNIX:本地域套接字(在同一台机器使用文件进行通信,不用跨机器)
//AF_INET:ipv4版本的ip协议
//AF_INET6:ipv6版本的ip协议

//type:套接字的类型
//SOCK_DGRAM:用户数据报套接字--对应UDP
//SOCK_STREAM:流式套接字--对应TCP

//protocol:协议
//0:标识按照套接字类型选择默认协议
//IPPROTO_TCP(6):代表TCP协议
//IPPROTO_UDP(17):代表UDP协议

//返回值:返回套接字操作句柄,本质上就是一个文件描述符

//大于等于0:创建成功
//小于0:创建失败

2.2.2 绑定接口

int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);

//参数:

//sockfd:套接字描述符

//addr:绑定的地址信息

//adderlen:绑定的地址信息长度

2.2.3 发送接口

ssize_t sendto(int sockfd,const void *buf,size_t len,int flags,const struct sockaddr *dest_addr,socklen_t addrlen);

//参数:

//sockfd:套接字描述符

//buf:要发送的数据

//len:发送的数据的长度

//flags:0(阻塞发送)

//dest_addr:对端地址信息结构,包含对端主机的IP,端口

//adderlen:对端地址信息结构长度

//返回值:

//真正发送的数据长度,单位字节

2.2.4 接受接口

ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen);

//参数:

//sockfd:套接字描述符

//buf:接收到的数据保存到buf指向的空间当中,buf指向的空间需要提前开辟

//len:最多可以接受多少数据

//flags:0(阻塞接收)

//src_addr:接收到的数据从哪里来,对端的地址信息结构
     //客户端调用,接收到的就是服务端的地址信息结构
     //服务端调用,接收到的就是客户端的地址信息结构

//addrlen:绑定的地址信息长度

//返回值:

//真正接收到的数据长度,单位字节

2.2.5 关闭套接字接口

int close(int fd);

代码模拟

服务端

//这三个头文件是套接字编程需要包含的头文件
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <iostream>


int main(){
    /*
     * 1.创建套接字
     * 2.绑定地址信息
     * 3.收发数据
     * 4.关闭套接字
     * */

    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sockfd < 0){
        return 0;
    }

    //addr : 服务端绑定的地址信息结构, 保存着服务端需要绑定的ip地址和端口
    struct sockaddr_in addr;
    // 填充地址类型, 后续bind函数解析了地址类型之后, 就知道传入的是哪一个数据结构了
    addr.sin_family = AF_INET;
    // 填充端口, 一定要注意将主机字节序转换为网络字节序
    addr.sin_port = htons(8080);
    // 填充ip地址
    // addr.sin_addr.s_addr : 接收的是无符号32位的整数(ip地址)
    // in_addr_t inet_addr(const char *cp);
    //      参数:字符指针, 接收一个点分十进制的字符串
    //      功能:将点分十进制的ip地址转换为无符号32位的整数
    //            将无符号32位的整数从主机字节序转换为网络字节序
    // "0.0.0.0" : 绑定本机的任意网卡
    // 容易犯错的地方: 绑定了云服务器的公网ip, 这个是万万不行的
    //                  绑定云服务器的私网ip地址, 还可以
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");

    int ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        return 0;
    }

    //能接收, 但是没有给客户端进行响应
    while(1){
        char buf[1024] = {0};
        struct sockaddr_in peer_addr;
        socklen_t peer_addrlen = sizeof(peer_addr);
        recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&peer_addr, &peer_addrlen);

        printf("[buf is ] %s from %s, %d\n", buf, inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));

        /*
         * ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
         * */
        memset(buf, '\0', sizeof(buf));
        printf("please enter msg: ");
        fflush(stdout);
        std::cin >> buf;

        //sprintf(buf, "hello, %s:%d, i am server", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
        sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&peer_addr, sizeof(peer_addr));
    }

    close(sockfd);
    return 0;
}

接收端

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

int main(){
    int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sockfd < 0){
        perror("socket");
        return 0;
    }

    //struct sockaddr_in b_addr;
    //b_addr.sin_family = AF_INET;
    //b_addr.sin_port = htons(10010);
    //b_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    //int ret = bind(sockfd, (struct sockaddr*)&b_addr, sizeof(b_addr));
    //if(ret < 0){
    //    perror("bind");
    //    return 0;
    //}

    /*
     *  ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
     */
    const char* str = "i am client...";

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    // 这里填充的是服务端的端口, 因为数据是发送到服务端的
    addr.sin_port = htons(8080);
    // 这里填充的是服务端的ip
    // 本地回环地址
    addr.sin_addr.s_addr = inet_addr("120.78.126.148");
    //addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    while(1){
        char buf[1024] = {0};
        printf("please enter msg: ");
        fflush(stdout);
        std::cin >> buf;

        sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&addr, sizeof(addr));

        /*
         * ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
         * */
        //相当于不要recvfrom函数返回服务端的地址信息,以及地址信息的长度, 因为
        //客户端是清楚服务端的地址信息的
        memset(buf, '\0', sizeof(buf));
        recvfrom(sockfd, buf, sizeof(buf) - 1, 0, NULL, NULL);
        printf("[buf is] %s\n", buf);
        sleep(1); 
    }

    close(sockfd);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值