Unix网络编程小结——简单的服务器实现

1.前言

要实现一个简易的服务器,需要几个步骤来与客户端建立连接,并接收客户端的数据进行处理。
Unix网络编程小结——简单的服务器实现_第1张图片
上图是实现TCP客户/服务器程序需要使用到的基本套接字函数。
本篇以实现服务器端为主。

2.socket()

首先要调用socket函数得到一个监听套接字文件描述符作为起始,用于后续使用bind()和listen()
使用时需要包含头文件“#include ”。
server.cpp

#include 
int main(int argc, char* argv[]) {
	int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
	... 
}

原函数为 socket(int domain, int type, int protocal);
第一个参数domain表示你所使用的协议族,常用:

  • AF_INET : IPv4协议族
  • AF_INET6 : IPv6协议族
  • AF_LOCAL, AF_UNIX: 本地交互

第二个参数type指定套接字类型,或者说是一种传输方式,常用:

  • SOCK_STREAM: 字节流套接字
  • SOCK_DGRAM:数据包套接字

第三个参数protocal设为某个协议的常值,或者设为0,通常设为0,它表示使用系统默认的前两个参数组合情况下使用的协议。这里AF_INET和SOCK_STREAM参数组合默认使用TCP协议。

3.bind()

通过socket()获得监听套接字文件描述符后,可以通过bind()来绑定协议地址给服务器。
server.cpp:

#include 
#include 
#define PORT 80
int main(int argc, char* argv[]) {
	int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
	
	struct sockaddr_in address;
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = htonl(INADDR_ANY);
	address.sin_port = htons(PORT); 
	bind(listen_fd, (struct sockaddr*)&address, sizeof(address));
		...
}

原函数为:bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
第一个参数sockfd就是前面获取的套接字文件描述符,
第二个参数是一个指向地址结构的指针,需要将结构体内的几个参数进行初始化后使用,这里牵扯到了一个网络字节序的概念。
一般的操作系统都是用小端字节序(little endian),而网络中数据采用网络字节序也就是大端字节序(big endian),所以需要使用转化函数htonl()、htons()来实现主机字节序和网络字节序之间的转换。
htonl : host to network long
htons: host to network short
第三个参数是地址结构的长度。

4.listen()

绑定完后需要做的就是listen(),使用它来进行监听客户端的行为,并使得套接字的状态从CLOSED转换为LISTEN。
server.cpp:

#include 
#include 
#define PORT 80
int main(int argc, char* argv[]) {
	int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
	
	struct sockaddr_in address;
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = htonl(INADDR_ANY);
	address.sin_port = htons(PORT); 
	bind(listen_fd, (struct sockaddr*)&address, sizeof(address));
	
	listen(listen_fd, 5);
	...
}

原函数是:listen(int sockfd, int backlog);
第二参数backlog表示未完成连接队列 + 已完成连接队列的大小之和,一般设定为5,但是在Linux系统下实际值要乘一个称为模糊因子的数1.5,所以实际为8。
Unix网络编程小结——简单的服务器实现_第2张图片

5.accept()

服务器调用accept()用于从已完成连接队列头部返回一个已完成连接文件名描述符(connect socket fd)

#include 
#include 
#define PORT 80
int main(int argc, char* argv[]) {
	int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
	
	struct sockaddr_in address;
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = htonl(INADDR_ANY);
	address.sin_port = htons(PORT); 
	bind(listen_fd, (struct sockaddr*)&address, sizeof(address));
	
	listen(listen_fd, 5);
	struct sockaddr_in client_addr;
	socklen_t client_addrlength = sizeof(client_addr);
	int connfd = accept(listenfd, (struct sockaddr*)&client_addr, &client_addrlength);
	...
	close(listen_fd);
	close(connfd);
}

原函数为int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
第一个参数不用说了,第二个参数用于接收指向客户端的地址结构的指针,第三个参数是其大小。
因此需要创建一个客户端地址结构变量用于接收。

你可能感兴趣的:(笔记,服务器,网络,unix)