socket-单进程阻塞io

一。创建 socket

首先引入头文件

// 提供socket函数及数据结构
#include <sys/socket.h>

然后创建 socket 和 返回的文件描述符 lfd

int lfd;
lfd = socket(AF_INET,SOCK_STREAM,0);
声明为
int     socket(int, int, int);

第一个参数为地址族协议类型,有

AF_INET   : ipv4
AF_INET6  : ipv6
AF_UNIX   : 本地协议,用在同一台机器上传输

第二个参数为传输协议,有

SOCK_STREAM : 按顺序,可靠,完整的流连接,默认 tcp
SOCK_DGRAM  : 无连接,固定长度,不可靠的数据报,默认 udp
还有其他的,比如 ping 命令 所使用的 icmp 协议 等

第三个参数为使用哪些传输协议,0 表示使用默认协议

返回值为一个文件描述符,错误则返回 -1 

二。绑定网络地址和端口 bind

首先引入头文件

// 定义数据结构sockaddr_in
#include <netinet/in.h>

创建 网络地址 和 端口 的输入参数

/*
 * Socket address, internet style.
 */
struct sockaddr_in {
    __uint8_t       sin_len;
    sa_family_t     sin_family;    地址族协议类型,和 socket 第一个参数一样
    in_port_t       sin_port;      端口号
    struct  in_addr sin_addr;      ip 地址,需要用到下面的结构体,即 sin_addr.s_addr
    char            sin_zero[8];
};
/*
 * Internet address (a structure for historical reasons)
 */
struct in_addr {
    in_addr_t s_addr;
};

初始化上面结构体的输入参数

struct sockaddr_in sock_server;
sock_server.sin_family      = AF_INET;
sock_server.sin_addr.s_addr = htonl(INADDR_ANY);    
sock_server.sin_port
= htons(6666);
htonl 将本地数字IP 字节序 转换为 网络字节序,INADDR_ANY 为有效的本地IP,即 127.0.0.1 和对外的一个IP
htons 将本地数字端口 字节序转换成 网络字节序,网络一般都是采用大端传输

对第一步 socket 所返回的 lfd 文件描述符进行绑定 地址 和 端口

int ret;
ret = bind(lfd, (struct sockaddr *)&sock_server, sizeof(sock_server));

声明为

int     bind(int, const struct sockaddr *, socklen_t)

第一个参数为需要绑定的文件描述符

第二个参数为IP和端口的数据结构,需要向上转换为 sockaddr 指针,数据结构为

/*
 * [XSI] Structure used by kernel to store most addresses.
 */
struct sockaddr {
    __uint8_t       sa_len;         /* total length */
    sa_family_t     sa_family;      /* [XSI] address family */
    char            sa_data[14];    /* [XSI] addr value (actually larger) */
};

  sockaddr 细化后 就为 sockaddr_in ,sockaddr * 是个通用指针,这样可以接受多种协议的结构体,而每种协议长度不同,所以要传第三个参数

第三个参数为第二个参数结构体的长度
返回值为 0表示成功,-1 表示失败
 
三。监听绑定后的文件描述符 listen
 ret = listen(lfd, 5);

声明为

int     listen(int, int)

第一个参数为需要监听的文件描述符

第二个参数为监听的最大连接数,这个是指 三次握手开始 到 三次握手结束 的这段时间的最大连接数,不是通道建立成功后的连接数

成功返回 0 ,失败返回 -1

四,接收成功建立通道的连接 accept

int cfd;
cfd = accept(lfd, (struct sockaddr *)&sock_server, &client_len);

声明为

int     accept(int, struct sockaddr * __restrict, socklen_t * __restrict)

第一个参数为监听的文件描述符

第二个参数和 bind 的第二个参数一样,但这个参数是作为传出参数,记录了成连接的 IP 和 端口

第三个参数为传入传出参数,传人时为 (第二个参数) 通用结构体的大小,传出为 连接成功后 具体结构体 (第二个参数) 的大小 

失败返回 -1 ,成功 返回和客户端连接成功新的文件描述符

五。对连接成功的文件描述符进行读写

char buf[2048];
int n;
n = read(cfd, buf, sizeof(buf));
write(cfd, buf, n);

六。连接测试

编译运行服务端,然后连接测试,使用 net connect 命令

 客户端写什么,服务端就回复什么

代码地址:https://gitee.com/GH16/network

7. 参考

https://www.bilibili.com/video/BV1M4411j73S?p=21

原文地址:https://www.cnblogs.com/GH-123/p/12873578.html