一。创建 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 * 是个通用指针,这样可以接受多种协议的结构体,而每种协议长度不同,所以要传第三个参数
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