套接字编程

本文详细解析了套接字编程的基本概念,包括TCP和UDP的服务器端和客户端框架,涉及socket创建、地址绑定、数据交互等关键函数及其用法。
摘要由CSDN通过智能技术生成

1.套接字编程的个人理解

理解套接字编程的核心是要理解socket。网络编程的作用是使得不同主机上的进程可以通信,那么这个“通道”一般可认为是双向通道,也就是全双工通信。而socket在网络编程中可以理解为是“通道”的端,两个进程要通信,需要借助这两个端连接起来的“通道”进行数据交互。
在网络请求中,有发起请求的一方和响应的一方,发起请求的一方称为客户端,响应的一方称为服务端。套接字通信类似于打电话,有主动拨打电话的一端,也有接电话回应的一端,而不管是哪一端的套接字编程,都有一个框架(编程步骤),下面将分别介绍框架。

2.套接字编程的框架(TCP)

以下是文字版,从整体框架上理解套接字编程,以及每个步骤需要使用到的函数。

服务器端

  1. 创建套接字,声明服务器端地址(socket、htonl、htons函数)
  2. 将地址绑定到套接字(bind函数)
  3. 开启套接字监听(listen函数)
  4. 使用套接字受理连接(accept函数)
  5. 数据交互(read、write函数)
  6. 断开连接(close函数)

客户端

  1. 创建套接字,声明客户端地址(socket、htonl、htons函数)
  2. 使用套接字发起请求(connect函数)
  3. 数据交互(read、write函数)
  4. 断开连接(close函数)

上述框架中使用到的函数,以下做出详细解释(包括头文件和函数参数的解释)。

//创建套接字
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
//domain = 协议簇,通常是PF_INET(ipv4)
//type = 套接字类型,SOCK_STREAM(面向连接的)/ SOCK_DGRAM(面向消息的)
//protocol = 协议,通常前两个参数确定了,该参数可以填0,也可以tcp/udp
//return = 成功则返回文件描述符,失败返回-1
//绑定套接字,将套接字和本主机的ip地址绑定
//当程序在不同主机之间交互,且主机有多块网卡,需要手动绑定
#include<sys/socket.h>
int bind(int sockfd, struct sockadd* myaddr, socklen_t addrlen);
//sockfd = 文件描述符, 套接字
//myaddr = 套接字的地址,通常使用sockaddr_in结构体
//addrlen = 地址的长度
//return = 成功则返回0,失败返回-1
//开启套接字监听(是否有请求连接),确定请求队列的长度
#include<sys/socket.h>
int listen(int sockfd, int backlog);
//sockfd = 文件描述符, 套接字 
//backlog = 最大监听请求的个数
//return = 成功则返回0,失败返回-1
//接收套接字,创建一个新的的套接字,使得与请求连接的套接字建立联系
#include<sys/socket.h>
int accept(int sockfd, struct sockadd* myaddr, socklen_t* addrlen);
//sockfd = 套接字
//myaddr = 新的套接字的地址结构体
//addrlen = 地址长度,注意用的是指针。(为什么用指针?)
//return = 成功则返回一个新的套接字,这个套接字的地址信息存在myaddr中,失败返回-1
//请求连接套接字
#include<sys/socket.h>
int connect(int client_socket, struct sockadd* serv_addr, socklen_t addrlen);
//client_socket = 客户端创建的套接字
//serv_addr = 服务端的套接字地址
//addrlen = 地址长度
//return = 成功则返回0,失败返回-1

这里单独解释一下对于文件的打开和关闭操作。

//打开文件
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
int open(const char* path, int flag);
//path = 文件路径
//flag = 读写模式:仅读、仅写、可读可写、创建
//return= 成功则返回文件描述符,失败返回-1
//关闭文件(套接字)
#include<unistd.h>
int close(int fd);
//fd = 文件描述符
//return = 成功则返回0,失败返回-1

在声明主机IP地址的时候,需要用到以下结构体。注意到bind、accept、connect函数参数要求的是sockadd结构体的地址,所以在传参的时候要强制类型转为sockadd指针,如(sockaddr*)&addr。

//sockaddr_in结构体
#include <arpa/inet.h>
addr.sin_family;//协议簇
addr.sin_addr.s_addr;//ip地址
addr.sin_port;//端口号

在设置sockaddr_in结构体时,涉及“主机字节序与网络字节序(大端字节序)的转换”,其中大小端字节序和CPU架构有关。在网络传输数据的过程中,统一使用网络字节序,即统一转换为大端字节序,所以需要用到以下函数将主机字节转换为网络字节序。

//名称中:h表示主机字节序,n表示网络字节序。
//名称中:s对应参数的short,l对应参数的long(4字节用来储存IP地址)
unsigned short htons(unsigned short); // port
unsigned long htonl(unsigned long); // ip

在设置结构体sockaddr_in中的addr.sin_addr.s_addr字段时(设置主机IP地址),通常使用的是点分十进制表示的字符串,但是该字段要求的是32位整型,因此需要将字符串转换为32位整型。以下提供两种转换方式:在网络编程中统一使用的是网络字节序,涉及到“字符串形式的点分十进制与网络字节序的转换”。

//个人更喜欢这种,因为在声明IP地址的时候,代码可以保持统一
#include<arpa/inet.h>
in_addr_t inet_addr(const char* string);
//string = 字符串形式的ip地址
//return = 32位整型的ip地址
//这种方式主要是可以直接传入addr,省去了addr.sin_addr.s_addr的再次赋值
int inet_aton(const char* string, strcuct in_addr* addr);
//string = 字符串形式的ip地址
//addr = 转换结果的保存地址,直接用的是sin_addr
//return = 成功则返回0,失败返回-1

在Linux中,一切皆文件。socket函数返回的文件描述符则可以认为是一种文件句柄,所以可以通过操作文件的方式去进行socket的数据交互,以下是读写函数的解释。

#include<unistd.h>
ssize_t write(int fd, const void* buf, size_t nbytes);
//fd = 用于写入数据的文件描述符
//buf = 写入数据的缓冲区地址,要使用类型转换
//nbytes = 要写入数据的字节数
//return = 成功时返回写入的字节数,失败了返回-1
#include<unistd.h>
ssize_t read(int fd, void* buf, size_t nbytes);
//fd = 用于读取数据的文件描述符
//buf = 读取数据的缓冲区地址,要使用类型转换
//nbytes = 要读取数据的最大字节数
//return = 成功时返回读取的字节数(遇到文件尾EOF则返回0),失败了返回-1

3.套接字编程的框架(UDP)

与面向连接的TCP套接字编程不同,面向消息的UDP套接字编程中没有客户端和服务器端的区别,只有发送数据的一端和接收数据的一端。这里仔细解释一下发送数据的一端和接收数据的一端,也就是说socket套接字作为一个端,它既可以发送数据,也可以接收数据,当发送数据的时候可以认为是客户端,当接收数据的时候可以认为是服务器端。

以下是文字版,从整体框架上理解套接字编程,以及每个步骤需要使用到的函数。

  1. 创建套接字,声明服务器端地址(socket、htonl、htons函数)
  2. 将地址绑定到套接字(bind函数)
  3. 数据交互(sendto、recvfrom函数)
  4. 关闭套接字(close函数)

上述框架中使用到的函数,以下做出详细解释(包括头文件和函数参数的解释)。

#include <sys/socket.h>
ssize_t sendto(int sock, void* buff, size_t nbytes, int flags, sockaddr* to, socklen_t addrlen);
//sock = 我方的套接字
//buff = 要发送的数据
//nbytes = 发送数据的大小,字节数
//flags = 可选参数,通常为0
//to = 要发送的目的地址,已经注册了地址信息的
//addrlen = 目的地址的长度
//return = 返回成功发送的字节数,失败则返回-1
#include <sys/socket.h>
ssize_t recvfrom(int sock, void* buff, size_t nbytes, int flags, sockaddr* from, socklen_t* addrlen);
//sock = 我方的套接字
//buff = 用来接收数据的结构,通常用c风格的字符数组,必须得手动添加终止符
//nbytes = 接收数据的大小,字节数
//flags = 可选参数,通常为0
//from = 来自发送端的地址,这个地址不需要注册地址信息
//addrlen = 发送端地址的长度
//return = 返回成功接收的字节数,失败则返回-1
  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值