目录
理解IP和端口号
socket编程接口
简单UDP网络小程序实现
本期我们将开始学习Linux计算机网络的相关知识。
理解IP和端口号
IP:一个IP唯一标识一个网络中的主机。
端口号:唯一标识一个主机中的一个进程。
IP+端口号我们也称作套接字。
所以我们可以得出一个结论,IP+端口号可以唯一标识一个计算机中的一个进程。
在学习Linux操作系统的时候我们也学习过进程的概念,在操作系统中我们使用pid来唯一标识计算机中的一个进程。那么套接字和进程pid冲突吗?
其实也不冲突,就如学生的身份证号和学生在学校的学号一样,都可以唯一标识学生的身份。那为什么在学校不用学生的身份证号作为学生的学号呢?其实这是为了解耦,如果真的使用身份证号作为学生的学号,那么万一有一天身份证号报废了,学生在学校的学号也就报废了。所以为了防止类似的情况出现,我们使用了学号,实现了学校与外界环境的解耦。在计算机网络中使用套接字也是类似的原因。
socket编程接口
// 创建 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);
在计算机网络通信中,socket通信方式有很多种,网络套接和域间套接,但是不难发现,在socket编程中只有唯一的接口。那么针对不同的socket通信方式怎么样保证只用上述唯一的接口就能实现通信呢?
回答这个问题之前,我们先来了解一下,不同通信方式所对应的数据结构。有三种数据结构,准确来说有两种数据结构。图示如下。
struct sockaddr_in 对应网络套接,struct sockaddr_un对应域间套接。这两个数据结构可以强转成struct sockaddr通用结构,进而使得多种socket通信方式使用同一套socket接口。
简单UDP网络小程序实现
题设:使用socket相关api接口,实现client客户端进程发送任意消息,sever服务器端进程接收到“你好之后”,给client客户端进程返回“hello”功能的程序。
客户端代码如下。
udp_client.cc
#include <iostream>
#include <sys/types.h>
#include <cerrno>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>int main(int argc, char *argv[])
{// 客户端// 1.创建套接字,打开网络文件int sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock < 0){std::cout << "socket create error" << errno << std::endl;return 1;}// 客户端需要绑定ip和端口吗// 不需要,因为客户端是向服务器端发送数据的,所以不需要,ip和端口号,// 发送数据时,操作系统自动绑定//2.使用服务while (1){// 给谁发struct sockaddr_in sever;sever.sin_family = AF_INET;sever.sin_port = htons(atoi(argv[2]));sever.sin_addr.s_addr = inet_addr(argv[1]);// 向服务器发送数据std::string message;std::cout<<"请输入# ";std::cin >> message;sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)&sever, sizeof(sever));// 获取从服务器中返回的数据struct sockaddr_in tmp;#define NUM 1024char buffer[NUM];socklen_t len = sizeof(tmp);recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&tmp, &len);std::cout << "sever返回的数据: " << buffer << std::endl;}return 0;
}
1. 客户端进程先创建并打开一个套接字文件。客户端不用去绑定IP和端口号,因为客户端是向服务器端发送数据的,所以在发送数据时操作系统会自动为客户端进程绑定IP和端口号。
2.客户端进程获取服务,接收到了服务器端进程返回的数据。
服务器端代码如下。
udp_sever.cc
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string>const uint16_t port = 8080;
int main()
{// 1.创建socket,即打开一个文件int sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock < 0){std::cout << "socket create erro" << errno << std::endl;return 1;}// 2.绑定IP和端口号struct sockaddr_in local;// 当前socket通信方式为网络套接。local.sin_family = AF_INET;local.sin_port = htons(port);// 绑定IP,需要将点分十进制IP转为32位整数IP地址,与此同时还要考虑大小端存储,// 所以使用inet_addr()接口即可// 但一般在服务器端,我们一般不显式绑定IP,因为在服务器端,可以认为一个服务器// 可以有多个IP,所以服务器可以接收所有向该主机IP传递的数据,而不是只接收// 给定的一个IP收到的数据local.sin_addr.s_addr = INADDR_ANY; // 默认接收向该主机所有IP传递的数据if (bind(sock, (const sockaddr *)&local, sizeof(local)) < 0){std::cout << "bind err" << errno << std::endl;return 2;}//提供服务#define NUM 1024char buffer[NUM];while(1){//1.接收从客户端发送的数据struct sockaddr_in peer;socklen_t len=sizeof(peer);recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);std::cout <<"#client发送的数据: "<<buffer << std::endl;//2.获取到数据之后,向客户端发送返回的数据std::string message="hello";sendto(sock,message.c_str(),message.size(),0,(struct sockaddr*)&peer,len);}return 0;
}
1.服务器进程创建并打开一个套接字文件。
2.服务器端进程需要绑定IP和端口号,因为服务器端是接收数据的,所以必须绑定IP和端口号,只有这样,客户端进程才知道往哪个服务器进程发送数据。
3.创建服务,接收客户端进程发送的数据,并向客户端进程发送返回的数据。
运行结果如下。
运行结果符合预期。
以上便是本期的所有内容。
本期内容到此结束^_^