socket编程相关函数及简单服务器模型
1.网络字节序
-
小端法:高位存高地址,低位存低地址(PC本地存储采用)
-
大端法:高位存低地址,低位存高地址(网络存储采用)
-
函数原型:(字节序切换)
uint32_t htonl(uint32_t hostlong); //无符号长整型本地字节序转网络字节序 uint16_t htons(uint16_t hostshort); //无符号短整型本地字节序转网络字节序 uint32_t ntohl(uint32_t netlong); //无符号长整型网络字节序转本地字节序 uint16_t ntohs(uint16_t netshort); //无符号短整型网络字节序转本地字节序
2.IP地址转换
-
inet_pton函数原型:
int inet_pton(int af, const char* src, void* dst);
-
功能:本地字节序(string IP)→ 网络字节序
-
参数:
af:AF_INET、AF_INET6
src:传入的点分十进制的参数,传入参数本地IP地址
dst:传出参数,转换后的网络字节序的IP地址
-
返回值:
成功:1
失败:-1
异常:0,src指向不是一个有效的ip地址
-
函数原型:
const char* inet_ntop(int af, const void* src, char* dst, socklen_t size);
-
功能:网络字节序 → 本地字节序(string IP)
-
参数:
af:AF_INET、AF_INET6
src:网络字节序ip地址
dst:本地ip地址(string IP)
size:dst的大小
3.Socket
-
概念:通信过程中套接字是成对存在的,一个文件描述符指向一个套接字(一个套接字内部由内核借助两个缓冲区实现)。
-
数据类型及初始化操作
struct sockaddr_in addr; //新版的套接字结构体 /*struct sockaddr_in { sa_family_t sin_family; //地址结构类型 in_port_t sin_port; //端口号(网络字节序) struct in_addr sin_addr; //ip地址(网络字节序) } struct in_addr { uint32_t s_addr; }*/ //初始化各种参数 addr.sin_family = AF_INET; addr.sin_port = htons(9527); //int dst; //inet_pton(AF_INET, "192.168.1.1", (void*)&dst); addr.sin_addr.s_addr = htonl(INADDR_ANY); //取出系统中有效的任意IP地址,二进制类型 struct sockaddr add; //旧版的套接字结构体(已经淘汰)
-
socket函数
-
原型
int socket(int domain, int type, int protocol);
-
功能:创建套接字
-
参数:
domain:AF_INET、AF_INET6、AF_UNIX表示协议的类型
type:SOCK_STREAM、SOCK_DGRAM
protocol:通常为0
-
返回值:
成功:新套接字所对应的文件描述符
失败:-1,errno
-
-
bind函数
-
原型
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
-
功能:给套接字绑定一个地址结构(IP+port)
-
参数:
sockfd:套接字文件描述符
addr:传入参数,套接字的地址
addrlen:套接字地址结构长度
-
返回值:
成功:0
失败:-1,errno
-
-
listen函数
-
原型:
int listen(int sockfd, int backlog);
-
功能:设置同时与服务器建立连接的上限数量(同时进行3次握手的客户端数量)
-
参数:
sockfd:套接字文件描述符
backlog:上限数值,最大为128
-
返回值:
成功:0
失败:-1,errno
-
-
accept函数
-
原型:
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
-
功能:阻塞等待客户端建立连接,成功则返回一个与客户端成功连接的socket文件
-
参数:
sockfd:套接字文件描述符,socket函数返回值
addr:传出参数,传出成功与服务器连接的客户端的地址结构(IP+port)
addrlen:传入传出参数,入:addr的大小,出:客户端addr的实际大小
-
返回值:
成功:能与服务器进行通行的socket对应的文件描述符
失败:-1,errno
-
-
connect函数
-
原型
int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
-
功能:使用现有的socket与服务器建立连接
-
参数:
sockfd:socket的返回值,文件描述符
addr:传入参数,服务器的地址结构
addrlen:服务器地址结构的长度
-
返回值:
成功:0
失败:-1,errno
-
-
注意事项:
-
如果不用bind绑定客户端地址结构,采用“隐式绑定”
-
tcp的服务器通信流程分析:
-
socket,创建socket
-
bind,绑定服务器地址结构
-
listen,设置监听上限
-
accept,阻塞监听客户端连接
-
read,读socket获取客户端数据
-
toupper,大小写切换
-
write,向客户端发送数据
-
close,关闭套接字
-
-
tcp的客户端通信流程分析:
- socket,创建套接字
- connect,与服务器建立连接
- write,写数据到socket
- read,读转换后的数据
- close,关闭套接字
4. 多进程高并发服务器
lfd = Socket(); //创建监听套接字
Bind(); //绑定地址结构
Listen();
while(1)
{
cfd = Accept(); //接受客户端连接请求
pid = fork(); //创建子进程
if(pid == 0)
{
//子进程
close(lfd);
read(cfd);
write(cfd);
}
else if(pid > 0)
{
//父进程
close(cfd);
//注册信号捕捉函数
//回调函数中完成子进程的回收
continue;
}
}
5. 多线程并发服务器
lfd = Socket(); //创建监听套接字
Bind(); //绑定地址结构
Listen();
while(1)
{
cfd = Accept(); //接受客户端连接请求
pthread_create(); //创建子线程
pthread_detach(); //将子线程分离出去可以自动回收
//创建一个新线程用于专门回收线程
//pthread_join(tid, void**);
}
//子线程
void* tfn(void* arg)
{
close(lfd);
read(cfd);
write(cfd);
}
6.read函数注意事项
- 返回值
- 大于0:实际读到的字节数
- 等于0:已经读到末尾(相当于对端已经关闭套接字)
- -1:
- errno = EAGAIN 或 EWOULDBLOCK:设置了非阻塞模式读,数据没有到达
- errno = SINTR 慢速系统调用被中断
- errno = 其他情况异常