Chapter2 基本的TCP套接字

Chapter2 基本的TCP套接字

 套接字是一个抽象层,应用程序通过套接字发送或接受数据,其方式类似打开文件句柄写入或读取。TCP/IP中的套接字主要是流套接字和数据报套接字。流套接字基于端到端协议,提供可靠的服务;数据报套接字基于UDP协议,提供尽力而为服务。
 一个程序可以对应多个套接字,同样,一个套接字可以对应多个程序。套接字通过Internet地址、协议、端口号唯一标识。

套接字的创建和销毁

 在进行通信之前,需要先创建套接字抽象层。

int socket(int domian,int type,int protocol)
  • 参数domian指定套接字的通信领域,是IPv4(AF_INET)还是IPv6(AF_INET6)
  • 参数type指定套接字的类型,是用于可靠字节流(SOCK_STREAM)还是尽力而为(SOCK_DGRAM)
  • 参数protocol指定套接字使用的协议,流套接字使用TCP(IPPROTO_TCP),数据报套接字使用UDP协议(IPPROTO_UDP)
  • 函数返回为套接字描述符,非负值表示创建成功, − 1 -1 1表示失败。套接字描述符可以传递给其他API,以标识要在其上执行操作的套接字抽象层。

 通信完成后,要关闭通信,并释放与套接字关联的资源。

int close(int socket)

如果成功会返回 0 0 0,否则返回 − 1 -1 1

Internet地址数据结构

 Sockets API提供了一种泛型数据结构(sockaddr),以存储网络地址和端口。IPv4和IPv6有其特化的数据结构(sockaddr_insockaddr_in6),但是在为API提供参数时,需要强制转换成sockaddr,函数会检查sa_family项以获知真实类型,并强制转换回合适类型。

struct sockaddr_in
{
	sa_family_t sin_family;
	in_port_t sin_port;
	struct in_addr sin_addr;
	char sin_zero[8];// not usedd
}
  • sin_family负责表示地址族(AF_INETAF_INET6),其中的s_addr项可以标定sin_addr的内存地址
  • sin_port为端口号,需要转换成二进制,htons(int port)将主机字节序转换成网络字节序
  • sin_addr为网络地址,也得是二进制
struct sockaddr_in6
{
	sa_family sin6_family;
	in_port_6 sin6_port;
	uint32_t sin6_flowinfo;
	struct in6_addr sin6_addr;
	uint32_t sin6_scope_id;
}

书上没讲

打印字符到网络二进制的相互转换

 前面提到sin_portsin_addr需要表示为网络二进制,sin_porthtons()负责,sin_addrinet_pton()负责。

int inet_pton(int addressFamily, const char *src, void *dst);
  • addressFamily为地址族(AF_INETAF_INET6)
  • src为IP地址字符串
  • dst为地址存储结构中IP地址所应存放的内存块,即s_addr
  • 如果成功则返回 1 1 1,如果src指向的内容无效则返回 0 0 0,如果addressFamily无效则返回 − 1 -1 1

 相似的,从网络二进制到可打印字符由inet_ntop()负责

const char *inet_ntop(int addressFamily, const void *src, char *dst, socklen_t dstBytes)
  • src为被转换的网络二进制所在的内存空间,即s_addr
  • dst为程序员为可打印字符分配的空间,IPv4使用INET_ADDRSTRLEN指定大小,IPv6使用INET6_ADDRSTRLEN
  • dstBytesdst所指向空间的大小
  • 如果成功,返回可打印字符所在的空间;如果失败,返回NULL

客户与服务器建立链接

 在服务器的IP地址和端口处,客户和服务器进行接触。对于客户机而言,它需要将它的套接字,连接到服务器的IP地址和端口处。

int connect(int socket,const struct sockaddr *foreignAddress,soklen_t addressLength)

对于服务器而言,它需要将它的套接字绑定到它自己的IP地址和端口上。

int bind(int socket,struct sockaddr *loackAddress,socklen_t addresslen)

一个主机会有多个IP地址,如果服务器希望接受发送到该主机的所有地址上的信息,它可以将loackAddress中的sin_addr项置为INADDR_ANY(或者sin6_addr置为INADDR6_ANY),但是要注意,INADDR_ANY是主机字节序,需要专场网络字节序(honl(INADDR_ANY)负责这个工作)。INADDR6_ANY不必。

通信

服务器的套接字要转换成侦听状态

 服务器端的通信并不又原先的套接字完成,而是由原先的套接字根据建立的链接新建套接字。这点容易理解:服务要处理很多请求,但原套接字只有一个,肯定是新建套接字更容易理解。

int listen(int sockdt,int queuelen)

listen函数负责将原套接字转成侦听状态,queuelen为队列长度

根据链接新建套接字

int accept(int socket,struct sockaddr *clientAddress,socklen_t addresslen)

socket项为原套接字,alientAddress为客户端的IP地址和端口号信息,addresslen为前者的长度。
 通过accept操作,服务器可以知道客户机的IP地址和端口号,并返回一个新套接字,用于处理该链接。

通信

 一旦连接建立,客户与服务器之间的区别就会消失,可以通过各自的套接字使用send()recv()互相通信。

ssize_t send(int socket,const void *msg,size_t msgLength,int flags)
ssize_t recv(int socket,void *rcvBuffer,size_t bufferLength,int flags)
  • socket是用于通信的套接字
  • msg是发送数据所在的内存区域,rcvBuffer是接收缓存
  • msgLength为发送数据的长度,bufferLength为接收缓存的长度,是的,后者应该很大(BUFSIZ)
  • flags指定行为
  • send()的默认行为是阻塞,直到发送了msgLength长度的字符为止。如果成功则返回发送的字符长度,否则返回 − 1 -1 1
  • recv()的默认行为是阻塞,直到接收到了一些字符为止。并返回接收到的字符长度,如果失败返回 − 1 -1 1,也就是说,如果recv()不可能为 0 0 0
  • 要注意send()一次发送的字符,recv()可能要多次接收;反之亦然,send()多次发送的字符,recv()可能一次就能完成接收。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值