Unix/C/C++--套接字socket

1 简介

2 流程

3 接口介绍

3.1 POSIX socket flow:

1、create a socket

#include <sys/socket>
// success return file descriptor, error return -1
int socket (int domain, int type, int protocol);

参数domain(域)确定通信的特性,包括地址格式(在下一节详细
描述)。下图总结了由POSIX.1指定的各个域。各个域都有自己表示地
址的格式,而表示各个域的常数都以AF_开头,意指地址族(address
family)。
在这里插入图片描述
参数type确定套接字的类型,进一步确定通信特征。
在这里插入图片描述
参数protocol通常是 0,表示为给定的域和套接字类型选择默认协
议。当对同一域和套接字类型支持多个协议时,可以使用protocol 选择
一个特定协议。在 AF_INET 通信域中,套接字类型SOCK_STREAM的
默认协议是传输控制协议(Transmission Control Protocol,TCP)。在
AF_INET通信域中,套接字类型SOCK_DGRAM的默认协议是UDP。图
16-3列出了为因特网域套接字定义的协议。
在这里插入图片描述
2、shutdown read,write,or r/w, flow
套接字通信是双向的。可以采用shutdown函数来禁止一个套接字的
I/O。

#include <sys/socket.h>
int shutdown (int sockfd, int how);
返回值:若成功,返回0;若出错,返回−1

如果how是SHUT_RD(关闭读端),那么无法从套接字读取数据。
如果how是SHUT_WR(关闭写端),那么无法使用套接字发送数据。
如果how是SHUT_RDWR,则既无法读取数据,又无法发送数据。
close才释放网络端点。shutdown是控制端点功能。
3、字节序
在这里插入图片描述
对于TCP/IP,地址用网络字节序来表示,所以应用程序有时需要在处理器的字节序与网络字节序之间转换它们。
对于TCP/IP应用程序,有4个用来在处理器字节序和网络字节序之
间实施转换的函数。

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostint32);
返回值:以网络字节序表示的32位整数
uint16_t htons(uint16_t hostint16);
返回值:以网络字节序表示的16位整数
uint32_t ntohl(uint32_t netint32);
返回值:以主机字节序表示的32位整数
uint16_t ntohs(uint16_t netint16);
返回值:以主机字节序表示的16位整数
h表示“主机”字节序,n表示“网络”字节序。l表示“长”(即4
字节)整数,s表示“短”(即4字节)整数。

4、地址结构sockaddr
为了适应套接字函数,通信地址都会转为通用的地址结构sockaddr
在 Linux 中,该结构定义如下:

struct sockaddr {
sa_family_t     sa_family;  /* address family */
char         sa_data[14]; /* variable-length
address */
};

5、套接字socket和地址sockaddr关联

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t len);
返回值:若成功,返回0;若出错,返回−1

6、建立连接(客户端、服务端)

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
socklen_t len);
返回值:若成功,返回0;若出错,返回−1

7、服务器开启,可以接受连接请求
服务器调用listen函数来宣告它愿意接受连接请求

#include <sys/socket.h>
int listen(int sockfd, int backlog);
返回值:若成功,返回0;若出错,返回−1

参数backlog提供了一个提示,提示系统该进程所要入队的未完成连
接请求数量。其实际值由系统决定,但上限由<sys/socket.h>中的
SOMAXCONN指定。
8、使用accept函数获得连接请求并建立连接

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *restrict addr,
socklen_t *restrict len);
返回值:若成功,返回文件(套接字)描述符;若出错,返回−1

函数accept所返回的文件描述符是套接字描述符,该描述符连接到
调用connect的客户端。这个新的套接字描述符和原始套接字(sockfd)
具有相同的套接字类型和地址族。传给accept的原始套接字没有关联到
这个连接,而是继续保持可用状态并接收其他连接请求。
9、数据读写
既然一个套接字端点表示为一个文件描述符,那么只要建立连接,
就可以使用read和write来通过套接字通信。
尽管可以通过read和write交换数据,但这就是这两个函数所能做的
一切。如果想指定选项,从多个客户端接收数据包,或者发送带外数
据,就需要使用6个为数据传递而设计的套接字函数中的一个。
3个函数用来发送数据,3个用于接收数据。

#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
返回值:若成功,返回发送的字节数;若出错,返回−1
类似write,使用send时套接字必须已经连接。参数buf和nbytes的含
义与write中的一致。
于sendto可以在无连接的套接字上指定一个目标地址。
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t nbytes,
int flags,
const struct sockaddr *destaddr, socklen_t
destlen);
返回值:若成功,返回发送的字节数;若出错,返回−1
通过套接字发送数据时,还有一个选择。可以调用带有msghdr结构
的sendmsg来指定多重缓冲区传输数据,这和writev函数很相似。
#include <sys/socket.h>
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
返回值:若成功,返回发送的字节数;若出错,返回−1
函数recv和read相似,但是recv可以指定标志来控制如何接收数据。
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
返回值:返回数据的字节长度;若无可用数据或对等方已经按序结束,
返回0;若出错,返回−1
如果有兴趣定位发送者,可以使用recvfrom来得到数据发送者的源
地址。
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags,
struct sockaddr *restrict addr, socklen_t *restrict addrlen);
返回值:返回数据的字节长度;若无可用数据或对等方已经按序结束,
返回0;若出错,返回−1
为了将接收到的数据送入多个缓冲区,类似于readv,
或者想接收辅助数据,可以使用recvmsg。
#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
返回值:返回数据的字节长度;若无可用数据或对等方已经按序结束,
返回0;若出错,返回−1

3.2 地址结构

sockaddr结构体
sockaddr的缺陷:sa_data把目标地址和端口信息混在一起了

struct  sockaddr{
  sa_family_t  sa_family; 
  //地址族,最常用的是"AF_INET"(IPV4)和"AF_INET6"(IPV6); 
  char  sa_data[14];      
	//包含套接字中的目标地址和端口信息;**加粗样式**
}

sockaddr_in 结构体
sockaddr_in结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中
struct sockaddr_in/ struct sockaddr_in6结构体

struct  sockaddr_in{
  sa_family_t   sin_family;  //地址族,常用AF_INET
  uint16_t         sin_port;     //16位TCP/UDP端口号
  struct  in_addr  sin_addr;    //32位IP地址
  cha         sin_zero[8]   //不使用
}struct  in_addr{
  in_addr_t    s_addr;      //32位IPV4地址
}

struct sockaddr_in6 {
   sa_family_t     sin6_family; /* AF_INET6 */
   in_port_t      sin6_port; /* port number */
   uint32_t       sin6_flowinfo; /* IPv6 flow information */
   struct in6_addr    sin6_addr; /* IPv6 address */
   uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */
};

struct in6_addr {
  unsigned  char s6_addr[16]; /* IPv6 address */
};

struct sockaddr_in6相关详见 :http://man7.org/linux/man-pages/man7/ipv6.7.html

参考

1、《Unix高级环境编程》[第三版]
2、C语言socket编程----struct sockaddr 和struct sockaddr_in介绍和初始化

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读