笔记
TCP和UDP的基础通信
一.套接字
socket函数介绍
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:为通信创建一个端点,并返回该端点的文件描述符
参数1:通信域
Name Purpose Man page
AF_UNIX, AF_LOCAL 本地通信,同一主机之间进程通信 详情请看man 7 unix
AF_INET IPv4 提供的网络通信 详情请看man 7 ip
AF_INET6 IPv6 提供的网络通信 详情请看man 7 ipv6
参数2:指定通信语义,可以由多个宏值使用位或连接
SOCK_STREAM:表示提供TCP协议的传输方式
SOCK_DGRAM:表示提供UDP协议的传输方式
SOCK_NONBLOCK:套接字设置非阻塞属性
参数3:如果参数2中仅仅指定一个协议,那么参数3可以填0,如果指定多个,则参数3需要指定特定的协议
TCP协议名称:IPPROTO_TCP
UDP协议名称:IPPROTO_UDP
返回值:成功返回创建的套接字文件描述符,失败返回 -1并置位错误码
二.TCP实现网络通信
TCP相关函数介绍
1> bind绑定
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
功能:位套接字分配名称
参数1:通过socket函数创建出来的套接字文件描述符
参数2:通用地址信息结构体,需要根据具体使用的地址族而定, struct sockaddr仅仅只是为了类型的强制转换,防止出现警告
跨主机间通信:man 7 ip
struct sockaddr_in {
sa_family_t sin_family; /* 表示通信域 */
in_port_t sin_port; /* 端口号的网络字节序 */
struct in_addr sin_addr; /* ip地址 */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* IP地址的网络字节序 */
};
同一主机间通信:man 7 uninx
struct sockaddr_un {
sa_family_t sun_family; /* 表示通信域:AF_UNIX */
char sun_path[108]; /* 套接字文件的地址 */
};
参数3:参数2的大小
返回值:成功返回0,失败返回-1并置位错误码
注意关于bind的两个错误:
1、 Cannot assign requested address:表示IP地址填写错误,检查IP是否有问题
2、Address already in use:表示地址信息正在占用,可以调用函数快速重用,也可以等一会
2> listen监听
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:将套接字设置成被动监听状态,已接受客户端的连接请求
参数1:套接字文件描述符
参数2:容纳连接的队列的最大长度,一般填128
返回值:成功返回0,失败返回-1并置为错误码
3> accept接收连接请求
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:用于阻塞接收客户端连接请求
参数1:服务器套接字文件描述符
参数2:用于接收对端地址信息结构体的指针
参数3:接收对端地址信息的长度
返回值:成功返回一个新的用于通信的套接字文件描述符,失败返回-1并置位错误码
4> recv接收数据
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:从套接字中读取数据到buf中
参数1:用于通信的套接字文件描述符
参数2:接收数据后的容器地址
参数3:接收的数据的大小
参数4:是否阻塞接收
0:表示阻塞接收消息
MSG_DONTWAIT:表示非阻塞接收数据
返回值:
>0:表示成功读取的字符个数
=0:表示通信对端已经下线
=-1:表示出错,置位错误码
5> send发送数据
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:向通信套接字文件描述符中写入数据
参数1:通信的套接字文件描述符
参数2:要发送数据的起始地址
参数3:要发送数据的大小
参数4:是否阻塞接收
0:表示阻塞接收消息
MSG_DONTWAIT:表示非阻塞接收数据
返回值:成功返回发送字符的个数,失败返回-1并置位错误码
6> connect连接函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
功能:将套接字文件描述符连接到addr指向的地址空间中
参数1:客户端套接字文件描述符
参数2:对端地址信息结构体
参数3:参数2的大小
返回值:成功返回0,失败返回-1并置位错误码
TCP服务器通信模型
1、sfd = socket(); //创建一个用于连接的套接字文件描述符
2、bind(); //为服务器套接字绑定ip地址和端口号,为了让客户端额能够找到服务器
3、listen(); //将服务器套接字设置成被动监听状态,用于接收客户端的连接请求
4、newfd = accept(); //阻塞等待客户端的连接请求,如果有客户端发来连接请求,创建一个新的用于通信的套接字文件描述符
5、while(1)
{
send\recv\read\write; //数据收发工作
}
6、close(); //关闭套接字、关闭监听
三.UDP实现网络通信
UDP相关函数
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
//功能:从套接字文件描述符中读取数据,并将对端地址信息结构体接收
参数1:套接字文件描述符
参数2:要接收数据的起始地址
参数3:要接收的数据大小
参数4:是否阻塞,0表示阻塞,MSG_NOWAIT表示非阻塞
参数5:接收对端地址信息结构体
参数6:参数5的大小
返回值:成功返回读取的字节的大小,失败返回-1并置位错误码
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
//功能:向套接字文件描述符中读取数据,写给指定的对端接收
参数1:套接字文件描述符
参数2:要发送数据的起始地址
参数3:要发送的数据大小
参数4:是否阻塞,0表示阻塞,MSG_NOWAIT表示非阻塞
参数5:接收对端地址信息结构体
参数6:参数5的大小
返回值:成功返回发送的字节的大小,失败返回-1并置位错误码
四.TCP和UDP基础通信模型注意事项
1> 无论是TCP还是UDP通信中,服务器端必须要绑定ip地址和端口号,以便于让客户端找到该服务器。对于客户端而言,可以绑定也可以不绑定,如果绑定了,则使用自己绑定的ip和端口号。如果没有绑定,则系统会自动绑定一个当前的IP地址和一个随机的端口号供客户端使用。
2> 对于TCP通信而言,可以使用recv和send进行通信,也可以使用read、write进行通信,还可以使用sendto和recvfrom进行通信,都没有问题。
3> 对于UDP通信而言,如果当前端只是用于接收数据,不发送数据,可以使用recvfrom、recv、read进行接收;如果当前端接收数据后还要发送数据给对端,则需要使用recvfrom进行接收数据,顺便将对端地址信息结构体接收过来。
4> UDP中是否可以使用connect函数呢?
答,可以使用。当UDP服务器端使用connect会跟指定的客户端建立一个唯一的通道。其他客户端就不能再进行通信了。如果想要断开连接,需要再次使用connect函数,并且将地址信息结构体中的family设置成AF_UNSPEC.
好处:1、能够提供数据传输效率
例如:A和B同时向服务器发送消息,但是A发送的消息较大,需要较长的时间,发送过程中可能会出现时间片用完,此时B向服务器发送消息,导致,服务器处理消息时,消息混乱。这时就可以先单独跟A建立连接,等所有数据传输结束后,再跟B通信
2、传输性能高
一般的UDP通信:获取对端地址信息 --> 将信息加载到内核 ---> 数据收发 --->获取对端地址信息 --> 将信息加载到内核 ---> 数据收发 --->获取对端地址信息 --> 将信息加载到内核 ---> 数据收发 --->....
UDP建立连接后:获取对端地址信息 --> 将信息加载到内核 ---> 数据收发 --->数据收发 --->数据收发 --->数据收发 --->数据收发 --->数据收发 --->