目录
一、网络编程套接字
1、一些概念
1.1源IP地址和目的IP地址
1.2端口号port
1.3TCP和UDP的性质
1.4网络字节序、IP地址类型转换、数据接收与发送函数、popen函数
2、UDP套接字
2.1UDP服务器创建流程
2.2UDP客户端创建流程
2.3创建socket套接字
2.4绑定套接字对应的IP地址、端口号
2.5客户端、服务器数据的接收与发送
3、TCP套接字
3.1TCP服务器创建流程
3.2TCP客户端创建流程
3.3创建socket套接字
3.4绑定套接字对应的IP地址、端口号
3.5服务器设置socket为监听状态
3.6服务器获取客户端连接请求
3.7客户端发起连接请求
UDP/TCP客户端、服务器代码可参考本人gitee
一、网络编程套接字
1、一些概念
1.1源IP地址和目的IP地址
源IP地址:发送主机的IP地址。
目的IP地址:接收主机的IP地址。
1.2端口号port
端口号是传输层的协议的内容。
1、端口号是一个2字节的整数;
2、端口号用于告诉操作系统,当前这个数据要交给哪一个进程处理;
3、IPA+portA就能标定发送的主机中的进程,IPB+portB就能标定接收数据的主机中的进程,实现进程间通信。(IP+port就是套接字)
4、一个端口号只能绑定一个进程,一个进程可以绑定多个端口号。
既然网络通信的双方本质都是进程,为什么不直接使用pid来标定网络通信双方通信进程的唯一性,而是使用端口号呢?1、解耦2、例如客户端需要找到服务器进程,因为进程每次创建销毁pid是会变的,而网络编程弄了个端口号,直接写死了是哪个进程,这样每次都能找到。
那么怎么找呢?操作系统内部维护了一张哈希表,key是端口号,value是进程PCB的地址。
1.3TCP和UDP的性质
UDP:
传输层协议、无连接、不可靠传输、面向数据报
TCP:
传输层协议、有连接、可靠传输、面向字节流
1.4网络字节序、IP地址类型转换、数据接收与发送函数、popen函数
1、TCP/IP协议规定,网络数据流应采用大端字节序,即低地址存高字节,不管这台主机是大端机还是小端机
2、如果当前发送主机是小端, 就需要先将数据转成大端; 反之直接发送即可(不管本机是大端还是小端,最好都转换一下,便于日后跨平台、跨主机的需要)
3、发送主机和接收主机都是按照低地址到高地址发送/接收数据;
可以使用以下库函数做网络字节序和主机字节序的转换:
BYTEORDER(3)
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
可以使用以下库函数对ip地址进行转换:
INET(3)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);//将字符串转uint32_t的同时转为网络字节序
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);//将32位IPv4地址(in_addr结构体)转换成点分十进制字符串形式的IP地址
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);
popen函数==pipe+fork+exec*
POPEN(3)
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
用途:函数popen()用于打开一个用于读取或写入的管道,以便与另一个进程进行通信。
参数:
- command:要执行的命令
- type:打开管道的模式,可以是“r”(读)或“w”(写)。
返回值:返回一个指向FILE结构的指针,可以像使用普通文件一样使用它来读取或写入管道。如果打开管道失败,返回NULL
2、UDP套接字
UDP套接字种类及接口
1、网络套接字2、原始套接字3、unix域间套接字
2.1UDP服务器创建流程
1、创建套接字(sockrt)
2、绑定端口号和IP地址。这个端口号是写死的,这个IP地址0.0.0.0或htonl(INADDR_ANY)两种写法(bind)
3、发送、接收数据,对数据进行处理(recvfrom/sendto)
2.2UDP客户端创建流程
1、创建套接字(sockrt)
2、客户端需要bind,但是客户端的绑定不需要我们自己写,操作系统会去绑定;(无需程序员bind)
3、发送、接收数据,对数据进行处理(recvfrom/sendto)
相关指令及函数:
ifconfig 显示网络设备信息,找到127.0.0.1本地回环地址
ps axj | grep udpServer过滤出当前的udpServer进程
netstat -nuap//n:能显示数字的现实成数字;u:udp;a:all;p:进程
sudo netstat -nuap//n:以数字形式显示地址和端口号,不进行反向解析,a:all,u::udp,p:process ID/name,
sudo netstat -altp//l::listen,t:tcp
recvfrom
sendto
close
bzero
2.3创建socket套接字
SOCKET(2)
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
用途:创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
参数:
- domain:指定套接字的协议族。常见的协议族有AF_INET(IPv4网络通信)、AF_UNIX/AF_LOCAL(本地通信)
- type:指定套接字的类型。常见的类型有SOCK_STREAM(流套接字)和SOCK_DGRAM(数据报套接字)。
- protocol:给0就行。【指定套接字使用的协议。常见的协议有IPPROTO_TCP(TCP协议)和IPPROTO_UDP(UDP协议)】
调用成功返回值:返回一个文件描述符;
调用失败返回值:返回-1,设置error变量以指示原因。
2.4绑定套接字对应的IP地址、端口号
BIND(2)
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
用途:将一个套接字(socket)与一个特定的IP地址和端口号绑定
参数:
- sockfd:套接字描述符,即要绑定的套接字。
- addr:一个指向 sockaddr 结构体的指针,该结构体包含了IP地址和端口号等信息。
- addrlen:sockaddr 结构体的大小
返回值:返回值为0,则表示函数执行成功;否则,返回值为-1,表示函数执行失败。在函数执行失败时,可以通过 errno 全局变量获取错误码,以便进行错误处理。
2.5客户端、服务器数据的接收与发送
UDP 套接字是无连接协议,必须使用recvfrom函数接收数据,sendto函数发送数据:
RECV(2)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
用途:从已连接的socket中接收数据,并将数据存储到指定的缓冲区中。
参数:
- sockfd:已经建立好连接的socket。
- buf:指向接收数据存放的缓冲区。
- len:缓冲区长度。
- flags:一般为0,阻塞式读取。
- src_addr:(输出参数)返回发送方的地址信息。
- addrlen:(输出参数)地址信息的长度。
调用成功返回值:函数成功接收到数据时,它会返回接收到的字节数
调用失败返回值:发生错误,则返回-1,并设置errno变量以指示错误类型。
SEND(2)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
用途:该函数用于将数据报发送到指定的目的地。
参数:
- sockfd:表示要发送数据的套接字文件描述符。
- buf:指向要发送的数据缓冲区。
- len:表示要发送的数据长度。
- flags:表示发送数据的选项,常用的选项有MSG_DONTWAIT和MSG_NOSIGNAL。
- dest_addr:(输入参数)指向目的地址的结构体指针,表示要发给谁。
- addrlen:(输入参数)表示目的地址结构体的长度。
返回值:成功发送的字节数,如果返回值为-1,则表示发送失败,具体错误码可以通过errno变量获取。
3、TCP套接字
3.1TCP服务器创建流程
1、创建监听套接字(socket)
2、绑定端口号和IP地址。这个端口号是写死的,这个IP地址0.0.0.0或htonl(INADDR_ANY)两种写法(bind)
3、服务器设置socket为监听状态(listen)
4、服务器获取客户端连接请求(accept)
5、文件操作进行读写通信(read/write)
3.2TCP客户端创建流程
1、创建套接字(socket)
2、客户端需要bind,但是客户端的绑定不需要我们自己写,操作系统会去绑定;(无需程序员bind)
3、客户端发起连接请求(connect)
4、文件操作进行读写通信(read/write)
3.3创建socket套接字
同UDP创建套接字的方法,只不过在传参时UDP传入SOCK_DGRAM(数据报),而TCP传入SOCK_STREAM(字节流)
_sockfd=socket(AF_INET,SOCK_DGRAM,0);//UDP网络通信+数据报
_listenfd=socket(AF_INET,SOCK_STREAM,0);//TCP网络通信+字节流
3.4绑定套接字对应的IP地址、端口号
同UDP。
3.5服务器设置socket为监听状态
因为TCP是有连接的协议,需要使用listen函数将一个套接字设置为监听状态。
LISTEN(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
用途: listen函数用于将一个套接字(socket)设置为监听模式,以便接受客户端的连接请求。
参数:
- sockfd:需要设置为监听模式的套接字描述符
- backlog:指定等待连接队列的最大长度,即同时能够处理的客户端连接请求的最大数量,超过这个数量的连接请求将被拒绝
调用成功返回值:成功返回0
调用失败返回值:失败返回-1,并设置errno变量以指示错误类型。
3.6服务器获取客户端连接请求
ACCEPT(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
用途:accept函数用于从处于监听状态的套接字队列中取出一个已经完成了三次握手的连接请求,并创建一个新的套接字用于与客户端进行通信。
参数:
- sockfd:处于监听状态的套接字描述符
- addr:指向一个sockaddr结构体的指针,用于存储客户端的地址信息
- addrlen:addr结构体的长度,需要在调用前初始化为sizeof(struct sockaddr)
调用成功返回值:返回一个新的套接字描述符,用于与客户端进行通信,这个新的套接字描述符是唯一的,只能用于与这个客户端进行通信。
调用失败返回值:失败返回-1,并设置errno变量以指示错误类型。
3.7客户端发起连接请求
CONNECT(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
用途:connect函数用于客户端与服务器建立连接,自动帮客户端的套接字与其ip、port进行绑定。
参数:
- sockfd:需要连接的套接字描述符。
- addr:指向目标地址的指针,包括目标计算机的IP地址和端口号。
- addrlen:addr结构体的长度,需要在调用前初始化为sizeof(struct sockaddr)
调用成功返回值:返回0
调用失败返回值:失败返回-1,并设置errno变量以指示错误类型。