socket网络编程——基础

socket网络编程——基础

本文重点介绍 socket 网络编程中的套接字函数,介绍各个函数是什么以及怎么用

一、常用套接字函数介绍

使用套接字函数需要包含头文件 <arpa/inet.h>

1、socket() 函数

// 创建一个套接字
int socket(int domain, int type, int protocol);

参数 domain:使用的地址族协议,AF_INET 为使用 IPv4 格式的 ip 地址,AF_INET6 为使用IPv6格式的 ip 地址,一般选择 IPv4

参数 type:SOCK_STREAM 为使用流式的传输协议(如TCP),SOCK_DGRAM 为使用报式(报文)的传输协议(如UDP),一般选择 TCP

参数 protocol:一般写 0 即可

返回值:成功则返回可用于套接字通信的 fd(文件描述符),失败则返回 -1

2、bind() 函数

// 将文件描述符和本地的IP与端口进行绑定   
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数 sockfd:监听的 fd, 通过 socket() 调用得到的返回值

参数 addr:传入参数, 要绑定的 IP 和端口信息需要初始化到这个结构体中,IP 和端口要转换为网络字节序

参数 addrlen:参数 addr 指向的内存大小, sizeof(struct sockaddr)

返回值:成功则返回 0,失败则返回 -1

3、listen() 函数

// 监听
int listen(int sockfd, int backlog);

参数 sockfd:监听的 fd, 通过 socket() 调用得到的返回值

参数 backlog:同时能处理的最大连接要求,最大值为 128

返回值:函数调用成功返回 0,调用失败返回 -1

4、accept()函数(默认阻塞)

// 等待并接受客户端的连接请求, 建立新的连接, 会得到一个新的文件描述符(通信的)		
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数 sockfd:监听的 fd, 通过 socket() 调用得到的返回值

参数 addr:传出参数, 存储建立连接的客户端地址信息

参数 addrlen:传入传出参数,存储 addr 指向的内存大小

返回值:成功则得到一个用于通信的 fd,失败则返回 -1

5、read()/recv()函数(默认阻塞)

// 接收数据
ssize_t read(int sockfd, void *buf, size_t size);
ssize_t recv(int sockfd, void *buf, size_t size, int flags);

参数 sockfd:用于通信的 fd, accept() 函数的返回值

参数 buf:指向一块内存, 存储接收的数据

参数 size:sizeof(buf)

参数 flags:一般指定为 0

返回值:大于 0 表示实际接收的字节数;等于 0 表示对方断开了连接;等于 -1表示接收数据失败

6、write()/send()函数

// 发送数据
ssize_t write(int fd, const void *buf, size_t len);
ssize_t send(int fd, const void *buf, size_t len, int flags);

参数 fd:通信的 fd, accept() 函数的返回值

参数 buf:传入参数, 要发送的字符串

参数 len:sizeof(buf)

参数 flags:一般指定为 0

返回值:大于 0 表示实际发送的字节数,等于 -1表示发送数据失败

7、connect()函数

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

参数 sockfd:通信的 fd, 通过调用 socket() 函数就得到了

参数 addr:存储要连接的服务器端的地址信息

参数 addrlen: sizeof(struct sockaddr)

返回值:连接成功则返回0,连接失败返回 -1

二、TCP通信流程

如下图所示:

在这里插入图片描述

三、简单的TCP通信代码

服务端代码:

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

void *client_thread(void *arg);

int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        perror("Could not create socket");
        return 1;
    }

    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(2000);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (-1 == bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)))
    {
        perror("Bind failed");
        close(sockfd);
        return 1;
    }

    if (-1 == listen(sockfd, 128))
    {
        perror("Listen failed");
        close(sockfd);
        return 1;
    }
    printf("Listening on port 2000...\n");

    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);

    while (1)
    {
        int clientfd = accept(sockfd, (struct sockaddr *)&clientaddr, &len);
        if (-1 == clientfd)
        {
            perror("Accept failed");
            continue;
        }
        printf("Connection accepted: %d\n", clientfd);

        pthread_t thid;
        if (0 != pthread_create(&thid, NULL, client_thread, &clientfd))
        {
            perror("Fail to create thread");
            close(clientfd);
        }
        pthread_detach(thid);
    }
}

void *client_thread(void *arg)
{
    int clientfd = *(int *)arg;

    char buffer[1024] = {0};
    while (1)
    {
        int count = recv(clientfd, buffer, sizeof(buffer), 0);
        if (count <= 0)
        { // 断开连接或发生错误
            if (count == 0)
            {
                printf("client disconnect: %d\n", clientfd);
            }
            else
            {
                perror("recv failed");
            }
            close(clientfd);
            return NULL;
        }
        printf("RECV[%d]: %s\n", clientfd, buffer);
        count = send(clientfd, buffer, count, 0);
        if (count < 0)
        {
            perror("send failed");
            close(clientfd);
            return NULL;
        }
        printf("SEND[%d]: %d bytes\n", clientfd, count);
    }
}

客户端用NetAssist(网络助手)模拟即可,是个很轻量的软件,有需要的朋友,链接如下:链接: https://pan.baidu.com/s/1tNtUHSNESZ7PTx4V9FQKQQ 提取码: crx9

四、代码测试

编译并运行:

在这里插入图片描述

开三个NetAssist连接,收发数据正常,说明代码逻辑没问题

在这里插入图片描述

在这里插入图片描述

五、总结

其实上述代码就是经典的一请求一线程模型,相比于大家所熟悉的IO多用复用,其实一请求一线程也不是一无是处,某种程度上来说是异步的,将IO就绪与IO收发数据分开,但是,一个请求就需要开一个线程,显然是很浪费资源的,并且 fd 也不好管理,如果有一种轻量级的线程就好了,也就是所谓的协程,这部分内容以后再详细讨论

更多资源可以查看:学习资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值