4.8Socket套接字

介绍

字节序介绍:

 

/*
    网络通信时,需要将主机字节序转换成网络字节序(大端),
    另外一段获取到数据以后根据情况将网络字节序转换成主机字节序。

    // 转换端口
    uint16_t htons(uint16_t hostshort);		// 主机字节序 - 网络字节序
    uint16_t ntohs(uint16_t netshort);		// 网络字节序 - 主机字节序

    // 转IP
    uint32_t htonl(uint32_t hostlong);		// 主机字节序 - 网络字节序
    uint32_t ntohl(uint32_t netlong);		// 网络字节序 - 主机字节序
*/
#include<stdio.h>
#include<arpa/inet.h>
int main()
{
    //htons 转换端口
    unsigned short a = 0x0102;
    printf("a:%x\n",a);
    unsigned short b = htons(a);
    printf("b:%x\n",b);

    printf("====================\n");

    //htonl 转换ip
    char buf[4]={192,168,1,100};
    int num = *(int *)buf;
    int sum = htonl(num);
    unsigned char *p=(unsigned char *)&sum;
    printf("%d %d %d %d\n",*p,*(p+1),*(p+2),*(p+3));

    printf("====================\n");

    //ntohl
    unsigned char buf1[4]={1,1,168,192};
    int num1=*(int *)buf1;
    int sum1=ntohl(num1);
    unsigned char *p1=(unsigned char*)&sum1;
    printf("%d %d %d %d\n",*p1,*(p1+1),*(p1+2),*(p1+3));

    //ntohs
    unsigned short a1=0x0201;
    printf("a1 : %x\n",a1);
    unsigned short b1=ntohs(a1);
    printf("b1 : %x\n",b1);
    return 0;
}

 

/*
    #include <arpa/inet.h>
    // p:点分十进制的IP字符串,n:表示network,网络字节序的整数
    int inet_pton(int af, const char *src, void *dst);
        af:地址族: AF_INET  AF_INET6
        src:需要转换的点分十进制的IP字符串
        dst:转换后的结果保存在这个里面

    // 将网络字节序的整数,转换成点分十进制的IP地址字符串
    const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
        af:地址族: AF_INET  AF_INET6
        src: 要转换的ip的整数的地址
        dst: 转换成IP地址字符串保存的地方
        size:第三个参数的大小(数组的大小)
        返回值:返回转换后的数据的地址(字符串),和 dst 是一样的

*/
#include<stdio.h>
#include<arpa/inet.h>

int main(){
    //创建一个ip字符串,点分十进制的ip地址字符串
    char buf[]="192.168.1.4";
    unsigned int num=0;

    //将点分十进制的IP字符串转换成网络字节序的整数
    inet_pton(AF_INET,buf,&num);
    unsigned char *p = (unsigned char *)&num;
    printf("%d %d %d %d\n",*p,*(p+1),*(p+2),*(p+3));

    //将网络字节序的ip整数转换成点分十进制的ip字符串
    char ip[16]="";
    const char * str=inet_ntop(AF_INET,&num,ip,sizeof(ip));
    printf("str : %s\n",str);
    printf("ip : %s\n",str);
    printf("%d\n",ip==str);
    return 0;
}

套接字函数

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> // 包含了这个头文件,上面两个就可以省略
int socket(int domain, int type, int protocol);
- 功能:创建一个套接字
- 参数:
- domain: 协议族
    AF_INET : ipv4
    AF_INET6 : ipv6
    AF_UNIX, AF_LOCAL : 本地套接字通信(进程间通信)
- type: 通信过程中使用的协议类型
    SOCK_STREAM : 流式协议
    SOCK_DGRAM : 报式协议
- protocol : 具体的一个协议。一般写0
    - SOCK_STREAM : 流式协议默认使用 TCP
    - SOCK_DGRAM : 报式协议默认使用 UDP
- 返回值:
    - 成功:返回文件描述符,操作的就是内核缓冲区。
    - 失败:-1
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // socket命
名
- 功能:绑定,将fd 和本地的IP + 端口进行绑定
- 参数:
    - sockfd : 通过socket函数得到的文件描述符
    - addr : 需要绑定的socket地址,这个地址封装了ip和端口号的信息
    - addrlen : 第二个参数结构体占的内存大小
int listen(int sockfd, int backlog); // /proc/sys/net/core/somaxconn
- 功能:监听这个socket上的连接
- 参数:
    - sockfd : 通过socket()函数得到的文件描述符
    - backlog : 未连接的和已经连接的和的最大值, 5
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- 功能:接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接
- 参数:
    - sockfd : 用于监听的文件描述符
    - addr : 传出参数,记录了连接成功后客户端的地址信息(ip,port)
    - addrlen : 指定第二个参数的对应的内存大小
- 返回值:
    - 成功 :用于通信的文件描述符
    - -1 : 失败
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
7. TCP 三次握手
TCP 是一种面向连接的单播协议,在发送数据前,通信双方必须在彼此间建立一条连接。所谓的“连
接”,其实是客户端和服务器的内存里保存的一份关于对方的信息,如 IP 地址、端口号等。
TCP 可以看成是一种字节流,它会处理 IP 层或以下的层的丢包、重复以及错误问题。在连接的建立过程
中,双方需要交换一些连接的参数。这些参数可以放在 TCP 头部。
TCP 提供了一种可靠、面向连接、字节流、传输层的服务,采用三次握手建立一个连接。采用 四次挥手
来关闭一个连接。
16 位端口号(port number):告知主机报文段是来自哪里(源端口)以及传给哪个上层协议或
应用程序(目的端口)的。进行 TCP 通信时,客户端通常使用系统自动选择的临时端口号。
32 位序号(sequence number):一次 TCP 通信(从 TCP 连接建立到断开)过程中某一个传输
方向上的字节流的每个字节的编号。假设主机 A 和主机 B 进行 TCP 通信,A 发送给 B 的第一个
TCP 报文段中,序号值被系统初始化为某个随机值 ISN(Initial Sequence Number,初始序号
值)。那么在该传输方向上(从 A 到 B),后续的 TCP 报文段中序号值将被系统设置成 ISN 加上
该报文段所携带数据的第一个字节在整个字节流中的偏移。例如,某个 TCP 报文段传送的数据是字
节流中的第 1025 ~ 2048 字节,那么该报文段的序号值就是 ISN + 1025。另外一个传输方向(从
B 到 A)的 TCP 报文段的序号值也具有相同的含义。
32 位确认号(acknowledgement number):用作对另一方发送来的 TCP 报文段的响应。其值是
收到的 TCP 报文段的序号值 + 标志位长度(SYN,FIN) + 数据长度 。假设主机 A 和主机 B 进行
TCP 通信,那么 A 发送出的 TCP 报文段不仅携带自己的序号,而且包含对 B 发送来的 TCP 报文段
的确认号。反之,B 发送出的 TCP 报文段也同样携带自己的序号和对 A 发送来的报文段的确认序
号。
4 位头部长度(head length):标识该 TCP 头部有多少个 32 bit(4 字节)。因为 4 位最大能表示
15,所以 TCP 头部最长是60 字节。
- 功能: 客户端连接服务器
- 参数:
    - sockfd : 用于通信的文件描述符
    - addr : 客户端要连接的服务器的地址信息
    - addrlen : 第二个参数的内存大小
- 返回值:成功 0, 失败 -1
ssize_t write(int fd, const void *buf, size_t count); // 写数据
ssize_t read(int fd, void *buf, size_t count); // 读数据

TCP socket连接案例

server端

//TCP 通信的服务器
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

int main()
{
    //1.创建socket(用于监听的套接字)
    int lfd=socket(AF_INET,SOCK_STREAM,0);
    if(lfd==-1){
        perror("socket");
        exit(-1);
    }

    //2.绑定
    struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    saddr.sin_addr.s_addr=INADDR_ANY;
    saddr.sin_port=htons(9999);
    int ret=bind(lfd,(struct sockaddr *)&saddr,sizeof (saddr));
    
    if(ret==-1){
        perror("bind");
        exit(-1);
    }

    //3.监听
    ret=listen(lfd,8);
    if(ret==-1){
        perror("listen");
        exit(-1);
    }

    //4.接收客户端连接
    struct sockaddr_in clientaddr;
    int len= sizeof(clientaddr);
    int cfd = accept(lfd,(struct sockaddr*)&clientaddr,&len);
    if(cfd==-1){
        perror("accept");
        exit(-1);
    }

    //输出客户端的信息
    char clientIP[16];
    inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientIP,sizeof(clientIP));
    unsigned short clientPort = ntohs(clientaddr.sin_port);
    
    //5.通信
    char recvBuf[1024]={0};
    while(1){
        //获取客户端的数据
        int num= read(cfd,recvBuf,sizeof (recvBuf));
        if(num==-1){
            perror("read");
            exit(-1);
        }else if(num>0){
            printf("recv client data : %s\n",recvBuf);
        }else if(num==0){
            printf("client closed...");
            break;
        }

        char *data="hello,i am server";
        //给客户端发送数据
        write(cfd,data,strlen(data));
    }

    //关闭文件描述符
    close(cfd);
    close(lfd);
    return 0;
}

client端

#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

int main(){
    //1.创建套接字
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd==-1){
        perror("socket");
        exit(-1);
    }
    
    //2.连接服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family=AF_INET;
    inet_pton(AF_INET,"192.168.108.128",&serveraddr.sin_addr);
    serveraddr.sin_port=htons(9999);
    int ret=connect(fd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
    
    if(ret==-1){
        perror("connect");
        exit(-1);
    }

    //3.通信
    char recvBuf[1024]={0};
    while(1){
        char *data="hello,i am client";
        //给客户端发送数据
        write(fd,data,strlen(data));

        sleep(1);

        int len=read(fd,recvBuf,sizeof(recvBuf));
        if(len==-1){
            perror("read");
            exit(-1);
        }else if(len>0){
            printf("recv server data : %s\n",recvBuf);
        }else if(len==0){
            //表示服务器端断开连接
            printf("server closed");
            break;
        }
    }
    close(fd);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值