一、网络编程基础:
1、套接字:
套接字(socket)是一种双向的通信端口。使用标注UNIX文件描述符于其他程序通信的方式。也可以看作是处于不同主机之间的两个进程的通信连接的端点。
2、套接字的作用:
当两个进程进行通信时候,一方面程序要将信息输入到套接字,一方面程序又从套接字中读取信息。当然这两个进程即可以是不同主机上的两个进程,也可以是同一主机上的俩个进程。
3、套接字的通信过程:
当位于不同主机活同一个主机上的两个进通A和B要进行通信的时候,A、B这两个进程,每个创建一个套接字,然后。然后实施绑定端口,监听端口信息,发送请求,响应请求等等一系列的措施。当然对于不同机制的通信协议,以上的具体操作是不一样的。 当两个进程连接好了以后, 两个进程对各自的套接字进行写入信息或者读出信息操作就可以进行通信了。
4、套接字的类型:
■ 流套接字(SOCK_STREAM):用于提供面向连接的、可靠的数据传输协议,只能读取TCP协议的数据。
■ 数据报套接字:(SOCK_DGRAM):提供一种无连接的服务,不能保证数据传输的可靠性,只能读取UDP协议的数据。
■ 原始套接字:(SOCK_RAW):可以读取内核没有处理的IP数据包,访问其他协议数据;
5、套接字的相关的数据结构:
struct sockaddr{ //保存套接字的地址信息
unsigned short sa_family; //用于指定协议族
char sa_data[14]; //保存套接字的IP和端口号信息
};
struct sockadd_in{
short int sin_family; //指定协议族
unsigned short int sin_port; //套接字通信的端口号
struct in_addr sin_addr; //通信的IP地址
unsigned char sin_zero[8]; //用以填充0,保持与sockaddr同样大小 };
6、数据存储方式
■ 大端存储(big-endian):数据高字节存放在低存储单元,低字节存放在高存储单元
■ 小端存储(little-endian):数据的低字节存放在低的存储单元,高字节存放在高的存储单元
不同的的体系CPU中数据存储的方式是不同的,为了程序在不同硬件平台中的一致,并且网络编程中也要考虑到字节的顺序问题,因此在网络传输中都采用大端存储方式,因此采用小端顺序存储方式的主机在将数据发往网络的时候必须先转化为大端方式,接受到数据是在转换回来。
linux提供的字节转换顺序的函数如下:
uint32_t htonl(uint32_t hostlong); //32位主机字节顺序转化为网络字节顺序
uint16_t htons(uint16_t hostshort); //16位主机字节顺序转化为网络字节顺序
uint32_t ntohl(uint32_t netshort); //32位网络字节顺序转化诶主机字节顺序
uint16_t ntohs(uint16_t netshort); //16位网络字节顺序转化为主机字节顺序
二、面向连接的套接字通信
1、面向连接的套接字的流程:
■ 服务器端: 创建套接字(socket())==> 绑定端口号(bind()) ==> 监听该端口号(listen())
==> 接收连接请求(accept()) ==> 从套接字中读取信息(read()) ==> 向套接字中写入信息(write())
■ 客户端: 创建套接字(socket()) ==> 连接到指定的计算机端口(connect()) ==> 向套接字 中写入信息(write()) ==> 从套接字中读取信息((read())
2、具体的函数:
■ socket函数
创建一个套接字
头文件
函数原型:
int socket(int domain,int types,int protocol)
参数domain 为通信协议族,如PF_INET 为IPv6协议
参数types 指定套接字的类型 如SOCK_STREAM 提供面向连接的字节流
参数protocol用于指定套接字使用的通信协议。通常指定了通信协议族,当只有的单一的协议支持特定的套接字类型。设施将protocol设置为0;如 指定了协议族PF_INET,而此协议族只有TCP/IP协议,所以只需将protocol设为0;
函数返回一个套接字描述符
■ bind函数
将套接字绑定到指定的端口
函数原型:
int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen);
参数sockfd为创建的套接字描述符
参数mu_addr为指向sockaddr结构体的指针(该结构体中保存有端口号和IP地址)
参数addlen为sockaddr结构体的长度
函数成功返回0,失败返回-1
■ listen()函数
函数原型:
监听绑定的通信端口,实现等待的功能
int listen(int sockfd,int backlog)
参数 sockfd为套接字文件描述符
参数backlog为提出请求后在服务接受请求等待时候的等待队列中的连接数,默认为20
函数成功返回0,失败返回-1
函数只能用于套接字类型为SOCK_STREAM或者SCOK_SEQPACKET的场合
■ accept函数
处于监听状态服务器在接收到客户机的连接信息后,将其放在的呢个代队列中,当系统空闲是将接受客户机的连接请求
函数原型:
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
参数sockfd为socket函数返回的套接字描述符
参数addr为指向sockaddr结构体的指针
参数addrlen为addr参数指向的内存空间的长度
函数调用成功返回一个新的套接字文件描述符,这个新的文件描述符用于和客户机进行通信,而以前的套接字仍然用处于监听状态
■ connect函数
连接指定的服务起器端口
函数原型:
int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen);
参数sockfd为客户机创建的套接字的描述符,serv_addr为指向存放要连接的服务器的sockaddr结构体的指针
参数addr_len为serv_addr指向的内存空空间的大小
函数成功则返回0,失败则返回-1。
■ send 函数
函数原型:
ssize_t send(int s,const void *buf,size_t len,int flags);
用于发送数据到指定的套接字文件描述符。只能用于已经建立连接的socket通信中,即只能用于面向连接的通信中。
参数s为要发送数据的套接字描述符
参数len为要发送的数据的长度。
参数bug为指向存放要发送数据的指着
参数flag为具体的设置参数,如:MSG_DONTROUTE 表示不通过网关发送数据,只将数据发送到同一个子网中的计算机。
函数成功返回实际发送的数据的字节数,失败则返回-1
■ recv函数
实现从套接字中读取发送国来的消息
函数原型
ssize_t recv(int s,void *buf,size_t,int flags);
参数s为要读取信息的套接字描述符,
参数buf为指向要保存数据缓冲区的指针
参数flags用于具体的设置
参数len为要读取的信息的长度
函数调用成功返回接收的数据的长度,失败在返回-1
■ close函数
关闭套接字
函数原型
int close(int sockfd);
三、面向无连接的套接字通信:
1、面向无连接的套接字的流程
■ 服务器端:创建套接字(socket()) ==> 将socket与某端口进行绑定(bind()) ==> 读取发送过来的信息(recvfrom()) ==> 发送消息给客户端(sendto())
■ 客户端: 创建套接字(socket()) ==> 连接指定的计算机的端口 ==> 发送信息给指定的服务器(sendto()) ==> 读取发送过来的消息(recvfrom())
具体的函数:
■ recvfrom函数
用于接收通过套接字发送过来的消息
函数原型:
ssize_t recvfrom(int s,void *buf,size_tt len,int flags,struct sockaddr *from,socklen_t *fromlen)
参数s为套接字文件描述符
参数buf为指向接收到信息的指针
参数len为内存空间的最大长度,用于控制是否接收带外数据
参数fromlen为指向发送方地址的大小的指针
参数flags为具体的设置接收数据的方式控制参数
函数成功返回接收到的字节数,失败则返回-1
■ sendto函数
用于发送信息给指定的主机
函数的原型:
ssize_t sendto(int s,const void *buf,size_t len,int flags,const struct sockaddr *to,socklen_t tolen);
参数s为为套接字文件爱你描述符
参数buf为指向要发送信息得知空间的指针
参数len为要发送的字节数
参数flags为相关的控制信息
参数fromlen为指向接收方地址大小的指针
函数成功调用则返回实际发送的字节数,失败则返回0
四、网络的多路复用
在服务器端调用recv函数或者recvfrom函数接收客户端发送来的消息活调用addcpt函数是,都将处于阻塞状态,当程序处于阻塞状态时,程序将停止运行,这将限制程序的处理能力和功能
linux系统提供了fcntl函数来实现将套接字端口设置为非阻塞状态。具体的过程:
sock = socket(PF_INET,SOCK_STREAM,0)
fcntl(sock,F_SETFL 将此端口设置为非阻塞状态后,函数就可以接收来自其他客户解的连接了,但是如何保证能够收到来的信息,因此可以不断的询问讨套接字是否收到了信息。这样却会极大的占用CPU时间,
应此可用select函数进行处理,select函数提供了实现多路复用的功能。可以在监听其他客户机请求的时候保持于已经建立连接的客户机之间的通信
■ select函数的函数原型
int select(int nfds,fd_set *readfds,fd_set*writefds,fd_set *exceptfds,struct timeval *timeout)
readfds 为要监视的读文件描述符集合
writefds为要监视的写文件描述符集合
execptfds为要监视的一场条件的文件描述集合
nfds为要监视的三个文件描述符集合最大的文件描述符的数值加1
timeout 为select函数的等待时间
struct timeval{
long tv_sec; //秒
long tv_usec; //毫秒
};
■ FD_SET函数
将某个文件描述符加入上述的某一个集合
函数原型
void FD_SET(int fd,fd_set *set)
■ FD_iSSET函数
将获得准备好得文件描述的信息
int FD_ISSET(int fd,fd_SET *set)
当select函数发现有文件描述符准备好之后,将返回准备好的文件描述符号的数量,但是select并不知道有没有准备好的文件描述服,此时就需要FD_ISSET函数
■ FD_ZERO函数
清除文件描述符集
FD_ZERO(fd_set *set)
■ FD_CLR函数
将fd文件描述符此嗯文件描述符集合中移出
void FD_CLR(int fd,fd_set *set)
阅读(681) | 评论(0) | 转发(0) |