文章目录
网络编程模型
上述模型中,每个概念的含义如下:
- Socket: 建立 Socket。创建一个 Socket 描述符;
- bind: 用于服务器端,把 Socket 的描述符与协议类型、本地IP、本地端口等信息进行绑定;
- listen: 用于服务器端,主要用于监听来自外部的连接,可以设置最大允许外部连接的数量;
- connect: 用于客户端,向服务器发起连接;
- accept: 用于服务器,接收客户端的连接请求。此时会返回一个与该客户端对应的新的
Socket
描述符,与该客户端的通信使用此Socket
的描述符; - send/sendto: 通过 Socket 的发送数据;
- recv/recvfrom: 通过 Socket 的接收数据;
- 关闭 Socket 的。
主要函数详解
建立套接字 socket()
常用实例为:
/*创建IPv4 TCP Socket*/
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sfd) {
perror("socket");
exit(-1);
}
绑定地址 bind()
bind()
绑定函数的作用:主要用于把 socket 绑定到主机地址上。
常用实例:
// 初始化主机信息
struct sockaddr_in my_addr;
memset(&my_addr, 0, sizeof(struct sockaddr_in)); // 清空主机地址
// bzero(&my_addr, sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET; // 定义协议族:IPV4
my_addr.sin_port = htons(8888); // 定义端口号:把主机字节序转化为网络字节序
my_addr.sin_addr.s_addr = inet_addr("192.168.0.101") ; // 将ip 点分十进制转化为二进制,并转化为网络字节序
// 把 socket 绑定到主机地址上
if(bind(sfd, (struct sockaddr *)&my_addr), sizeof(struct sockaddr_in) == -1) {
perror("bind");
close(sfd);
exit(-1);
}
注意:
- 如果设置:
my_addr.sin_port = 0
,函数会自动选择一个未占用的端口来使用; - 如果设置:
my_addr.sin_addr.s_addr = INADDR_ANY
,系统会自动填入本机 IP 地址;
监听 listen()
listen
函数:主要作用是用于监听端口,检测是否有连接请求信号。
常用实例:
// 监听 sfd,设置最大的连接数为 10
if(listen(sfd, 10) == -1) {
perror("listen");
close(sfd);
exit(-1);
}
接受请求 accept()
accept
函数用于接受远程客户端的连接请求,建立与客户机之间的通信连接。假如没有客户端发出请求,服务器默认将在此处阻塞。
注意: 服务器处于监听状态时,如果某时刻获得了客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统有空闲时再处理客户机的连接请求。
常用实例:
struct sockaddr_in clientaddr; // 用于存储客户端主机信息
bzero(&clientaddr, sizeof(struct sockaddr)); // 情况结构 clientaddr
int addrlen = sizeof(struct sockaddr_in); // 计算结构 clientaddr 的长度
// 接收客户端的请求,并把客户端 socket 数据存入结构 clientaddr 中。
int new_fd = accept(sfd, (struct sockaddr *)&clientaddr, &addrlen); // 返回客户端 Socket
if (-1 == new_fd) {
perror("accept");
close(sfd);
exit(-1);
}
print("%s %d success connect\n", inet_ntoa(clientaddr.sin_addr), ntoths(clientaddr.sin_port));
连接服务器 connect()
connect
函数:将客户端 Socket 连接至服务器。主要用在 TCP 客户端。
常用实例:
// 存储服务器IP、端口信息
struct sockaddr_in saddr; // 用于存储服务器socket数据
bzero(&seraddr, sizeof(struct sockaddr_in)); // 清空结构 saddr
seraddr.sin_family = AF_INET; // IPv4 网络
seraddr.sin_port = htons(8888); // 把端口设置为网络字节序
seraddr.sin_addr.s_addr = inet_addr("192.168.0.101"); // 把服务器IP设置为网络字节序的二进制
// 本地socket连接服务器的socket.
if(connect(cfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr)) == -1) {
perror("connect");
close("sfd");
exit(-1);
}
发送数据 send()/sendto()
send
函数:主要用在 TCP 协议中,其作用是向指定的服务器/客户端发送数据;
sendto()
函数:主要用在 UDP 协议中,其作用是向指定的服务器/客户端发送数据;
常用实例:
// 在 TCP 中,服务器(客户端)向客户端(服务器)发送字符串 "hello"。
if(send(client_fd, "hello", 6, 0) == -1) {
perror("send");
close(client_fd);
exit(-1);
}
// 在 UDP 中,服务器(或客户端)向客户端(或服务器)发送字符串 "hello",
// 在UDP中,需要指明对方的地址信息(socket信息)
if(sendto(socket_fd, "hello", 6, 0, (struct sockaddr *)&toaddr, sizeof(struct sockaddr) == -1) {
perror("sendto");
close(client_fd);
exit(-1);
}
接收数据 recv()/recvfrom()
recv()
函数:主要用在 TCP 中,其作用是接收来自服务器/客户端的数据;
recvfrom()
函数:主要用在 UDP 中,其作用是接收来自服务器/客户端的数据;
常用实例:
// 在 TCP 中,recv 通过已经建立联系的socket client_fd 进行联系
// 服务器(客户端)向客户端(服务器)发送数据
char buf[512] = {0};
if(recv(client_fd, buf, sizeof(buf), 0) == -1) {
perror("recv");
close(client_fd);
exit(-1);
}
puts(buf);
// 在 UCP 中,服务器(客户端)向客户端(或服务器)发送数据;
// 需要指明对方的socket信息
char buf[512] = {0};
if(recvfrom(socket_fd, buf, sizeof(buf), 0, (struct sockaddr *)&fromaddr, sizeof(struct sockaddr)) == -1) {
perror("recvfrom");
close(socket_fd);
exit(-1);
}
puts(buf);