Server 端流程
- socket 创建
int socket(int domain, int type, int protocol);
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
- 网络地址信息初始化
struct sockaddr_in serv_addr;
char * serv_ip = "211.217.6.7";
char * serv_port = "9190";
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(serv_ip);
serv_addr.sin_port = htons(atoi(serv_port));
//serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//serv_addr.sin_port = htons(atoi(argv[1]));
这里 IP 地址是写死的,实际可以从参数传入,或者使用 INADDR_ANY,可自动获取服务端 IP 地址。
3. 向套接字分配网络地址
把初始化的地址信息分配给套接字,由bind
函数完成
int bind(int sockfd, const struct sockaddr * myaddr, socklen_t addrlen)
sockfd: 要分配地址信息的套接字
myaddr:存有地址信息的结构体变量地址值
addrlen:第二个结构体变量的长度
如果成功,将 myaddr 的地址信息分配给 sockfd
int serv_sock;
struct sockaddr_in serv_addr;
//初始化 server 的套接字
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if (serv_sock == -1) {
error_handling("socket() error");
}
//初始化 server 的网络地址信息
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
//绑定网络地址信息到 server 的套接字
if (bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
error_handling("bind() error");
}
- 进入等待连接请求状态
调用
listen
进入等待连接请求状态,只有调用后,客户端才能进入可发出连接请求的状态(调用connect
函数),如果提前,会报错。
/**
sock: 希望进入连接请求状态的套接字文件描述符
backlog:连接请求等待队列的长度,若为5,表示最多使5个连接请求进入队列
*/
int listen(int sock, int backlog) __DARWIN_ALIAS(listen);
int serv_sock;
listen(serv_sock, 5);
服务器端进入等待连接请求状态是指,客户端请求连接时,受理连接前使请求一直处于等待状态。
5. 受理客户端连接请求
受理请求意味着进入可收发数据的状态。
/**
sock: 服务器端套接字的文件描述符
addr: 保存发起连接请求的客户端地址信息的变量地址值,调用函数后向传递来的地址变量参数填充客户端地址信息
addrlen: 第二个参数addr结构体的长度,但是存有长度的变量地址。
*/
int accept(int sock, struct sockaddr * addr, socklen_t * addrlen);
struct sockaddr_in clnt_addr;
clnt_addr_size = sizeof(clnt_addr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
accept
调用成功时,内部产生用于数据 I/O 的套接字自动与客户端套接字连接。接下来的数据传输接受通过该 socket 进行。
客户端流程
- 创建套接字
int sock;
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- 客户端网络地址初始化
声明
sockaddr_in
结构体,初始化为要连接的服务器端的 IP 和端口
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
//两个参数分别为 server 端的 IP 和端口号.
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
- 请求连接,调用
connect
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
error_handling("connect() error");
}
Connect在以下情况会返回
- 服务端接受连接请求
- 网络异常导致请求中断
接收连接不代表服务器调用了 accept 函数,只是请求进入了等待队列,connect 返回后不立即进入数据交换阶段。
客户端的 IP 和端口在调用connect
时自动生成,IP 是客户端的主机 IP,端口随机。