UDP编程
基本概念:
- UDP (User Datagram Protocol) 是一种无连接的协议。与TCP不同,它不保证消息的到达顺序,也不保证消息的完整性。UDP适用于对速度要求较高、容忍少量数据丢失的应用场景,如视频流、语音通话等。
UDP服务器端编程流程:
- 创建Socket : 使用
socket()
函数创建一个UDP套接字。 - 绑定Socket : 使用
bind()
函数将套接字绑定到一个指定的IP地址和端口号。 - 接收数据 : 使用
recvfrom()
函数接收客户端发送的数据包。 - 发送数据 : 使用
sendto()
函数将数据发送回客户端。 - 关闭Socket : 使用
close()
函数关闭套接字。
UDP客户端编程流程:
- 创建Socket : 使用
socket()
函数创建一个UDP套接字。 - 发送数据 : 使用
sendto()
函数向服务器发送数据包。 - 接收数据 : 使用
recvfrom()
函数接收服务器返回的数据包。 - 关闭Socket : 使用
close()
函数关闭套接字。
形象化描述:
这是一个邮局系统:
- 客户端就像一个寄信人,使用邮局(socket)将包裹(数据)发送到一个指定的地址(服务器的IP和端口)。
- 服务器端就像一个收信人,它会收到包裹(数据),然后可能会给寄信人回信(返回数据)。
- 无连接的特点可以理解为寄信时并不需要先打电话告知邮局需要寄信(与TCP不同),直接寄送即可,但可能包裹(数据)在路上会丢失或顺序错乱。
步骤:
-
启动服务器端:
- 一个信箱代表服务器端,标有IP地址和端口号。
- 过程:服务器创建一个Socket,并绑定到信箱的地址。
-
客户端发送数据:
- 一个寄信人写了一封信(数据包),并把信投入邮局。
- 过程:客户端创建Socket,并通过邮局将信件发送到服务器端的信箱。
-
服务器端接收数据:
- 信箱接收到信件,服务器端读取信件内容。
- 过程:服务器端通过绑定的Socket接收到数据包,并处理或读取其中的数据。
-
服务器端发送回应:
- 服务器端写了回信,并将回信投入邮局寄给客户端。
- 过程:服务器端通过Socket将数据包返回给客户端。
-
客户端接收回应:
- 寄信人收到了回信。
- 过程:客户端通过Socket接收到服务器返回的数据包。
1. socket()
函数
- 作用: 创建一个新的套接字。
- 原型 :
int socket(int domain, int type, int protocol);
- 参数解释 :
domain
: 指定通信的协议族,例如AF_INET
表示IPv4网络协议,AF_INET6
表示IPv6网络协议。type
: 指定套接字的类型,SOCK_DGRAM
表示数据报套接字(用于UDP)。protocol
: 通常为0,表示使用默认协议,UDP的协议号为IPPROTO_UDP
。
2. bind()
函数
- 作用: 将套接字绑定到一个本地地址和端口上。
- 原型 :
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 参数解释 :
sockfd
: 由socket()
函数返回的套接字描述符。addr
: 指向struct sockaddr
结构体的指针,包含要绑定的本地IP地址和端口号。addrlen
:addr
结构体的大小(字节数)。
3. sendto()
函数
- 作用: 通过UDP将数据发送到指定的地址。
- 原型 :
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
- 参数解释 :
sockfd
: 由socket()
函数返回的套接字描述符。buf
: 指向包含要发送数据的缓冲区。len
: 缓冲区中数据的长度(字节数)。flags
: 一般设置为0,表示不使用任何特殊标志。dest_addr
: 指向struct sockaddr
结构体的指针,包含目标地址和端口号。addrlen
:dest_addr
结构体的大小(字节数)。
4. recvfrom()
函数
- 作用: 接收来自UDP的数据包,并获取发送方的地址信息。
- 原型 :
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
- 参数解释 :
sockfd
: 由socket()
函数返回的套接字描述符。buf
: 指向用于存放接收数据的缓冲区。len
: 缓冲区的大小(字节数)。flags
: 一般设置为0,表示不使用任何特殊标志。src_addr
: 指向struct sockaddr
结构体的指针,用于存储发送方的地址信息。addrlen
: 指向存储src_addr
结构体大小的变量的指针。在函数返回时,变量会被更新为实际的地址大小。
5. close()
函数
- 作用: 关闭套接字,释放资源。
- 原型 :
int close(int sockfd);
- 参数解释 :
sockfd
: 由socket()
函数返回的套接字描述符。
结构体
cpp
struct sockaddr_in {
short sin_family; // 地址族 (AF_INET)
unsigned short sin_port; // 端口号 (使用 htons() 转换)
struct in_addr sin_addr; // IP地址
char sin_zero[8]; // 填充,使结构体大小与 struct sockaddr 一致
};
cpp
struct in_addr {
unsigned long s_addr; // 32位IPv4地址
};
TCP编程
基本概念:
- TCP 是一种面向连接的协议,确保数据包按照顺序到达,并且保证数据传输的可靠性。这意味着在TCP连接中,客户端和服务器之间必须先建立连接,然后才能传输数据。
TCP服务器端编程流程:
- 创建Socket : 使用
socket()
函数创建一个TCP套接字。 - 绑定Socket : 使用
bind()
函数将套接字绑定到一个指定的IP地址和端口号。 - 监听Socket : 使用
listen()
函数使套接字进入监听状态,等待客户端连接。 - 接受连接 : 使用
accept()
函数接受来自客户端的连接请求,创建一个新的套接字用于通信。 - 接收数据 : 使用
recv()
函数从连接的客户端接收数据。 - 发送数据 : 使用
send()
函数向客户端发送数据。 - 关闭Socket : 使用
close()
函数关闭套接字。
TCP客户端编程流程:
- 创建Socket : 使用
socket()
函数创建一个TCP套接字。 - 连接服务器 : 使用
connect()
函数向服务器发起连接请求。 - 发送数据 : 使用
send()
函数向服务器发送数据。 - 接收数据 : 使用
recv()
函数接收服务器返回的数据。 - 关闭Socket : 使用
close()
函数关闭套接字。
形象化展示的细节描述:
想象一下这是一个电话通信系统:
- 客户端就像一个打电话的人,首先需要通过电话交换机(socket)拨通服务器的电话号码(IP地址和端口),等待对方接听。
- 服务器端就像一个接电话的人,它会监听来电(listen),一旦接听后(accept),双方就可以在电话线上传递信息(数据)。
- 面向连接的特点可以理解为电话通信前必须先拨号建立连接,确认对方接听后才能交流,且对话内容是有序且可靠传输的。
步骤:
-
启动服务器端:
- 服务器端像一个等待电话来电的人,已经拿起电话,准备接听。
- 过程:服务器创建一个Socket,绑定到本地地址,并开始监听可能的来电。
-
客户端发起连接请求:
- 客户端拨通服务器的电话号码,并等待对方接听。
- 过程:客户端创建一个Socket,并通过
connect()
发起连接请求。
-
服务器端接受连接:
- 服务器端听到了电话铃声,并接起电话。
- 过程:服务器通过
accept()
函数接受连接请求,建立通信连接。
-
数据传输:
- 双方开始通过电话交流信息(数据),按顺序传递消息。
- 过程:客户端和服务器通过
send()
和recv()
函数进行数据的发送和接收。
-
关闭连接:
- 双方结束通话,挂断电话。
- 过程:客户端和服务器通过
close()
函数关闭各自的Socket,断开连接。
1. socket()
函数
原型 :int socket(int domain, int type, int protocol);
参数:
domain
:指定协议族。常用的有:AF_INET
:IPv4协议。AF_INET6
:IPv6协议。AF_UNIX
:本地通信。
type
:指定套接字类型。常用的有:SOCK_STREAM
:流式套接字,用于TCP。SOCK_DGRAM
:数据报套接字,用于UDP。
protocol
:指定具体协议。通常为0,表示自动选择合适的协议。
2. bind()
函数
原型 :int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd
:套接字描述符,由socket()
函数返回。addr
:指向包含本地地址的struct sockaddr
结构体的指针。该结构体包含地址族、IP地址和端口号。addrlen
:地址结构体的长度,通常使用sizeof(struct sockaddr_in)
。
3. listen()
函数
原型 :int listen(int sockfd, int backlog);
参数:
sockfd
:套接字描述符。backlog
:待处理连接队列的最大长度,表示系统可以挂起的最大连接请求数。
4. accept()
函数
原型 :int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd
:监听套接字描述符。addr
:指向存储客户端地址的struct sockaddr
结构体的指针。addrlen
:指向地址结构体长度的指针,函数返回时会更新为实际地址的长度。
5. connect()
函数
原型 :int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd
:套接字描述符。addr
:指向包含远程地址的struct sockaddr
结构体的指针。addrlen
:地址结构体的长度。
6. send()
函数
原型 :ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
sockfd
:套接字描述符。buf
:指向要发送的数据缓冲区的指针。len
:要发送的数据长度。flags
:发送标志,通常为0。
7. recv()
函数
原型 :ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数:
sockfd
:套接字描述符。buf
:指向接收数据缓冲区的指针。len
:接收数据的最大长度。flags
:接收标志,通常为0。
8. close()
函数
原型 :int close(int fd);
参数:
fd
:文件描述符,这里用于关闭套接字。