目录
一、网络编程的基础:
如果两个计算机想要通信,必须在同一个局域网。
在一个局域网之内,如果想要进行通信,那么这两者都必须要有唯一一个ID --- IP地址
IP地址:网络号 + 主机号
网络号:用来标识局域网
主机号:用来标识局域网里面的主机
子网掩码的功能是告知主机或路由设备,地址的哪一部分是网络号,包括子网的网络号部分,哪一部分是主机号部分。
在子网掩码中,网络部分和子网络部分对应的位全为“1”,主机部分对应的位全为“0”
11111111 11111111 11111111 11111111 -> 全是网络号,没有主机,因此没有什么作用
00000000 00000000 00000000 00000000 -> 全部是主机,没有网络,可以的,这是世界最大的网
子网掩码一定是有连续的1和连续的0组成
子网掩码里面的1必须全部相同,0所对应的必须不同
192.168.31.249 -> 点分式ip地址,.隔开的是一个字节
255.255.255.0
网络号:192.168.31
主机号:249255.255.255.0 对应的局域网能容纳的主机最多是:256个
.0 .1 .255 -> 这三个地址不能用 因此实际上行我们能用253个
网络程序= IP + 端口
端口号用于区分网络程序,现在是2个字节
0 ~ 1023 -> 知名端口
1024 ~ 49151 -> 注册端口
网络编程对于两端
1.服务器 --- 服务器
2.客户端 --- 客户端的应用程序
二、 两种通信
1. TCP
TCP --- 面向连接的,可靠通信,保证数据的无丢失无失序,有重发机制,也就是数据没有
成功到达,会重发
服务端:
1.创建套接字 --- 网络通信是一个文件,这个文件我们就叫套接字,是一个特殊的文件
描述符scokfd
2.我们需要将服务器的IP地址绑定到套接字
3.监听连接 --- 创建一个监听队列,建立5个10个可以了
4.循环等待客户端的连接,如果没有连接则等待,如果有连接则返回一个连接套接字
5.和客户端进行正常收发
6.关闭套接字
客户端
1.创建套接字
2.准备服务器的ip地址,准备连接
3.连接服务器
4.和服务器进行正常收发
5.关闭套接字
2.UDP
UDP --- 面对无连接的,通信是不可靠的,但是效率要高于TCP,
大量的实时不可靠通信我们就使用UDP
服务端
1.创建套接字
2.绑定地址
3.等待客户端给你发送信息
如果等待了客户端的信息,服务器会知道客户端的地址
根据刚刚得到的客户端的地址给客户端发送信息即可
4.关闭套接字客户端
1.创建套接字
2.准备服务器地址,准备给服务器发送信息
3.给服务器发送信息,然后可根据我们自己的项目逻辑来进行收发
4.关闭套接字
三、代码描述
1.创建套接字
NAME
socket - create an endpoint for communication --- 创建一个套接字
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>int socket(int domain, int type, int protocol);
domain:传输层的协议
AF_UNIX UNIX协议域,后面我们可以用这个玩意儿来实现进程间通信
AF_INET IPV4
AF_INET6 IPV6......
type:你采用那种协议进程数据传输
SOCK_STREAM 面向连接 -> TCP
SOCK_DGRAM 面向无连接 -> UDP
protocol:私有协议
我们直接写0 表示一个不知名的协议
返回值:
成功返回一个套接字,类似于文件描述符
失败返回-1,同时errno被设置
2.将一个名字和一个套接字绑定到一起
NAME
bind --- 将一个名字和一个套接字绑定到一起(赋一个名字给一个套接字)SYNOPSIS 概述
#include <sys/types.h>
#include <sys/socket.h>将 my_addr这个地址绑定到sockfd这个套接字上面去
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
sockfd:套接字 你要绑定到这个套接字上面来
my_addr:我们的地址
struct sockaddr这个地址是一个公版
根据需求我们自己要选用正确的类型
struct sockaddr_in //这个类型是面向网络的 因此我们需要选用这个类型{
sa_family_t sin_family; /* 地址族: AF_INET */
这里就是socket第一个参数的填法
u_int16_t sin_port; /* 按网络字节次序的端口 */
端口号 在注册端口里面选用一个即可
8888 === 0x22B8
-> 0xB822
struct in_addr sin_addr; /* internet地址 */
//这个就是我们ip地址
"192.168.31.249" -> 点分式
一个字符串给人看的
"192.168.31.249" -> 0xC0A81FF9
-> 网络字节序就是 0xF91FA8C0
};/* Internet地址. */
struct in_addr {
u_int32_t s_addr; /* 按网络字节次序的地址 */
//也就是我们的ip转换成一个整数了
};
addrlen:地址的长度 实际上就是struct sockaddr_in这个类型的长度
返回值:
成功返回0,失败返回-1,同时errno被设置
3.网络字节次序
这个是一个大端模式,如果直接把ip地址给它,不行,会变成反的
因此我们需要将小端模式转换成大端模式
这个是端口转换
SYNOPSIS
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);uint16_t htons(uint16_t hostshort); //h版本端口小端模式转网络字节序
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);//n版本网络字节序转端口小端
l:长 4个字节
s:短 2个字节
uint16_t htons(uint16_t hostshort);现在我们需要选用这个
4.IP地址函数转换
NAME
inet_aton, inet_addr, inet_network, inet_ntoa, inet_makeaddr,
inet_lnaof, inet_netof - Internet address manipulation routinesSYNOPSIS
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int inet_aton(const char *cp, struct in_addr *inp);
cp -> inp将在cp中存储的点分十进制字符串类型的IP地址,转换为二进制的IP地址,转换后的值保存在指针inp指向的结构struct in_addr中。
in_addr_t inet_addr(const char *cp);//如果是结构体里面用结构体 采用这个就可以了
cp -> 通过返回值返回in_addr_t它将参数cp所指向的字符串形式的IP地址(“192.168.1.11”)转换为二进制的网络字节序的IP地址形式
in_addr_t inet_network(const char *cp);//主机
字节序的二进制IPchar *inet_ntoa(struct in_addr in);//将得到的ip地址转换成点分式字符串
struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host);
将主机字节序的网络地址net和主机地址host合并成一个网络字节序的IP地址。
in_addr_t inet_lnaof(struct in_addr in);
从参数in中提取出主机地址,执行成功后返回主机字节顺序形式的主机地址。
5.在一个套接字上倾听连接
NAME 名称
listen - listen for connections on a socket 在一个套接字上倾听连接
创建一个监听队列,监听我们的连接
SYNOPSIS 概述
#include <sys/socket.h>int listen(int s, int backlog);
s:在哪个套接字上面进行监听
backlog:你需要创建多长的队列
成功返回0,失败返回-1,同时errno被设置
6.在一个套接字上接收一个连接
NAME 名称
accept - 在一个套接字上接收一个连接SYNOPSIS 概述
#include <sys/types.h>
#include <sys/socket.h>int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
s:套接字
addr:保存连接对方的ip地址,实际上就是客户端的
addrlen:地址的长度,一定要带长度进去
返回值:
返回一个连接套接字,服务器的通信是基于这个连接套接字的
失败返回-1,同时errno被设置
7.连接服务器
NAME
connect - initiate a connection on a socket
连接服务器
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd:套接字
addr:服务器的ip地址,你需要提前准备
addrlen:这个地址的长度
返回值:
成功返回0,失败返回-1,同时errno被设置
8. 从套接字接收信息
NAME
recv, recvfrom, recvmsg - receive a message from a socket
从套接字接收信息
TCP下面三个函数都可以用
但是UDP是无连接的,因此只能使用recvfrom
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
ssize_t read(int fd, void *buf, size_t count);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);
前面三个参数请参数read的
flags:一个标志 填0即可
src_addr:保存对方的ip地址
addrlen:保存对方的ip地址长度
返回值:
成功返回实际读到的字节数
失败返回-1,同时errno被设置
9.从套接字发送消息
NAME
send, sendto, sendmsg - 从套接字发送消息
发送信息
TCP下面三个函数都可以用
UDP只能使用sendto
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>ssize_t write(int fd, const void *buf, size_t count);
int send(int s, const void *msg, size_t len, int flags);
int sendto(int s, const void *msg, size_t len, int flags, const struct
sockaddr *to, socklen_t tolen);
flags:一个标志
to:对方的ip地址 你要把这个消息发送给谁
tolen:你的地址的长度
返回值:
成功返回实际发送的字节数
失败返回-1,同时errno被设置
10.关闭套接字
NAME
shutdown - shut down part of a full-duplex connection
关闭套接字
SYNOPSIS
#include <sys/socket.h>
int close(int fd);//关闭套接字的读写
int shutdown(int sockfd, int how);
sockfd:套接字
how:怎么关闭
SHUT_RD 关闭读
SHUT_WR 关闭写
SHUT_RDWR 关闭读写