socket编程

本文详细介绍了套接字概念、网络字节序与主机序转换、IP地址转换、structsockaddr_in结构、socket函数到C/S模式流程图,涵盖TCP/IP协议与socketAPI的实战应用。

1 套接字概念

Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。

既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。

在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。

套接字通信原理如下图所示:
在这里插入图片描述
**在网络通信中,套接字一定是成对出现的。**一端的发送缓冲区对应对端的接收缓冲区。我们使用同一个文件描述符索发送缓冲区和接收缓冲区。

TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API。

在这里插入图片描述

2 网络字节序与主机序转换函数

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);


3 IP地址转换函数

早期:只能处理IPv4的ip地址

/*
 * typedef uint32_t in_addr_t;
 *
 *            struct in_addr {
 *                           in_addr_t s_addr;
 *            };
 */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*
 * function: 将点分十进制字符串格式转换为二进制形式(按网络字节顺序),并将其存储在inp指向的结构中
 *
 * function arguments:
 *  argv1: 传入参数,点分十进制IP地址
 *  argv2: 传出参数,二进制形式(按网络字节顺序)IP地址
 *
 * return value:
 *  success: 返回非0
 *  faild: 返回0,传入的IP地址无效
 */
int inet_aton(const char *cp, struct in_addr *inp);

/*
 * function: 将一个点分十进制的IP转换成一个长整数型数
 *
 * function arguments:
 *  argv1: 传入参数,点分十进制IP地址
 *
 * return value:
 *  IP转换成的一个长整数型数
 */
in_addr_t inet_addr(const char *cp);

/*
 * function: 将网络地址转换成点分十进制字符串格式
 *
 * function arguments:
 *  argv1: 二进制形式(按网络字节顺序)IP地址
 *
 * return value:
 *  IP地址在转换的十进制字符串格式
 */
char *inet_ntoa(struct in_addr in);

现在:支持IPv4和IPv6

#include <arpa/inet.h>
/*
 * function: 将点分十进制字符串格式转换为二进制形式IP地址(按网络字节顺序)
 *
 * function arguments:
 *  argv1: AF_INET/AF_INET6
 *  argv2: 传入参数,点分十进制的IP地址
 *  argv3: 传出参数, 二进制形式IP地址(按网络字节顺序)
 *
 * return value:
 *  success: 返回1
 *  abnormal: 返回0,IP地址无效
 *  faild: 返回-1,设置errno
 */
int inet_pton(int af, const char *src, void *dst);

/*
 * function: 将二进制形式IP地址(按网络字节顺序)转换为点分十进制字符串格式
 *
 * function arguments:          
 *  argv1: AF_INET/AF_INET6
 *  argv2: 传入参数, 二进制形式IP地址(按网络字节顺序)
 *  argv3: 传出参数,点分十进制的IP地址
 *  argv4: dst的大小
 *
 * return value:
 *  success: 指向dst的指针
 *  faild: NULL
 */
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);


4 struct sockaddr_in结构体

strcut sockaddr 很多网络编程函数诞生早于IPv4协议,那时候都使用的是sockaddr结构体,为了向前兼容,现在sockaddr退化成了(void *)的作用,传递一个地址给函数,至于这个函数是sockaddr_in还是sockaddr_in6,由地址族确定,然后函数内部再强制类型转化为所需的地址类型。

可以通过 man 7 ip 参看。

在这里插入图片描述

 struct sockaddr_in {
              sa_family_t sin_family; /* 地址族: AF_INET */
              u_int16_t sin_port; /* 按网络字节次序的端口 */
              struct in_addr sin_addr; /* internet地址 */
              };

              /* Internet地址. */
              struct in_addr {
              u_int32_t s_addr; /* 按网络字节次序的地址 */
              };


5 相关函数

1 socket函数

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
/*
 * function: socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符
 *
 * function arguments:
 *  argv1:
 *      AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
 *      AF_INET6 与上面类似,不过是来用IPv6的地址
 *      AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用
 *  argv2:
 *      SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
 *      SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
 *      SOCK_SEQPACKET该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。
 *      SOCK_RAW socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
 *      SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序
 *  argv3:
 *      传0 表示使用默认协议。
 *
 * return value:
 *  success: 返回指向新创建的socket的文件描述符
 *  faild: 返回-1,并设置errno
 */
int socket(int domain, int type, int protocol);

2 bind函数

#include <sys/types.h>
#include <sys/socket.h>
/*
 * function: 为套接字sockfd指定本地地址my_addr
 *
 * function arguments:
 *  argv1: socket文件描述符
 *  argv2: 地址结构(IP+port)
 *  argv3: 地址结构的大小
 *
 * return value:
 *  success: 返回0
 *  faild: 返回-1,并设置errno
 */

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

// 如果客户端不是bind函数绑定客户端的地址结构,采用“隐式绑定”

3 listen函数

#include <sys/socket.h>

/*
 * function: 设置同时与服务器建立连接的上限次数。(同时进行三次握手的客户端数量)
 *
 * function arguments:
 *  argv1: socket文件描述符
 *  argv2: 上限数值。最大128
 *
 * return value:
 *  success: 返回0
 *  faild: 返回-1,并设置errno
 */

int listen(int s, int backlog);

4 accept函数

#include <sys/types.h>
#include <sys/socket.h>
/*
 * function: 阻塞等待客户端建立连接。成功的话,返回一个与客户端成功连接的socket文件描述符
 *
 * function arguments:
 *  argv1: socket文件描述符
 *  argv2: 传出参数,成功与服务器建立连接的那个客户端的地址结构(IP+port)
 *  argv3: 传入传出参数。入: addr的大小; 出: 客户端addr实际大小
 *
 * return value:
 *  success: 返回一个与客户端成功连接的socket文件描述符
 *  faild: 返回-1,并设置errno
 */
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);

5 connect函数

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
/*
 * function: 使用现有的socket与服务端建立连接 
 *      
 * function arguments:
 *  argv1: socket文件描述符
 *  argv2: 传入参数,服务器的地址结构(IP+port)
 *  argv3: 服务器地址结构的大小
 *
 * return value:
 *  success: 返回0
 *  faild: 返回-1,并设置errno
 */

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


6 C/S模式流程图(UDP、TCP)

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值