针对于嵌入式软件杂乱的知识点总结起来,提供给读者学习复习对下述内容的强化。
目录
1.请问你有没有基于做过socket的开发?具体网络层的操作该怎么做?
2.请你来说一下socket编程中服务器端和客户端主要用到哪些函数?
3.请你讲述一下Socket编程的send() recv() accept() socket() 函数?
1.请问你有没有基于做过socket的开发?具体网络层的操作该怎么做?
在基于Socket的开发中,虽然我们通常工作在传输层(TCP/UDP),但是通过Socket的某些高级操作,我们可以处理一些网络层的任务,如IP路由、原始Socket操作等。开发过程中,主要关注如何设置Socket、管理连接、发送和接收数据等操作,而网络层的一些细节(如路由、IP管理)通常由操作系统的网络栈处理。
1.1.TCP协议操作流程
1.1.1.服务端
创建Socket
int server_fd = socket(AF_INET, SOCK_STREAM, 0); // AF_INET=IPv4, SOCK_STREAM=TCP
绑定IP和端口
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY; // 绑定所有网卡
addr.sin_port = htons(8080); // 端口字节序转换
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
监听连接
listen(server_fd, 5); // 5=等待队列长度
接受客户端连接
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
收发数据
char buffer[1024];
recv(client_fd, buffer, sizeof(buffer), 0); // 接收
send(client_fd, "Hello", 5, 0); // 发送
注意处理recv()
返回0
(客户端断开)或-1
(错误)。
关闭连接
close(client_fd); // 关闭客户端连接
close(server_fd); // 关闭服务端Socket
1.1.2.客户端
创建Socket并连接
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); // IP转换
connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
1.2.UDP协议操作流程
1.2.1.服务端
创建Socket并绑定
int sock = socket(AF_INET, SOCK_DGRAM, 0); // SOCK_DGRAM=UDP
bind(sock, (struct sockaddr*)&addr, sizeof(addr));
收发数据
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_len);
sendto(sock, "Response", 8, 0, (struct sockaddr*)&client_addr, client_len);
基础回显服务
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
int main() {
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(8080),
.sin_addr.s_addr = INADDR_ANY
};
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(server_fd, 5);
while (1) {
int client_fd = accept(server_fd, NULL, NULL);
char buffer[1024];
int len = recv(client_fd, buffer, sizeof(buffer), 0);
send(client_fd, buffer, len, 0);
close(client_fd);
}
return 0;
}
2.请你来说一下socket编程中服务器端和客户端主要用到哪些函数?
基于TCP的socket
1.服务器端程序
(1)、创建一个socket,用函数socket()
(2)、绑定IP地址、端口等信息到socket上,用函数bind()(3)、设置允许的最大连接数,用函数listen()
(4)、接收客户端上来的连接,用函数accept()
(5)、收发数据,用函数send()和recv(),或者read()和write()
(6)、关闭网络连接
2.客户端程序
(1)、创建一个socket,用函数socket()
(2)、设置要连接的对方的IP地址和端口等属性
(3)、连接服务器,用函数connect()
(4)、收发数据,用函数send()和recv(),或read()和write()
(5)、关闭网络连接
基于UDP的socket
1.服务器端流程
(1)、建立套接字文件描述符,使用函数socket(),生成套接字文件描述符。
(2)、设置服务器地址和侦听端口,初始化要绑定的网络地址结构。
(3)、绑定侦听端口,使用bind()函数,将套接字文件描述符和一个地址类型变量进行绑定。(4)、接收客户端的数据,使用recvfrom()函数接收客户端的网络数据(5)、向客户端发送数据,使用sendto()函数向服务器主机发送数据。(6)、关闭套接字,使用close()函数释放资源。UDP协议的客户端流程
2.客户端流程
(1)、建立套接字文件描述符,socket()。
(2)、设置服务器地址和端口,struct sockaddr。
(3)、向服务器发送数据,sendto()。
(4)、接收服务器的数据,recvfrom()。
(5)、关闭套接字,close()。
3.请你讲述一下Socket编程的send() recv() accept() socket() 函数?
Socket编程中的 socket()
, accept()
, send()
, recv()
是核心函数,分别用于创建套接字、接受连接、发送和接收数据。
socket():创建一个新的Socket对象,用于网络通信。
bind():将套接字与地址(IP和端口)绑定,通常用于服务器端。
listen():将套接字设置为监听状态,等待客户端连接请求。
accept():接收客户端的连接请求,并返回一个新的Socket对象与客户端进行通信。
send():通过已连接的Socket发送数据。
recv():从已连接的Socket接收数据。
1. socket() 函数
socket()
是创建一个新的Socket的函数,它用于在客户端或服务器端创建一个用于网络通信的端点。可以指定通信的协议族、套接字类型以及协议类型。
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
family:指定套接字的协议族,常见的有:
AF_INET
:表示IPv4协议。
AF_INET6
:表示IPv6协议。
AF_UNIX
:表示Unix域套接字,用于本地进程间通信。
type:指定套接字类型,常见的有:
SOCK_STREAM
:表示TCP连接(面向连接的流协议)。
SOCK_DGRAM
:表示UDP连接(无连接的报文传输协议)。
SOCK_RAW
:原始套接字,用于接触更底层的协议(例如ICMP、ARP等)。
proto:指定协议类型,通常为0,表示根据family
和type
自动选择合适的协议。
fileno:如果已经有一个文件描述符(FD),可以将其作为参数传递,通常不需要设置。
import socket
# 创建一个TCP套接字,IPv4协议
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2. bind() 函数
bind()
将套接字与本地地址(IP地址和端口号)绑定在一起,使得服务器端能够监听特定端口的连接请求。bind()
通常在服务器端使用。
socket.bind(address)
address:是一个元组,包含服务器的IP地址和端口号。通常使用('0.0.0.0', port)
来让服务器绑定到所有可用的网络接口,或使用('127.0.0.1', port)
来仅绑定到本地接口。
# 绑定到所有IP地址和端口8080
server_socket.bind(('0.0.0.0', 8080))
3. listen() 函数
listen()
用于将套接字设置为监听状态,使得服务器可以接受连接请求。它还指定了等待队列的最大长度,表示可以挂起的连接请求的数量。
socket.listen(backlog)
backlog:指定等待连接队列的大小,即最大能挂起的未完成连接请求数量。这个数字不能太小,否则可能会导致客户端连接失败。
# 监听最多5个连接请求
server_socket.listen(5)
4. accept() 函数
accept()
用于服务器端等待客户端的连接请求。当客户端发起连接时,服务器端会通过accept()
接受该连接。accept()
返回一个新的Socket对象和客户端的地址信息。
client_socket, client_address = server_socket.accept()
client_socket:返回一个新的Socket对象,用于与客户端进行数据交换。
client_address:返回客户端的IP地址和端口号,格式是(IP, port)
。
# 接受客户端的连接请求
client_socket, client_address = server_socket.accept()
print(f"Connection from {client_address}")
5. send() 函数
send()
用于向连接的套接字发送数据。对于TCP协议,数据将按照字节流形式发送到网络中。如果成功发送,返回实际发送的字节数;如果发送失败,返回-1。
socket.send(bytes)
bytes:要发送的数据,必须是字节类型(bytes
)。如果是字符串类型,需先编码为字节格式。
# 向客户端发送数据
client_socket.send(b"Hello, client!")
6. recv() 函数
recv()
用于接收从连接的套接字中传输的数据。它会阻塞直到收到数据或者连接关闭。通常在客户端和服务器之间通信时使用。
socket.recv(bufsize, flags=0)
bufsize:指定一次接收的最大字节数,通常设置为1024、2048等,表示接收的缓冲区大小。
flags:接收标志,通常设为0。可以用于设置特定的标志,比如控制接收的行为。
# 从客户端接收数据
data = client_socket.recv(1024)
print(f"Received data: {data.decode()}")