Socket
socket是实现网络主机进程间通信的一种机制。
从用户空间来看,socket就是一个文件描述符,对socket的操作等同于对普通的文件描述符操作,可以使用read、write、close函数来操作,一旦针对该socket必要的初始化完成后,与对端的数据交互都是通过该socket来实现的
例如:
- 要向对方发送数据,只需要将数据write到该socket;
- 要接收数据,只要阻塞地在socket上读数据即可;
从内核空间来看,socket不再指向一个磁盘文件,相应地读写指针指向的代码也是网卡驱动程序提供的数据发送和接收函数。其主要的资源是一个内核空间的struct sk_buff结构体对象。在该对象中详细的描述了通信双方的基本信息,缓冲的数据等。
根据是否面向连接,可将socket通信分为面向连接的数据流通信和面向无连接的数据流通信。这两者在实现上有类似的地方,即都需要创建相应的socket对象,但是,这两者也有明显的区别
1、面向连接的TCP通信需要双方建立可行的数据连接后才能通信
2、而面向无连接的UDP通信则只是简单的将数据发送到对应的目的主机即可,而不管对方是否处于存活状态,对方是否允许接收该数据包以及该数据包是否完整地被发送到目标主机
面向来连接的数据流方式:此类型的套接字是可靠的,在这种套接字中,数据传送和发送顺序一致。在传输数据之前,通信双方需要建立可靠的链路,在传送过程中,数据作为字节流传输。这种传输数据的方式可靠性高,如:TCP
面向无连接的数据报方式:此类型是不可靠的,这种套接字,数据传送和发送顺序可能不一样。在发送和接收之间没有逻辑连接,每个数据报的发送和处理都是独立的,不同的数据报可以采用不同的路由路径达到目的地。如:UDP
基于BSD的socket为应用开发提供了统一的编程接口,只需要调用BSD socket所提供的编程函数即可
sockaddr结构
如果是IPV4网络通信,
socket编程接口
socket常见API
//创建socket文件描述符(TCP/UDP,客户端&&服务器)
int socket(int domain, //AF_INET(IPV4),AF_INET6(IPV6)
int type, //SOCK_STREAM(TCP),SOCK_DGRAM(UDP),SOCK_RAW(原始套接口)
int protocol) //0
- domain:用来指明此socket对象所使用的地址簇或者协议簇,就是此对象所使用的通信协议类型(地址簇是协议簇宏定义,实际上是一致的)
- type:socket类型
- protocol:标识采用协议簇中的哪一种协议,一般设置为0,代表让系统自动选择默认协议,但原始套接口需要指定具体的协议
//绑定端口号(TCP/UDP,服务器)
int bind(int _fd,
const struct sockaddr *address,//sockaddr结构指针
socket_t address_len);//绑定的地址长度,一般用sizeof
//监听网络(socket)(TCP,服务器)
int listen(int _fd,
int _n);
//_fd:绑定了IP及端口信息的socket文件描述符
//_n:请求排队的最大长度。当有多个客户端和服务器相连时,这个值表示可以使用的处于等待的队列长度
listen函数将绑定的socket文件描述符变为监听套接字,此时,服务器已经准备接收客户端连接请求了
//客户端发起连接
int connet(int _fd,
const struct sockaddr *address,
socket_t address_len);
//服务器接收数据
int accept(int _fd,
struct sockaddr *address,
socket_t address_len);
读/写socket对象
socket对象是一种特殊的文件,因此可以使用read函数来读socket对象数据,write函数向socket对象写入数据
TCP发送接收数据
size_t send(int _fd,
const void *buf,//要发送的数据
size_t n, //要发送数据的大小
int flags); //0
size_t recv(int _fd,const void *buf,size_t n,int flags);
//两个函数的第四个参数flags用来说明数据处理的方式
关闭socket对象
在通信结束后,需要关闭socket对象,这里有两种方法
1、close函数
int close(int _fd);
2、 shutdown函数
int shutdown(int _fd,int _how);
shutdown函数灵活性更大,可以关闭全部,或者关闭socket的一端
TCP连接是双向的(可读写的),当使用close()时,会把读写通道都关闭,有时希望只关闭一个方向,这时就要用shutdown。系统提供了以下3种关闭方式:
- how=0:系统关闭读通道,但可以继续往socket描述符中写
- how=1:关闭写通道,此时只能读
- how=2:关闭读写通道,和close一样,完全关闭
获取socket本地的和对端的信息
1、getsockname函数
获取一个套接字的本地信息,(这个套接字至少完成了绑定本地IP地址)。
int getsockname(int fd,_SOCKADDR _addr,socklen_t *_restrict len)
2、getpeername函数
获取一个已经连接上的套接字的远程信息,如IP地址和端口
int getpeername(int fd,_SOCKADDR _addr,socklen_t *_restrict len)
应用实例
struct sockaddr test;
getsockname(new_fd,(struct sockaddr *)&test,&size);
printf("ip=%s,port=%d\n",inet_ntoa(test.sin_addr),ntohs(test.sin_port));