linux-c-socket

2 篇文章 0 订阅

网络

ip

port:定位到主机的某一个进程,通过这个端口进程就可以接收到对应的网络数据了。unsigned int (0 ~ 2^`16-1)

头文件:sys/socket.h

字节序

字节序:字节的顺序,单个字节没有这个概念。字符串是字符的结合,也没有字节序问题。字符也没有字节序问题。
目前在各种体系的计算机中通常采用的字节存储机制主要有两种:Big-Endiam(大端,网络) 和 Little-Endian(小端,主机)。发送时:小端转大端。 接受时:大端转小端。

  • Little-Endian:数据的低位字节存储到内存的低地址为,数据的高位存储到内存的高地址位。(低低高高

  • Big-Endiam:数据的低位字节存储到内存的高地址为,数据的高位存储到内存的低地址位。(低高高低

  • 套接字通信过程操作的数据都是大端存储的,包括:接受/发送的数据、ip地址、端口

大小端转换函数

ip地址转换

头文件#include <arp/inet>

/*
af:地址族(ipv4 ipv6)
	- AF_INET:ipv4格式的地址
	- AF_INET6:ipv6格式的地址
src:传入的参数,对应要转换的点分十进制的ip地址
dst:传出的参数,函数调用完成,转换得到大端整形ip被写入到这块内存中
返回值:成功返回1,失败返回0或-1
*/
int inet_pton(int af, const char* src, void *dst);
/*
af:地址族(ipv4 ipv6)
	- AF_INET:ipv4格式的地址
	- AF_INET6:ipv6格式NULL的地址
src:传入的参数,这个指针指向内存中的大端整形ip地址
dst:传出参数,存储转换得到的小端的点分十进制的ip地址
size:修饰dst参数,标记dst指向的内存最多可以有多少个字节
返回值:
	- 成功:返回指针指向第三个参数对应的内存地址,通过返回值也可以直接取出转换得到的ip字符串
	- 失败:返回NULL
*/
const char* inet_ntop(int af, const void* src, char* dst, soklen_t size);
// 点分十进制 转 大端整形
in_addr_t inet_addr(const char* cp);
// 大端整形 转 点分十进制
char* inet_ntoa(struct in_addr in);

TCP通信流程 特点

特点:面向连接、安全的流式传输协议。

流程:

服务器通信流程:

  1. 创建一个用于监听的套接字,这个套接字是一个文件描述符

    int lfd = socket();
    
  2. 将监听的文件描述符和本地ip端口进行绑定

    bind();
    
  3. 设置监听(成功之后开始监听,监听的是客户端的链接)

    listen();
    
  4. 等待并接受客户端的请求,建立新的链接,会得到新的一个文件描述符(通信的),没有新连接请求就会阻塞

    int cfd = accept();
    
  5. 通信,读写操作默认都是阻塞

    read(); // 接受
    write();//发送
    
  6. 断开链接,关闭套接字

    close();
    

客户端的通信流程

  1. 创建一个通信的套接字

    int cfd = socket();
    
  2. 连接服务器,需要知道服务器绑定的ip和端口

    connect();
    
  3. 通信

    read(); //
    close();
    
  4. 断开连接

    close();
    

套接字函数

在终端用 man 函数名查看文档

sockt通信案例

简单收发:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h> // 包含了sys/socket.h 头文件



int main() {

    // 1.创建监听的套接子
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("socket");
        return -1;
    }
    // 2.绑定本地的ip port
    struct sockaddr_in saddr; // 初始化为 struct sockaddr_in,然后在传参数的时候强制转换到 struct sockaddr
    // 初始化
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY;  // 0 = 0.0.0.0,对于0来说,大端和小端没有区别,可以绑定本地的任何个一个ip地址。会自动区读取网卡的实际ip地址。
    int ret = bind(fd, (struct sockaddr*) &saddr, sizeof(saddr));
    if (ret == -1) {
        perror("bind");
        return -1;
    }
    // 3.设置监听
    ret = listen(fd, 128);
    if (ret == -1) {
        perror("listen");
        return -1;
    }
    // 4. 阻塞等待客户连接
    struct sockaddr_in caddr;
    int addrlen = sizeof(caddr);
    int cfd = accept(fd, (struct sockaddr*) &caddr, &addrlen);
    if (cfd == -1) {
        perror("accept");
        return -1;
    }
    char ip[32];
    printf("客户端的ip:%s, 端口号:%d\n", inet_ntop(AF_INET, &caddr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(caddr.sin_port));
    // 5. 通信
    while (1)
    {
        char buff[1024];
        int len = recv(cfd, buff, sizeof(buff), 0);
        if (len > 0) {
            printf("client says: %s\n", buff);
            send(cfd, buff, len, 0);
        } else if (len == 0) {
            printf("client已经断开连接\n");
            break;
        } else {
            perror("recv");
            break;
        }
    }
    // 6.关闭所有的文件描述符
    close(fd);
    close(cfd);    

    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h> // 包含了sys/socket.h 头文件





int main() {

    // 1.创建用于通信的套接子
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("socket");
        return -1;
    }
    // 2.连接服务器ip port
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    inet_pton(AF_INET, "192.168.137.129", &saddr.sin_addr.s_addr);
    int ret = connect(fd, (struct sockaddr*) &saddr, sizeof(saddr));
    if (ret == -1) {
        perror("socket");
        return -1;
    }
    // 3. 进行通信
    char buff[1024];
    int number = 0;
    while (1) {
        sprintf(buff, "你好, hello world,%d..\n", number++); 
        send(fd, buff, strlen(buff) + 1, 0);
        // 接受
        memset(buff, 0, sizeof(buff));
        int len = recv(fd, buff, sizeof(buff), 0);
        if (len > 0) {
            printf("server says: %s\n", buff);
        } else if (len == 0) {
            printf("服务器已经断开连接\n");
            break;
        } else {
            perror("recv");
            break;
        }
        sleep(2);
    }
    close(fd);

    return 0;
}
服务器并发

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h> // 包含了sys/socket.h 头文件
#include <pthread.h>

// 信息结构体
struct SockInfo{
    struct sockaddr_in addr; //地址信息
    int fd; // 文件描述符
};
// 存储信息
struct SockInfo infos[512];

void* working(void* args);

int main() {

    // 1.创建监听的套接子
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("socket");
        return -1;
    }
    // 2.绑定本地的ip port
    struct sockaddr_in saddr; // 初始化为 struct sockaddr_in,然后在传参数的时候强制转换到 struct sockaddr
    // 初始化
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY;  // 0 = 0.0.0.0,对于0来说,大端和小端没有区别,可以绑定本地的任何个一个ip地址。会自动区读取网卡的实际ip地址。
    int ret = bind(fd, (struct sockaddr*) &saddr, sizeof(saddr));
    if (ret == -1) {
        perror("bind");
        return -1;
    }
    // 3.设置监听
    ret = listen(fd, 128);
    if (ret == -1) {
        perror("listen");
        return -1;
    }
    // 4. 阻塞等待客户连接
    int addrlen = sizeof(struct sockaddr_in);

    // 初始化结构体数组
    int max = sizeof(infos) / sizeof(infos[0]);
    for(int i = 0; i < max; i++) {
        bzero(&infos[i], sizeof(infos[i]));
        infos[i].fd = -1; // -1表示无效值,当前的fd是不可以用的,可以申请这个元素
    }
    while (1)
    {
        struct SockInfo* pinfo = NULL;
        for(int i = 0; i < max;i ++) {
            if(infos[i].fd == -1) {
                pinfo = &infos[i];
                break;
            } 
        }
        int cfd = accept(fd, (struct sockaddr*) &pinfo->addr, &addrlen);
        pinfo->fd = cfd; //保存文件标识符
        if (cfd == -1) {
            perror("accept");
            break; // 放弃连接
            // continue; //冲连
        }
        char ip[32];
        printf("客户端的ip:%s, 端口号:%d\n", inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(pinfo->addr.sin_port));
        // 创建子线程
        pthread_t tid;
        pthread_create(&tid, NULL, working, pinfo);
        pthread_detach(tid); //子线程与主线程相分离
    }
    
    close(fd);
    return 0;
}

// 子线程
void* working(void* args) {
    struct SockInfo* pinfo = (struct SockInfo*) args;
    // 5. 通信
    while (1)
    {
        char buff[1024];
        int len = recv(pinfo->fd, buff, sizeof(buff), 0);
        if (len > 0) {
            printf("client says: %s\n", buff);
            send(pinfo->fd, buff, len, 0);
        } else if (len == 0) {
            printf("client已经断开连接\n");
            break;
        } else {
            perror("recv");
            break;
        }
    }
    // 6.关闭文件描述符
    close(pinfo->fd);
    pinfo->fd = -1;    // 记录已经回收的元素

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值