Linux学习——TCP

一.TCP编程API

1.socket函数

1.socket函数

include include

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

参数

domain

AF_INET

AF_INET6

AF_UNIX,AF_LOCAL

AF_NETLINK

AF_PACKET

type

SOCK_STREAM: 流式套接字,唯一对应于TCP

SOCK_DGRAM:数据报套接字,唯一对应着UDP

SOCK_RAW:原始套接字

protocol

一般填0,原始套接字编程时需填充

返回值

成功返回文件描述符

出错返回-1

如果是IPV6编程,要使用struct sockddr_in6结构体(man 7 IPV6),通常使用struct

sockaddr_storage来编程。

复制代码
int socket(int domain,int type,int protocol);
    参数
        domain
        AF_INET
        AF_INET6
        AF_UNIX,AF_LOCAL
        AF_NETLINK
        AF_PACKET
    type
        SOCK_STREAM: 流式套接字,唯一对应于TCP
        SOCK_DGRAM:数据报套接字,唯一对应着UDP
        SOCK_RAW:原始套接字
    protocol
        一般填0,原始套接字编程时需填充
    返回值
        成功返回文件描述符
        出错返回-1
        如果是IPV6编程,要使用struct sockddr_in6结构体(man 7 IPV6),通常使用struct                        
        sockaddr_storage来编程。

2.bind函数

2.bind函数

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

参数

sockfd:通过socket()函数拿到的fd

addr:采用struct sockaddr的结构体地址,通用结构体

struct sockaddr

{

sa_family_t sa_family;

char sa_data4;

}

struct sockaddr_in{ 基于Internel通信结构体

as_family_t sin_family;

in_port_t sin_port;

struct in_addr sin_addr;

sin_zero , //填充字节,需清零

}

struct in_addr{

uint32_t s_addr;

}

addrlen:地址长度

复制代码
2.bind函数
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
    参数
        sockfd:通过socket()函数拿到的fd
        addr:采用struct sockaddr的结构体地址,通用结构体
        struct sockaddr
         {
            sa_family_t sa_family;
            char sa_data[4];
         }
        struct sockaddr_in{ 基于Internel通信结构体
            as_family_t sin_family;
            in_port_t sin_port;
            struct in_addr sin_addr;
            sin_zero , //填充字节,需清零
            }
        struct in_addr{
            uint32_t s_addr;
            }
        addrlen:地址长度

3.listen函数

3.listen()函数
int listen(int sockfd,int backlog);
参数:
sockfd: 通过socket()函数拿到的fd;
backLog:同时允许几路客户端和服务器进行正在连接的过程(正在三次握手),一般填5。
内核中服务器的套接字fd会维护2个链表
1.正在三次握手的客户端链表(数量=2*backlog+1)
2.已经建立好连接的客户端链表(已经完成三次握手分配好了的newfd)
返回值:
成功返回0
出错返回-1
listen(fd,5);//表示系统允许11(2*5+1)个客户端同时进行三次握手

复制代码
3.listen()函数
int listen(int sockfd,int backlog);
    参数:
        sockfd: 通过socket()函数拿到的fd;
        backLog:同时允许几路客户端和服务器进行正在连接的过程(正在三次握手),一般填5。
            内核中服务器的套接字fd会维护2个链表
                1.正在三次握手的客户端链表(数量=2*backlog+1)
                2.已经建立好连接的客户端链表(已经完成三次握手分配好了的newfd)
        返回值:
                成功返回0
            出错返回-1
    listen(fd,5);//表示系统允许11(2*5+1)个客户端同时进行三次握手

4.accept函数

复制代码
4.accept()函数
阻塞等待客户端连接请求
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        参数
            sockfd:经过前面socket()创建并通过bind(),listen()设置过的fd
            addr:指向存放地址信息的结构体的首地址
                    获取客户端IP地址和端口号
            addrlen:存放地址信息的结构体的大小
    返回值
        成功,返回已经建立连接的新的newfd
        出错,返回-1

5.客户端连接函数connect

复制代码
5.客户端连接函数connect()
int connect (int sockfd, struct sockaddr * serv_addr, int addrlen)
    参数:
        sockfd:通过socket()函数拿到的fd
        addr:struct sockaddr的结构体变量地址
        addrlen:地址长度
    返回值:
        成功,返回0
        失败,返回-1

二.编程

socket函数编写

用将socket函数的返回值给fd,通过fd是否小于零来判断是否成功

bland函数

第一个参数是socket()拿到的fd

sockaddr_in 结构体

第二个参数是结构体,需先定义 sockaddr_in 结构体

定义之后在填充之前还需要将结构体清零 ,用bzero函数清零

清零之后去初始化结构体

man 2 bind 往下滑找到 socket,它是在man 7 ip,去man 7 ip 中找到我们填充的sockaddr_in结构体,发现它初始化用的是AF_INET

初始化之后去写结构体的第二个参数sin_port的ip地址,可以去宏定义,5000以前是内核相关的,为了防止冲突写5001

SERV_IP_ADDR通过ifconfig查看

sin_port用的是本地字节,需要转换成网络字节,用u_short htons(u_short short)主机字节序到网络字节序

结构体第三个参数

sin_addr 又指向s_addr,也是要转换成网络节字

sockaddr_in结构体也可以使用另一种方式初始化与配置

将sockaddr_in结构体转化成sockaddr结构体

bind函数成功返回0,失败返回-1

去判断是否成功

listen函数

accept函数

TCP服务器读取内容

接着上一章TCP学习所写的代码写 read函数代码

read成功返回字节数,失败返回-1

在do-while循环里不断读取accept函数接收到的数据,当小于-1就退出

后两个if作用是判断是否还在接收字节,没有就退出

第三个if 是客户端是否主动要退出如果是就退出

TCP客户端

三.代码优化

优化一

sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR)可以换成sin.sin_addr.s_addr =INADDR_ANY

就可以自动获取服务器地址,不用自己去写入

优化二

accept优化,通过accept获取客户端信息

之前accept的第二第三个参数都是不使用,现在使用

复制代码
4.accept()函数
阻塞等待客户端连接请求
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数
    sockfd:经过前面socket()创建并通过bind(),listen()设置过的fd
    addr:指向存放地址信息的结构体的首地址
    获取客户端IP地址和端口号
    addrlen:存放地址信息的结构体的大小
返回值
    成功,返回返回已经建立连接的新的newfd
    出错,返回-1

addr:指向存放地址信息的结构体的首地址,这个结构体是

structsockaddr_in { 基于Internel通信结构体

as_family_t sin_family;

in_port_t sin_port;

struct in_addr sin_addr;

sin_zero , //填充字节,需清零

}

两个机子通信是过程:本地字节序--->网络字节序--->本地字节序

这就需要用一些转换函数

我们是要获取IP地址,然后将其从本地转成网络/网络转本地所以使用下面这个函数

复制代码
inet_ntop(int af,const void *src,char *dst,socklen_t size)
    把ipv4和ipv6的网络字节序变成本地的字符串形式的IP地址

参数
    1.af:地址协议族(AF_INET或AF_INET6)
    2.src:是一个指针(32)
    3.dst:输出结果为32位点分形式的IP地址
    4.size:长度

成功返回非零,失败返回零

网络到主机,两个字节用ntonhs

IP地址一般都是16字节xxxx.xxxx.xxxx xxx 字符串加一个结束符所以16字节

10.0.2.15是ip地址,58220是端口号

相关推荐
今年下半年2 小时前
记录一次IBMS物联网项目的架构设计及技术栈应用
物联网·websocket·网络协议·tcp/ip·微服务·udp·kafka
雯宝4 小时前
mnt_init 函数
linux
UIU1144 小时前
递归算法与汉诺塔
c++·学习·算法·c#·递归
律宏阔10 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔10 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴10 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千10 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei3710 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei3710 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei3710 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化