一、TCP网络编程
网络编程模型:
C/S模型:客户端服务器模型
优点:
客户端可以缓存一些数据,使用时直接在本地读取,无需每次重新下载;
由于客户端和服务器都是自己开发的,可以自定义协议
缺点:代码开发量大
B/S模型:浏览器服务器模型
(一)流程
服务器流程:
创建套接字–socket()
填充服务器的网络信息结构体
将套接字与服务器网络信息结构体绑定–bind()
将套接字设置成被动监听状态–listen()
阻塞客户端连接–accept()
收发数据–write()/read()
关闭套接字–close()
客户端流程:
创建套接字–socket()
填充服务器的网络信息结构体
与服务器建立连接–connect()
收发数据–write()/read()
关闭套接字–close()
(二)相关函数
1. socket
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:
创建套接字
参数:
domain:通信域
AF_UNIX AF_LOCAL 本地通信使用
AF_INET IPV4使用
AF_INET6 IPV6使用
AF_PACKET 原始套接字使用
type:套接字类型
SOCK_STREAM TCP使用
SOCK_DGRAM UDP使用
protocol:附加协议 传0 表示没有附加协议
返回值:
成功 套接字(文件描述符)
失败 -1 重置错误码
2. bind
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:
将套接字与服务器网络信息结构体绑定
参数:
sockfd:套接字
addr:网络信息结构体的首地址
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
//上面的结构体只是用来强转 防止编译警告的
//实际使用的是下面的结构体
struct sockaddr_in {
sa_family_t sin_family; /* AF_INET */
in_port_t sin_port; /* 网络字节序的端口号 */
struct in_addr sin_addr; /* 网络地址 */
};
struct in_addr {
uint32_t s_addr; /* 网络字节序无符号4字节整型的IP地址 */
};
addrlen:addr的长度
返回值:
成功 0
失败 -1 重置错误码
3. listen
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:
将套接字设置成被动监听状态
参数:
sockfd:套接字
backlog:指定半连接队列的最大长度
一般传 5 10 等都可以 不是 0 就行
//因为我们实际开发时 一般都是并发服务器 基本能维持半连接队列中没有元素
返回值:
成功 0
失败 -1 重置错误码
4. accept
阻塞等待客户端连接
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:
提取半连接队列中的第一个链接,会返回一个新的套接字专门用于和该客户端通信
如果半连接队列中没有连接请求 accept函数会阻塞
参数:
sockfd:套接字 必须是已经绑定了本地地址 且置成被动监听状态的
addr:用于保存客户端的网络信息结构体的缓冲区的首地址 不关心可以传NULL
addrlen:addr的大小,不关心也可以传NULL
返回值:
成功 新的套接字用于收发数据
失败 -1 重置错误码
5. connect
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:
与服务器建立连接
参数:
sockfd:套接字
addr:要连接的服务器的网络信息结构体的首地址
addrlen:addr的长度
返回值:
成功 0
失败 -1 重置错误码
一、收发函数
(一)send函数
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:
向套接字上发送一条消息
参数:
sockfd:套接字
buf:要发送的数据的首地址
len:想要发送的字节数
flags:标志位 0 阻塞 MSG_DONTWAIT 非阻塞
返回值:
成功 实际发送的字节数
失败 -1 重置错误码
如果对方关闭了套接字或者断开连接,第一次send没有任何现象
但是第二次send会产生一个SIGPIPE
等价关系:
write(sockfd, buff, sizeof(buff));
send(sockfd, buff, sizeof(buff), 0);
sendto(sockfd, buff, sizeof(buff), 0, NULL, 0);
(二)recv函数
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:
在套接字上接收一条消息
参数:
sockfd:套接字
buf:用来存放接收到的数据的缓冲区的首地址
len:想要接收的字节数
flags:标志位 0 阻塞 MSG_DONTWAIT 非阻塞
返回值:
成功 实际接收的字节数
失败 -1 重置错误码
TCP的套接字,如果对方断开连接或者关闭套接字,recv会返回0
等价关系:
read(sockfd, buff, sizeof(buff));
recv(sockfd, buff, sizeof(buff), 0);
recvfrom(sockfd, buff, sizeof(buff), 0, NULL, NULL);
二、C/S客户端服务器代码
三、TCP粘包问题
服务器程序中禁止使用sleep函数