linux 网络编程(2) --- socket编程
socket编程
套接字的概念
socket
在通信过程中,套接字一定是成对出现的
一个文件描述符指向一个套接字,该套接字内部借助两个缓冲区实现,一个用于收,一个用于发
预备知识
网络字节序
小端法(pc本地存储):高位存高地址, 低位存低地址
大端法(网络存储):高位存低地址, 低位存高地址
1 | htonl 本地->网络(ip) |
IP地址转换函数
inet_pton函数
1 | 作用:将本地的点分十进制的ip字符串转换为一个网络字节序 本地字节序(string IP)->网络字节序 |
inet_ntop函数
1 | 作用:将网络字节序转换为本地的点分十进制的ip 网络字节序->本地字节序(string IP) |
sockaddr数据结构
- 要定义
sockaddr_in
的数据结构 - 使用时要强制转换
sockaddr_in 数据结构
1 | struct sockaddr_in { |
用法
1 | struct sockaddr_in addr; |
读与写
使用系统调用来读取数据与发送数据
注意
使用read
函数的时候
1 | 返回值: |
网络套接字函数
函数 | 作用 |
---|---|
socket() |
创建套接字 |
bind() |
绑定IP + port |
listen() |
设置同时监听上限 |
accept() |
阻塞监听客户端建立连接 |
connect() |
与目标建立连接 |
socket函数
1 | 作用:创建一个套接字 |
bind函数
1 | 作用:给socket绑定一个地址结构(ip + port) |
listen函数
1 | 作用:设置可以同时进行3次握手的客户端(同时与服务器建立的上限数) |
accpet函数
1 | 作用:阻塞等待客户端建立连接, 成功的话, 返回一个与客户端成功连接的socket文件描述符 |
1 | socklen_t client_addr_len = sizeof(addr); |
connect函数
1 | 作用:使用现有的socket与服务器建立连接 |
如果不是用bind绑定客户端地址结构,则采用“隐式绑定”
错误处理函数封装
- 将系统调用封装成自己的函数
- 完成系统调用的工作 2. 检查返回值,查看函数是否正确工作
- 命名规则:保持与系统调用相同的命名规则, 可以将首字母大写
实现目标:
- 功能一样
- 不用进行错误判断
多进程并发服务器
设计思路
Socket() 创建监听套接字
Bind() 绑定地址结构
Listen()
while(1) {
cfd = Accpet(); 接受客户端连接请求
pid = fork();if (pid == 0) { 子进程: read() – 处理 — write()
close(lfd) 关闭用于建立连接的套接字lfd
while(1){ … } 处理
} else if (pid > 0) { 父进程
close(cfd) 关闭用于与客户端通信的套接字
{…} 用于回收子进程的函数
continue;
}
}子进程:
1
2
3
4close(lfd)
read()
{ ... } 一系列动作
write()父进程:
1
2注册信号捕捉函数: SIGCHLD
在回调函数中,完成子进程回收 while(waitpid())
多线程并发服务器
设计思路
Socket() 创建监听套接字
Bind() 绑定地址结构
Listen()
while(1) {
cfd = Accept(lfd);
pthread_create(&tid, NULL, tfn, NULL);
pthread_detach(tid) 不获取线程的值// pthread_join(tid, void **) 获取线程的值, 为了防止线程阻塞, 可以新开一个线程来回收
子线程:
void *tfn(void *arg) {
close(lfd);
read(cfd)
功能
write(cfd)
}
1 |
|
端口复用
- 即创建端口号相同, 但是ip地址不同的socket(一般用在断开了连接,但是没有完全断开的情况),比如快速重启服务器
- 在
socket()
和bind()
之间插入代码
1 | int opt = 0 / 1; // 0为不复用(默认), 1为复用 |