Socket学习笔记
插板插座
网络套接字
在通信过程中,套接字一定是成对存在的
两份套接字,C一个 S一个
一个文件描述符指向一个套接字(该套接字内部由内核借助两个缓冲区实现)
网络字节序
小端法: 高位高地址 低位低地址 (Inter使用)
大端法 : 高位低地址 低位高地址 (IBM一开始使用)TCP/IP协议规定,网络数据流应采用大端字节序
例如UDP段格式,地址0-1是16位的源端口号,如果这个端口号是1000 (0x3e8), 则地址0是0x03, 地址1是0xe8, 也就是先发0x03,再发0xe8,这16位在发送主机的缓冲区中也应该是低地址存0x03, 高地址存0xe8。但是,如果发送主机是小端字节序的,这16位被解释成0xe803, 而不是1000。 因此,发送主机把1000填到发送缓冲区之前需要做字节序的转换。同样地,接收主机如果是小端字节序的,接到16位的源端口号也要做字节序的转换。如果主机是大端字节序的,发送和接收都不需要做转换。同理,32位的IP地址也要考虑网络字节序和主机字节序的问题。
htonl 是将本地的32位整型数转换为网络字节序。 主要是用于IP的转换。
192.168.1.11 --> string -->atoi -->int -->htonl -->网络字节序
IP地址转换函数
int inet_pton (int af ,const char *src, void *dst)
af 指代当前协议,IPV4 (AF_INET)或者IPV6 (AF_INET6)
src 传入ip地址 (点分十进制)
dst 传出 转换后的 网络字节序的IP地址
返回值:
成功:1
异常: 0 说明src 指向的不是一个有效的IP地址
失败: -1
const char *inet_ ntop(int af,const void * src, char * dst, socklen_t size); 网络字节序--->本地字节序(string IP) af: AF_INET、AF_INET6 src: 网络字节序IP地址 dst: 本地字节序(string IP) size: dst 的大小。
返回值:成功:dst
失败 : null
sockaddr 地址结构
struct sockaddr_in add;
第一个参数 add.sin_family = AF_INET
第二个参数 add.sin_port = htons(8080);
第三个参数是结构体 add.sin_addr.s_addr
但是一般是用add.sin_addr.s_addr = htonl( INADDR_ANY) (取出系统中任意有效的IP地址,是二进制类型,所以用htonl进行转换)
流程
TCP服务端同socket函数生成一个套接字,套接字中有文件描述符,这个文件描述符相当于句柄,一个入口。然后调用bind()函数将端口号和IP绑定,再调用listen() listen的作用是设置监听上限,就是一个参数设置。再进行调用accept()是阻塞监听客户端连接。accept的参数中包含着之前用socket()生成的套接字的句柄。然后accept函数会返回一个新的socket()。这个新的socket 和客户端的套接字进行通信。
TCP客户端通过socket()新建了一个套接字,然后通过connect()进行连接。connect()中要绑定IP和port 。此时服务端和客户端就连接在了一起,整个系统中存在三个socket(),一对和一个独立的,一对的是进行连接通信的,单独的那个是进行监听的。
Socket函数
int socket(int domain, int type, int protocol);
domain是指定当前的IP协议是哪个,例如我们选择AF_INET即IPV4.
type指定当前套接字的数据传输协议。期中有流式传输STREAM, 报式协议SOCK_DGRAM
protocol : 表示所选协议中的代表协议,一般我们都是传0.
举例: type的参数是STREAM时,protocol的参数是0,就表示当前协议是TCP,如果type是DGRAM,protocol的参数是0,就表示单前协议是UDP
返回值:如果成功就返回新套接字所对应的文件描述符。
失败就是 -1 errno。
举例:fd= socket(AF_INET,SOCK_STREAM,0) 就是返回一个IPV4的TCP协议的套接字。
bind()
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
给socket 绑定一个地址结构(IP+port)
sockfd 就是socket函数返回值
addr 就是sockaddr 地址结构,要分别对三个参数进行初始化。
这个结构体和参数中需要的结构体不同,所以初始化完了之后要进行强转。
例如:
struct sockaddr_in addr ;
addr.sin_family = AF_INET; //(和前面使用的IP协议相同)
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr :(struct sockaddr *) &addr ;
addrlen :sizeof(addr) 地址结构的大小
返回值:
成功: 0
失败: -1 errno
listen()
int listen(int sockfd, int backlog);
设置同时与服务器建立连接的上限数(同时进行三次握手的客户端数量)
sockfd: socket的句柄
backlog : 上限数值, 最大值为128
成功:返回0
失败: 返回-1
ACCEPT
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <sys/socket.h>
int accept4(int sockfd, struct sockaddr *addr,socklen_t *addrlen, int flags);
阻塞等待客户端建立连接,成功返回一个与客户端成功连接socket文件描述符。用一个socket返回一个新socket
sockfd: socket的句柄
addr : 和bind 的参数差一个关键字const ,上一个是传入参数,这个是传出参数,即传出的是成功与服务器建立连接的那个客户端的地址结构。
addrlen : 入:addr的大小 出:客户端addr的实际大小
返回值:
成功:能与客户端进行数据通信的socket对应的文件描述。
失败: -1 errno
一个端口肯定只能绑定一个socket。服务器端的端口在bind的时候已经绑定到了监听套接字socetfd所描述的对象上,accept函数新创建的socket对象其实并没有进行端口的占有,而是复制了socetfd的本地IP和端口号,并且记录了连接过来的客户端的IP和端口号。
那么,当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?
客户端发送过来的数据可以分为2种,一种是连接请求,一种是已经建立好连接后的数据传输。
由于TCP/IP协议栈是维护着一个接收和发送缓冲区的。在接收到来自客户端的数据包后,服务器端的TCP/IP协议栈应该会做如下处理:如果收到的是请求连接的数据包,则传给监听着连接请求端口的socetfd套接字,进行accept处理;如果是已经建立过连接后的客户端数据包,则将数据放入接收缓冲区。这样,当服务器端需要读取指定客户端的数据时,则可以利用socketfd_new 套接字通过recv或者read函数到缓冲区里面去取指定的数据(因为socketfd_new代表的socket对象记录了客户端IP和端口,因此可以鉴别)。
connect()
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
与服务器建立连接。
sockfd : 客户端socket函数返回值。
addr : 传入参数, 服务器的地址结构 。
addrlen : 服务器地址结构的长度。
返回值:
成功 :0
失败: -1 errno
这边不使用bind 函数进行绑定的话,就采用了“隐式绑定”。
TCP通信流程分析:
server:
-
socket( ) 创建socket
-
bind ( ) 绑定服务器地址结构
-
listen( ) 设置监听上限
-
accept() 阻塞监听客户端连接
-
read (fd) 读socket获取客户端数据
-
小--大写 toupper ()
-
write(fd)
-
close()
client:
-
socket() 创建socket
-
connect( ) 与服务器建立连接
-
write ( ) 写数据到socket
-
read() 读转换后的数据。
-
显示读取结果
-
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南