学习视频链接
黑马程序员-Linux网络编程_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1iJ411S7UA?p=17
一、网络套接字(英文socket) 1.1 原理图虚线方框表示的是一个套接字
1.2 简介在通信过程中,套接字一定是成对出现的
二、网络字节序 2.1 大小端法小端法:(pc本地存储) 高位存高地址。地位存低地址。int a = 0x12345678
大端法:(网络存储) 高位存低地址。地位存高地址
计算机采用小端法
网络采用大端法
2.2 函数介绍1、函数
为使网络程序具有可移植性,使同样的 C 代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和本地字节序的转换
#include uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);
htonl ——> 本地字节序 ——> 网络字节序 (IP)
htons ——> 本地字节序 ——> 网络字节序 (port)
ntohl ——> 网络字节序 ——> 本地字节序 (IP)
ntohs ——> 网络字节序 ——> 本地字节序 (port)
2、对上面函数进行封装
192.168.1.11 本质是一个 string,把这个转换成 int 整数,再调用 htonl 转换成网络字节序的整个流程可以用下面的函数代替
#include int inet_pton(int af, const char *src, void *dst); const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
该函数支持 IPv4 和 IPv6
三、IP地址转换函数 3.1 inet_pton1、作用
本地字节序 (string IP) ——> 网路字节序
2、int inet_pton(int af, const char *src, void *dst);
(1) 传入参数
af:AF_INET、AF_INET6
src:传入,IP 地址(点分十进制)
dst:传出,转换后的网络字节序的 IP 地址
(2) 返回值:
成功:1
异常:0,说明 src 指向的不是一个有效的 ip 地址
失败:-1
3.2 inet_ntop1、作用
网络字节序 ——> 本地字节序 (string IP)
2、const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
(1) 传入参数
af:AF_INET、AF_INET6
src:网络字节序 IP 地址
dst:本地字节序 (string IP)
size:dst 的大小
(2) 返回值
成功:dst
失败:NULL
四、sockaddr 数据结构 4.1 简介strcut sockaddr 很多网络编程函数诞生早于 IPv4 协议,那时候都使用的是 sockaddr 结构体,为了向前兼容,现在 sockaddr 退化成了 (void*) 的作用,传递一个地址给函数,至于这个函数是sockaddr_in 还是 sockaddr_in6,由地址族确定,然后函数内部再强制类型转化为所需的地址类型
通过 man 7 ip 查看
4.2 新建一个sockaddrstruct sockaddr_in addr;
addr.sin_family = AF_INET/AF _INET6; // 选择 ip 类型 ipv4、ipv6
addr.sin_port = htons(9527); // 选择端口号
// 写法1:选择 ip 地址
int dst;
inet_pton(AF_INET, "192.157.22.45", (void*)&dst);
addr.sin_addr.s_addr = dst;
// 写法2:选择 ip 地址
addr.sin_addr.s_addr = htonl(INADDR_ ANY); // 取出系统中有效的任意IP地址,二进制类型(可以转换成字节序)
bind(fd, (struct sockaddr *)&addr, size);
五、socket流程和相关函数 5.1 流程图server:
| 1、socket() | 创建socket |
| 2、bind() | 绑定服务器地址结构 |
| 3、listen() | 监听上限 |
| 4、accept() | 阻塞监听客户端连接 |
| 5、read() | 读socket获取客户端数据 |
| 6、小写—>大写 | toupper() |
| 7、write(fd) | |
| 8、close() |
client:
| 1、socket() | 创建socket |
| 2、connect() | 与服务器建立连接 |
| 3、write() | 写数据到socket |
| 4、read() | 读转换后的数据 |
| 5、显示读取结果 | |
| 6、close() |
1、作用
创建一个套接字
2、#include
int socket(int domain, int type, int protocol);
(1) 参数
domain:AF_INET、AF_INET6、AF_UNIX(ip地址协议:ipv4、ipv6、本地套接字)
type:SOCK_STREAM、SOCK_DGRAM(数据传输协议:流式协议、报式协议)
protocol:0(选择报式协议套接字)
(2) 返回值
成功:新套接字所对应文件描述符
失败:-1 errno
5.3 bind函数1、作用
给 socket 绑定一个地址结构 (IP +port)
2、#include
#include
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
(1) 参数
sockfd:socket 函数返回值
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr:传入参数(struct sockaddr *)&addr
addrlen:sizeof(addr) 地址结构的大小
(2) 返回值
成功:0
失败:-1 errno
5.4 listen函数1、作用
设置同时与服务器建立连接的上限数 (同时进行 3 次握手的客户端数量)
2、int listen(int sockfd, int backlog);
(1) 参数
sockfd:socket 函数返回值
backlog:上限数值。最大值 128
(2) 返回值:
成功:0
失败:-1 errno
5.5 accept函数1、作用
阻塞等待客户端建立连接,成功的话,返回一个与客户端成功连接的 socket 文件描述符
2、int accept(int sockfd, struct sockaddr *addr,socklen_t *addrlen);
(1) 参数
sockfd:socket 函数返回值
addr:传出参数。成功与服务器建立连接的那个客户端的地址结构 (IP + port)
socklen_t clit_addr_len = sizeof(addr);
addrlen:传入传出。&clit_addr_len 入:addr 的大小。 出:客户端 addr 实际大小
(2) 返回值
成功:能与服务器进行数据通信的 socket 对应的文件描述
失败:-1 errno
5.6 connect函数1、作用
使用现有的 socket 与服务器建立连接
2、int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
(1) 参数
sockfd:socket 函数返回值
addr:传入参数。服务器的地址结构
(2) 返回值:
成功:0
失败:-1 errno
如果不适用 bind 绑定客户端地址结构,采用 "隐式绑定"
六、server的实现 6.1 建立C语言项目 6.2 代码#include6.3 测试运行#include #include #include #include #include #include #include #include #define SERV_PORT 9527 void sys_err(const char *str) { perror(str); exit(1); } int main(int argc, char *argv[]) { int lfd = 0, cfd = 0; int ret; char buf[BUFSIZ]; // BUFSIZ 系统自带的宏,大小 4096 struct sockaddr_in serv_addr, clit_addr; socklen_t clit_addr_len; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // AF_INET: ipv4 // SOCK_STREAM: 提供面向连接的稳定数据传输,即TCP协议 lfd = socket(AF_INET, SOCK_STREAM, 0); if (lfd == -1) { sys_err("socket error"); } // 给socket绑定地址 bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); // 最大连接数 listen(lfd, 128); clit_addr_len = sizeof(clit_addr); cfd = accept(lfd, (struct sockaddr *)&clit_addr, &clit_addr_len); if (cfd == -1) { sys_err("accept error"); } while (1) { ret = read(cfd, buf, sizeof(buf)); write(STDOUT_FILENO, buf, ret); for (int i = 0; i < ret; i++) { buf[i] = toupper(buf[i]); } write(cfd, buf, ret); // 写回到客户端 } close(lfd); close(cfd); return 0; }



