为了能够正常让客户端能正常连接到服务器,服务器必须遵循以下处理流程:
①、调用 socket()函数打开套接字,得到套接字描述符;
②、调用 bind()函数将套接字与 IP 地址、端口号进行绑定;
③、调用 listen()函数让服务器进程进入监听状态;
④、调用 accept()函数获取客户端的连接请求并建立连接;
⑤、调用 read/recv、 write/send 与客户端进行通信;
⑥、调用 close()关闭套接字。
0.首先调用头文件
#include <sys/types.h>
#include <sys/socket.h>
1.使用socket()函数,产生TCP套接字
int socket(int domain, int type, int protocol);
socket()函数类似于open()函数,它用于创建一个通信端点,如果成功则返回一个网络文件描述符。参数 domain 用于指定一个通信域,用来选择协议簇。其中选择AF_INET代表IPV4。参数 type 指定套接字的类型,SOCK_STREAM代表TCP协议,SOCK_DGRAM代表UDP协议。参数 protocol 通常设置为 0。
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);//打开套接字
if (0 > socket_fd) {
perror("socket error");
exit(-1);
}
2.bind()函数
bind()函数用于将一个 IP 地址或端口号与一个套接字进行绑定
struct sockaddr_in server; //服务器端和客户端IPV4地址信息
//初始化server套接字地址结构
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
//使用bind()函数,将套接字与指定的协议地址绑定
if(bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
perror("Bind() error");
exit(1);
}
3.listen()函数
listen()函数只能在服务器进程中使用,让服务器进程进入监听状态,等待客户端的连接请求, listen()函数在一般在 bind()函数之后调用,在 accept()函数之前调用,它的函数原型是:
int listen(int sockfd, int backlog);
无法在一个已经连接的套接字(即已经成功执行 connect()的套接字或由 accept()调用返回的套接字)上执行 listen()。
4.accept()函数
accept()函数为阻塞函数,会等待客户端连接的到来。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
当有客户端连接请求到达时, accept()函数与远程客户端之间建立连接, accept()函数返回一个新的套接字。这个套接字与 socket()函数返回的套接字并不同, socket()函数返回的是服务器的套接字(以服务器为例),而accept()函数返回的套接字连接到调用 connect()的客户端,服务器通过该套接字与客户端进行数据交互,譬如向客户端发送数据、或从客户端接收数据。
所以,理解 accept()函数的关键点在于它会创建一个新的套接字,其实这个新的套接字就是与执行connect()(客户端调用 connect()向服务器发起连接请求)的客户端之间建立了连接,这个套接字代表了服务器与客户端的一个连接。如果 accept()函数执行出错,将会返回-1,并会设置errno 以指示错误原因。参数 addr 是一个传出参数,参数 addr 用来返回已连接的客户端的 IP 地址与端口号等这些信息。参数addrlen 应设置为 addr 所指向的对象的字节长度,如果我们对客户端的 IP 地址与端口号这些信息不感兴趣,可以把 arrd 和 addrlen 均置为空指针 NULL。
5.recv()函数
recv()函数默认为阻塞函数
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
不论是客户端还是服务器都可以通过 revc()函数读取网络数据。参数sockfd 指定套接字描述符,参数 buf 指向了一个数据接收缓冲区,参数 len 指定了读取数据的字节大小,参数 flags 可以指定一些标志用于控制如何接收数据。
6.send()函数
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数sockfd 指定套接字描述符,参数 buf 指向了一个数据接收缓冲区,参数 len 指定了读取数据的字节大小,参数 flags 指定一些标志来改变处理传输数据的方式,默认为0。
7.close()关闭套接字
close(int sockfd);
当不再需要套接字描述符时,可调用 close()函数来关闭套接字,释放相应的资源。注意这个套接字是accept()函数返回的套接字。
8.connect()函数
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
该函数用于客户端应用程序中,客户端调用 connect()函数将套接字 sockfd 与远程服务器进行连接,参数 addr 指定了待连接的服务器的 IP 地址以及端口号等信息,参数 addrlen 指定了 addr 指向的 struct sockaddr对象的字节大小。
客户端通过 connect()函数请求与服务器建立连接,对于 TCP 连接来说,调用该函数将发生 TCP 连接的握手过程,并最终建立一个 TCP 连接,而对于 UDP 协议来说,调用这个函数只是在sockfd 中记录服务器IP 地址与端口号,而不发送任何数据。
函数调用成功则返回 0,失败返回-1,并设置 errno 以指示错误原因。