网络核心概念
网络: 通过有线或无线的链路连接多个计算机设备进行数据通信或设备控制
介质:双绞线,同轴电缆,光纤
无线: WIFL ,移动网络,蓝牙,红外,NFC
设备: 计算机,交换机,路由器,IOT
在网络编程中,应用层协议编程是非常关键的一部分,因为它直接涉及到了如何在不同的应用程序之间交换数据。这里我们将详细讨论如何基于TCP和UDP在应用层进行编程,特别是以HTTP协议为例(HTTP是基于TCP的应用层协议)。
TCP/IP 应用层编程
1. TCP编程
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在TCP编程中,我们通常使用套接字(Socket)来实现网络通信。
TCP 客户端编程步骤:
- 创建套接字:使用
socket()
函数创建一个新的套接字。 - 连接到服务器:使用
connect()
函数将套接字连接到服务器。 - 发送和接收数据:使用
send()
和recv()
函数发送和接收数据。 - 关闭套接字:使用
close()
函数关闭套接字。
//client
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
int main(int argc, char const *argv[])
{
//创建套接字
//1. 域
//2. 套接字类型
//3. 协议
int socket_fd =socket(AF_INET,SOCK_STREAM,0);
if(socket_fd == -1)
{
printf("socket erro!\n");
return EXIT_FAILURE;
}
//连接服务器
//1. 套接字
//2. 目标地址
//3. 目标地址长度
struct sockaddr_in addr; //unix
addr.sin_family = AF_INET;
addr.sin_port = htons(9000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res =connect(socket_fd,(const struct sockaddr *)&addr,sizeof(addr));
if(res == -1)
{
printf("connect erro!\n");
return EXIT_FAILURE;
}
char ch[32] ;
for(int i=0;i<9;i++)
{
printf("pelese insert your words limit 31\n");
fgets(ch,32,stdin);
//去除字尾部符号
//读写数据
write(socket_fd,ch,sizeof(ch));
printf("已经发送数据%s\n",ch);
read(socket_fd,ch,sizeof(ch));
printf("接受到:%s\n",ch);
}
close(socket_fd);
return 0;
}
TCP 服务器编程步骤:
- 创建套接字:同样使用
socket()
函数。 - 绑定套接字:使用
bind()
函数将套接字绑定到一个特定的IP地址和端口上。 - 监听连接:使用
listen()
函数使套接字进入被动监听状态,等待客户端的连接请求。 - 接受连接:使用
accept()
函数接受客户端的连接请求,并为每个连接创建一个新的套接字。 - 发送和接收数据:使用
send()
和recv()
函数与客户端进行通信。 - 关闭套接字:关闭连接和套接字。
// server.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
// 创建套接字
int socket_server = socket(AF_INET, SOCK_STREAM, 0);
// 服务地址 端口号9000
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9000);
// 服务器
server_addr.sin_addr.s_addr = INADDR_ANY;
// 绑定地址
bind(socket_server, (struct sockaddr *)&server_addr, sizeof(server_addr));
// 监听
listen(socket_server, 5);
printf("服务器启动\n");
// struct sockaddr_un addr_client;
// addr_client.sun_family = AF_UNIX;
// socklen_t addr_len = sizeof(addr_client);
// 接收请求 可写死循环,或者其他条件
struct sockaddr_in addr_client;
addr_client.sin_family = AF_INET;
int addr_len = sizeof(addr_client);
// //子进程结束
signal(SIGCHLD, SIG_IGN);
while (1)
{
printf("服务器等待客户端请求\n");
// 接受请求,建立了TCP连接,获得了一个新的客户端套接字
int socket_client = accept(
socket_server,
(struct sockaddr *)&addr_client,
&addr_len);
if (socket_client == -1)
{
printf("连接失败\n");
return EXIT_FAILURE;
}
// 获得一个套接字标识符可以交给进程和线程灵活处理
// 并发
// 异步
// 开启多进程
if (fork() == 0)
{
int pid = getpid();
char buf[32];
for (size_t i = 0; i < 9; i++)
{
read(socket_client, buf, 32);
printf("%d 收到 %ld,%s\n", pid, i, buf);
char msg[32] = "bye tcp!";
write(socket_client, msg, 32);
}
// 进程退出
exit(EXIT_SUCCESS);
}
else
{
close(socket_client);
}
// 对请求做响应
// char buf[1024];
// read(socket_client,buf,sizeof(buf));
// //收到数据你可以查询数据库等操作
// printf("收到%s\n",buf);
// // strcpy(buf,"BYE TCP\n");
// write(socket_client,&buf,sizeof(buf));
}
return 0;
}
2. UDP编程
UDP(用户数据报协议)是一个简单的面向数据报的传输层协议。与TCP不同,UDP不提供可靠的数据传输服务,不保证数据包的顺序、到达或不重复。
UDP 客户端和服务器编程步骤类似:
- 创建套接字:使用
socket()
函数,但指定协议为UDP。int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } - int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
} - 绑定套接字(可选):对于服务器,使用
bind()
函数将套接字绑定到一个特定的IP地址和端口上。客户端通常不需要绑定。 - struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY; // 或者指定一个具体的IP地址
serv_addr.sin_port = htons(PORT); // PORT是服务器监听的端口号
if (bind(sockfd, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
} - 发送和接收数据:使用
sendto()
和recvfrom()
函数发送和接收数据。注意,由于UDP是无连接的,sendto()
和recvfrom()
函数都需要指定目标地址和端口(发送时)或来源地址和端口(接收时)。 - struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = inet_addr("目标IP地址");
client_addr.sin_port = htons(目标端口号);
ssize_t num_bytes_sent = sendto(sockfd, buffer, strlen(buffer), 0,
(const struct sockaddr *)&client_addr, sizeof(client_addr));
if (num_bytes_sent < 0) {
perror("sendto failed");
exit(EXIT_FAILURE);
} - 关闭套接字:使用
close()
函数关闭套接字。 - close(sockfd);
HTTP协议编程
HTTP(超文本传输协议)是基于TCP的应用层协议,用于在Web服务器和客户端之间传输超文本。在HTTP编程中,我们通常不直接实现HTTP协议,而是使用现成的库或框架来处理HTTP请求和响应。
HTTP 客户端编程:
- 使用HTTP客户端库(如Python的
requests
库)发送HTTP请求。 - 接收HTTP响应并处理响应内容。
HTTP 服务器编程:
- 使用Web服务器软件(如Apache, Nginx)或编写自定义的HTTP服务器。
- 解析HTTP请求,处理请求并生成HTTP响应。
- 发送HTTP响应给客户端。