全文目录
- 端口号
- 网络字节序
- 网络套接字
- socketaddr结构
- socket常见API及参数
- UDP编程流程
- 服务端流程
- 客户端流程
- TCP编程流程
- 服务端流程
- 客户端流程
进行网络传输时,可以通过IP地址找到指定的机器,但是需要将数据传递给那个程序就需要端口号来决定了。
端口号(port)是传输层协议的内容,来告诉操作系统当前数据要交给哪个进程来处理
端口号与进程ID的关系:
一个进程可以绑定多个端口号,但是一个端口号只能绑定一个进程
源端口号和目的端口号:
传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 “数据是谁发的, 要发给谁”;
在计算机存储中有着大端和小端之分,网络数据流中也有着大小端之分。
网络数据流传输流程:
TCP/IP 协议中规定:网络数据流应采用大端字节序,即低地址高字节。
如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;
网络字节序和主机字节序转换函数:
关于inet_ntoa
:
inet_ntoa
这个函数返回了一个char*
,man手册上说, inet_ntoa
函数, 是把这个返回结果放到了静态存储区. 这个时候不需要我们手动进行释放。但是多次调用该函数可能导致最后一次调用的结果覆盖之前的结果。
套接字 = I P 地址 + 端口号 套接字 = IP地址 +端口号 套接字=IP地址+端口号
网络套接字允许应用程序通过网络与其他计算机或设备进行通信。它们在客户端和服务器之间建立起连接,并负责处理数据的发送和接收。网络套接字通常包含一个IP地址和一个端口号,它们用于标识网络上的特定应用程序或服务。
有了套接字就能准确无误地将数据从远端进程传递给指定的进程。
通过网络套接字,应用程序可以使用不同的协议进行通信,如TCP和UDP
TCP和UDP:
T C P ( T r a n s m i s s i o n C o n t r o l P r o t o c o l 传输控制协议 ) : TCP(Transmission Control Protocol 传输控制协议) : TCP(TransmissionControlProtocol传输控制协议):
U D P ( U s e r D a t a g r a m P r o t o c o l 用户数据报协议 ) : UDP(User Datagram Protocol 用户数据报协议): UDP(UserDatagramProtocol用户数据报协议):
对于不同的协议套接字的结构地址不同,但是底层都是使用同一个结构 sockaddr
,不同的类型使用socket API时需要转换为sockaddr
类型的指针。
一般使用IPV4进行通信,sockaddr_in
结构参数的初始化:
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
// 接受数据(UDP,客户端/服务端)
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
// 接受数据(TCP,客户端/服务端)
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
// 发送数据(UDP,客户端/服务端)
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
// 发送数据(UDP,客户端/服务端)
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
// 发送数据(TCP,客户端/服务端)
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数解释:
domain: 套接字的协议族,如 AF_INET 表示 Internet 协议族。
type: 套接字的类型,可以是 SOCK_STREAM(流式套接字)或 SOCK_DGRAM(数据报套接字)。
protocol: 指定要使用的协议。如果给定大于 0 的协议号,则使用指定的协议,否则使用默认协议。
sockaddr_in: 存储套接字地址的结构体,包含 IP 地址和端口号等信息。其中 sin_family字段表示协议族,sin_port 字段表示端口号,sin_addr 字段表示 IP 地址。
backlog: 可以连接的客户端数量,即在调用 listen() 函数后,队列中等待连接的最大数量。
sockfd: 套接字描述符,表示对套接字的引用。可以通过 socket() 函数创建,并在之后的操作中使用。
addr: 一个指向存有目标套接字地址的指针,通常为 sockaddr_in 结构体类型的指针。
addrlen: 目标套接字地址的长度,通常为 sizeof(struct sockaddr_in)。
buf: 存储消息内容的缓冲区。
len: 要发送或接收的消息的长度。
flags: 一些特殊的标志位,如 MSG_DONTWAIT、MSG_ERRQUEUE 等。一般设为0即可
#include <sys/socket.h>
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
INADDR_ANY
,表示接受所有发送到指定端口的数据。#include <arpa/inet.h>
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port); // 端口号
addr.sin_addr.s_addr = INADDR_ANY; // 本机 IP 地址
bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
#include <unistd.h>
const char* message = "Hello, World!";
ssize_t sent_bytes = sendto(sockfd, message, strlen(message), 0,
(struct sockaddr*)&dest_addr, sizeof(dest_addr));
char buffer[1024];
struct sockaddr_in src_addr;
socklen_t addrlen = sizeof(src_addr);
ssize_t recv_bytes = recvfrom(sockfd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&src_addr, &addrlen);
#include <sys/socket.h>
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
#include <arpa/inet.h>
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port); // 服务器端口号
inet_pton(AF_INET, server_ip, &(server_addr.sin_addr));
const char* message = "Hello, Server!";
ssize_t sent_bytes = sendto(sockfd, message, strlen(message), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
char buffer[1024];
socklen_t addrlen = sizeof(server_addr);
ssize_t recv_bytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server_addr, &addrlen);
close(sockfd);
#include <sys/socket.h>
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
INADDR_ANY
,表示接受所有发送到指定端口的数据。#include <arpa/inet.h>
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port); // 端口号
addr.sin_addr.s_addr = INADDR_ANY; // 本机 IP 地址
bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
#include <sys/types.h>
#include <sys/socket.h>
int backlog = 5; // 最大同时连接数
listen(sockfd, backlog);
#include <sys/types.h>
#include <sys/socket.h>
struct sockaddr_in cli_addr;
socklen_t addrlen = sizeof(cli_addr);
int newsockfd = accept(sockfd, (struct sockaddr*)&cli_addr, &addrlen);
char buffer[1024];
ssize_t recv_bytes = recv(newsockfd, buffer, sizeof(buffer), 0);
const char* message = "Hello, Client!";
ssize_t sent_bytes = send(newsockfd, message, strlen(message), 0);
close(newsockfd);
close(sockfd);
在 TCP 客户端编程中,需要先创建套接字并设置服务器的地址和端口号,然后通过 connect()
函数与服务器建立连接。在连接建立之后,可以使用 send()
发送数据到服务器,使用 recv()
接收服务器返回的数据。最后,记得关闭连接和套接字,释放资源。
#include <sys/socket.h>
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
#include <arpa/inet.h>
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port); // 服务器端口号
inet_pton(AF_INET, server_ip, &(server_addr.sin_addr));
connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
const char* message = "Hello, Server!";
ssize_t sent_bytes = send(sockfd, message, strlen(message), 0);
char buffer[1024];
ssize_t recv_bytes = recv(sockfd, buffer, sizeof(buffer), 0);
close(sockfd);