网络编程(第二章: TCPUDP基础模型)

文章详细介绍了TCP和UDP协议在套接字编程中的应用,包括socket函数创建套接字,TCP的TCP模型,以及相关函数如bind、listen、accept、recv、send的功能和使用方法。对于UDP,提到了其与TCP的区别,以及recvfrom和sendto函数在UDP中的作用,还强调了UDP中connect函数的特性和优点。
摘要由CSDN通过智能技术生成

TCP/UDP(服务器、客户端源码)

[(12条消息) 网络编程(4.7作业)(TCP/UDP源代码)_m0_37565374的博客-CSDN博客]:

一. 套接字 socket

1.概念

  1. 最早的socket和消息队列、共享内存,管道一致只能实现一台主机中的多个进程间通信。后期加入了TCP/IP 使得socket支持不同主机的进程间通信。
  2. socket也是一个函数,返回值是一个文件描述符。

image-20230410141427582

2. socket函数(创建套接字)

功能:在内核空间中创建两个缓冲区:接收缓冲区,发送缓冲,用户空间可以接收到两块空间的文件描述符
原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

参数:

  • int domain:指定协议族(地址族)

    NamePurposeMan page
    AF_UNIX, AF_LOCAL局域网unix(7)
    AF_INETipv4ip(7)
    AF_INET6ipv6ipv6(7)
  • int type:

    SOCK_STREAM字节流式套接字:默认指定TCP协议
    SOCK_DGRAM数据报式套接字:默认指定UDP协议
    SOCK_RAW原始套接字,协议需要在第三个参数指定;
  • int protocol:指定协议; 默认协议填0;

    • IPPROTO_TCP tcp协议
    • IPPROTO_UDP udp协议;

返回值:

  • 成功,返回维护缓冲区的文件描述符,套接字文件描述符;
  • 失败,返回-1,更新errno;

二. TCP

1. TCP模型图

image-20230410143239498

image-20230410143247617

2. TCP搭建相关函数

2.1 socket函数(创建套接字)(同上)

2.2 bind(绑定套接字)

功能:将IP和端口绑定到套接字上
原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

  • int sockfd:指定要绑定到哪个套接字上,填对应文件描述符;

struct sockaddr*addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定;
                    指定需要绑定到套接字上的IP和端口
--AF_INET--man 7 ip----
   struct sockaddr_in {
       sa_family_t    sin_family; /* address family: AF_INET */     必须填AF_INET;
       in_port_t      sin_port;   /* port in network byte order */  端口号的网络字节序,1024~49151
       struct in_addr sin_addr;   /* internet address */            本机IP地址的网络字节序
                                                                   终端输入:ifconfig查看本机IP
   };
/* Internet address. */
struct in_addr {
   uint32_t       s_addr;     /* address in network byte order */
};

  • socklen_t addrlen:真实的地址信息结构体的大小:sizeof(struct sockaddr_in);

返回值:

  • 成功,返回0;
  • 失败,返回-1,更新errno;

2.3 listen(设置被动监听模式)

功能:将套接字设置为被动监听状态,监听是否有客户端连接成功;
原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);

参数:

  • int sockfd:指定将哪个套接字设置为被动监听状态; socket函数的返回值;
  • int backlog:指定未完成连接队列的容量; 允许同时多少个客户端处于未完成连接状态。 一般填128
    内核会维护两个队列:未完成连接的队列,已完成连接的队列;

返回值:

  • 成功,返回0;
  • 失败,返回-1,更新errno;

2.4 accept(获取客户信息)

功能:阻塞函数,从已完成连接的队列头中获取一个客户端信息,生成一个新的文件描述符
该文件描述符才是与客户端通信的文件描述符!!

原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:

  • int sockfd:被设置为被动监听状态的文件描述符;

  • struct sockaddr *addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定;
    存储连接成功的客户端的地址信息。如果不想获取,则填NULL;

  • socklen_t *addrlen:真实的地址信息结构体的大小,注意是指针类型,需要在外部定义普通变量,赋值后取地址;
    如果第二个参数填NULL,则当前参数填NULL;

返回值:

  • 成功,返回新的文件描述符,该文件描述符用于与客户端通信;
  • 失败,返回-1,更新errno;

2.5 recv(接收数据)

功能:从指定的套接字文件描述符中,接收数据
原型:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数:

  • int sockfd:指定要从哪个文件描述符中获取数据,填accept函数获取到的新的文件描述符;

  • void *buf:该指针指向的内存空间中存储获取到的数据,可以是任意类型数据;

  • size_t len:指定要接收的数据大小,以字节为单位;

  • int flags:
    0:阻塞方式接收,当没有数据的时候阻塞;
    MSG_DONTWAIT:非阻塞;

返回值:

  • >0 , 成功,返回成功接收到的字节数;
  • =0, 对端关闭,返回0;
  • =-1, 函数运行失败,更新errno;

2.6 send(发送消息)

功能:通过套接字文件描述符,向指定套接字发送数据;
原型:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

参数:

  • int sockfd:指定要向哪个文件描述符发送数据,填accept函数获取到的新的文件描述符;

  • void *buf:指定要发送的数据首地址,可以是任意类型数据;

  • size_t len:指定要发送的数据大小,以字节为单位;

  • int flags:
    0:阻塞方式发送,当数据满的时候阻塞;
    MSG_DONTWAIT:非阻塞;

返回值:

  • 成功,返回成功发送的字节数;
  • 失败,返回-1,更新errno;

2.7 connect(连接服务器)

功能:通过服务器的IP和端口,连接指定服务器;
原型:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

  • int sockfd:指定要将哪个套接字与服务器连接;

  • struct sockaddr *addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定;
    指定要连接的服务器所绑定的IP和端口;

  • socklen_t addrlen:真实的地址信息结构体的大小:sizeof(struct sockaddr_in);

返回值:

  • 成功,返回0;
  • 失败,返回-1,更新errno;

二. UDP

1.UDP模型图

image-20230411115540104

2.UDP搭建相关函数

2.1 socket(创建套接字)(同上)

2.2 bind(绑定套接字)(同上)

bind报错:

  1. bind: Address already in use ===>端口号被占用
  2. bind: Cannot assign requested address ===》IP地址错误,不是本机IP

2.3 recvfrom(接受数据)

功能:从指定套接字中接收数据,同时可以接收到该数据包从哪个发送方发送过来的
原型:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 
				struct sockaddr *src_addr, socklen_t *addrlen);

参数:

  • int sockfd:指定要从哪个文件描述符中获取数据,填accept函数获取到的新的文件描述符;
  • void *buf:该指针指向的内存空间中存储获取到的数据,可以是任意类型数据;
  • size_t len:指定要接收的数据大小,以字节为单位;
    • int flags:
      0:阻塞方式接收,当没有数据的时候阻塞;
      MSG _DONTWAIT:非阻塞;
  • struct sockaddr *src_addr:通用地址信息结构体,该地址信息结构体中存储数据包是从谁那里发送过来的。
    如果不想知道从谁那里过来,则填NULL;
  • socklen_t *addrlen:真实的地址信息结构体的大小,注意是指针类型,需要在外部定义普通变量,赋值后取地址;
    如果上一个参数填NULL,则当前参数填NULL;

返回值:

  • >0 , 成功,返回成功接收到的字节数;
  • =0, 对端关闭,返回0; 只适用于TCP情况;
  • =-1, 函数运行失败,更新errno;

recv(sockfd, buf, len, flags); 相当于 recvfrom(sockfd, buf, len, flags, NULL, NULL);

2.4 sendto(发送数据)

功能:将数据发送到指定的套接字中,需要指定发给谁,即指定好接收方的地址信息结构体
原型:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
          const struct sockaddr *dest_addr, socklen_t addrlen);

参数:

  • int sockfd:指定要向哪个文件描述符发送数据,填accept函数获取到的新的文件描述符;

  • void *buf:指定要发送的数据首地址,可以是任意类型数据;

  • size_t len:指定要发送的数据大小,以字节为单位;

  • int flags:

    • 0:阻塞方式发送,当数据满的时候阻塞;
    • MSG_DONTWAIT:非阻塞;
  • const struct sockaddr *dest_addr:通用地址信息结构体,真实的地址信息结构体跟着地址族来定
    需要填充接收方的地址信息结构体,指定数据要发给谁;

  • socklen_t addrlen:真实的地址信息结构体的大小:sizeof(struct sockaddr_in);

返回值:

  • 成功,返回成功发送的字节数;
  • 失败,返回-1,更新errno;

send(sockfd, buf, len, flags); 相当于sendto(sockfd, buf, len, flags, NULL, 0);

3. UDP中的connect函数(重点!)

  1. udp中可以使用connect函数,但是不会产生连接

    ​ TCP中的connect函数会产生三次握手,将client和server连接。

    ​ UDP中的connect函数,仅仅是将对端的IP和端口记录到内核套接字空间中,此时是udp只能与记录的对端进行通信。

  2. TCP中的connect函数只能被调用一次。

    UDP中的connect函数可以被调用多次,刷新内核中对端的IP和端口,

    如果想要清空内核中对端的地址信息,则将sin_family

    成员设置为AF_UNSPEC,调用connect函数即可。

  3. 当udp采用connect函数的方式收发报文后,可以调用send write recv read函数。也可以使用sendto recvfrom.但是需要使用以下形式

    sendto(sfd, buf, sizeof(buf), 0, NULL, 0);
    recvfrom(sfd, buf, sizeof(buf), 0, NULL, NULL);
    

udp中的connect函数的优点:

  1. 提升传输效率

    1. 不调用connect: 将对端的信息填充到内核 —> 发送报文 —>清空内核信息 —>将对端的信息填充到内核 —> 发送报文 —>清空内核信息—>…
    2. 调用connect: 将对端的信息填充到内核 —> 发送报文 —> 发送报文 —> 发送报文—> 发送报文
  2. 增加传输时候的稳定性

    sendto(sfd, buf, sizeof(buf), 0, NULL, 0);
    recvfrom(sfd, buf, sizeof(buf), 0, NULL, NULL);
    

udp中的connect函数的优点:

  1. 提升传输效率

    1. 不调用connect: 将对端的信息填充到内核 —> 发送报文 —>清空内核信息 —>将对端的信息填充到内核 —> 发送报文 —>清空内核信息—>…
    2. 调用connect: 将对端的信息填充到内核 —> 发送报文 —> 发送报文 —> 发送报文—> 发送报文
  2. 增加传输时候的稳定性

    1. 可以防止AB进程在做大量数据传输过程中,接收到C进程的数据,造成数据错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值