1、TCP 套接字通信工作流程
一.面向连接的套接字Socket通信工作流程
为了实现服务器与客户机的通信,服务器和客户机都必须建立套接字。服务器与客户机的工作原理可以用下面的过程来描述。
1.服务器先用 socket 函数来建立一个套接字,用这个套接字完成通信的监听。
2.用 bind 函数来绑定一个端口号和 IP 地址。因为本地计算机可能有多个网址和 IP,每一个 IP 和端口有多个端口。需要指定一个 IP 和端口进行监听。
3.服务器调用 listen 函数,使服务器的这个端口和 IP 处于监听状态,等待客户机的连接。
4.客户机用 socket 函数建立一个套接字,设定远程 IP 和端口。
5.客户机调用 connect 函数连接远程计算机指定的端口。
6.服务器用 accept 函数来接受远程计算机的连接,建立起与客户机之间的通信。
7.建立连接以后,客户机用 write 函数向 socket 中写入数据。也可以用 read 函数读取服务器发送来的数据。
8.服务器用 read 函数读取客户机发送来的数据,也可以用 write 函数来发送数据。
9.完成通信以后,用 close 函数关闭 socket 连接。
2、创建套接字
创建套接字的系统调用为socket,它的功能是生成一个套接字的描述符
#include<sys/types.h>
#include<sys/socket.h>
int socket(int family,int type,int protocol);
若成功则返回套接字的描述符,否则返回-1
参数family指明协议族,取值如 PF_UNIX(UNIX 协议族)、PF_INET(IPv4协议)、PF_INET6(IPv6协议)、
AF_ROUTE(路由套接字)等;
type指明通信字节流类型,其取值如 SOCK_STREAM(TCP方式)、SOCK_DGRAM(UDP方式)、SOCK_RAW(原始套接字)、SOCK_PACKET(支持数据链路访问)。一般来说,参数protocol可设置为0,除非用在原始套接字上。
3、绑定端口
用socket函数创建一个套接字后,需要使用bind函数在这个套接字上绑定一个指定的端口号和IP地址。
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr *my_addr ,socklen_len addrlen);
成功返回0,否则返回-1;
参数中sockfd表示已建立的socket 编号,my_addr 是一个指向sockaddr结构体类型的指针,参数addrlen表示my_addr 结构的长度。
4、等待监听函数
listen函数用于实现服务器的监听等待功能
#include<sys/socket.h>
Int listen(int sockfd,int backlog);
成功返回0,否则返回-1
参数sockfd表示已建立的套接字,backlog表示能同时处理的最大连接请求数目
5、接受连接函数
服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求。接受连接请求的函数是accept函数
#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socketlen_t *addrlen);
若成功则返回 新的套接字的描述符,否则返回-1;
参数sockfd表示处于监听状态的socket,addr是一个sockaddr结构体类型的指针,系统会把远程主机的信息保存到这个指针所指向的结构体中,addrlen 表示sockaddr 的内存长度。
当accept函数 接受一个连接时,会返回一个新的socket标识符,以后的数据传输与读取就是通过这个新的socket编号来处理,原来参数中的socket也可以继续使用。接受连接以后,远程主机的地址和端口信息将会保存到addr所指的结构体内。如果处理失败,返回值为-1;
6、请求连接函数
connect函数完成请求与服务器建立TCP通信连接的功能。
#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr *serv_addr,int addrlen);
成功返回0,否则返回-1;
connect函数会将本地的socket 连接到serv_addr 所指向的服务器IP 与端口号上去。
7、数据发送函数
#include<sys/types.h>
#include<sys/socket.h>
int send(int sockfd,const void *msg,int len,unsigned int flads);
成功返回已发送的字节数 ,否则返回-1;
参数sockfd表示已经建立的socket,msg是需要发送数据的指针,len表示需要发送数据的长度,参数flags一般设置为0.
8、数据接收函数
函数recv可以接受远程主机发送来的数据,并把这些数据保存到一个数组中。
#include<sys/types.h>
#include<sys/socket.h>
int recv(int sockfd,void *buf,int len,unsigned int flags);
成功则返回接收到的字符数,否则返回-1
参数sockfd表示已经建立的socket ,buf是一个指针,指向一个数组,接收到的数据会保存到这个数组中,len表示数组的长度,flags一般设置为0.
9、write和read函数
在网络编程中,当socket建立连接后,向这个socket中写入数据即表示向远程主机传送数据,从socket 中读取数据则相当于接收远程住居传送过来的数据。所以,write和read函数也可以用于套接字的编程中,他们的作用分别与read和recv函数类似。
#include<unistd.h>
ssize_t write (int fd,const void*buf,size_t count);
ssize_t read (int fd, void*buf,size_t count);
成功则返回已写入或已读取的字节数,若出错则返回-1;
在参数列表中,fd表示已经建立的socket ,buf是指向一段内存的指针,count表示buf指向内存的长度。read函数读取字节时,会把读取的内容保存到buf所指向的内存中,然后返回读取到的字节的个数。使用write函数传输数据时,会把buf指针指向的内存中的数据发送到socket连接的远程主机,然后返回实际发送的字节个数。