- 先前讲的进程间通信:管道、消息队列、共享内存、信号、信号量都是依赖Linux内核调度。(原理是用户层调用C库接口产生软中断使得程序切入内核态运行,由内核对应C库接口的系统调用system函数来操作底层硬件如:驱动、磁盘IO)依赖内核的通信导致只能在本机的不同进程间通信,没办法实现异机通信。所以得引入网络编程才可以实现不同计算机上的进程间通信。
- 网络编程两大核心要素:
2.1地址(在网络中找到对应的计算机)
地址又包含了:IP地址和端口号
-
-
- IP地址:标识哪一台设备(设备在网络中的楼号)
- 端口号:一台设备上会跑ftp/http/socket1/socket2/pubg吃鸡等多个服务程序。 端口号是帮助确定与ftp/http/socket1/socket2/pubg吃鸡中的哪一个服务程序通信。
-
pubg吃鸡的端口号是8880,就可以通过调用接口传参8880来与pubg吃鸡程序通 信。
2.2数据交流(找到对应的计算机后传输数据:聊天)
2.2.1要聊天就得讲一样的语言,双方要有一样通信协议(http/tcp/mpp)协议就是 数据格式。
- 入门级的网络编程:socket套接字网络编程(用tcp/mpp协议)
- socket套接字网络编程的两种数据协议
4.1 tcp协议:侧重点对点(面向连接),看重数据的准确性,精准拨号交流。比较可靠。
4.2 udp协议:侧重数据量。不看重数据的准确性。应用在数据量大且数据流快的场景。
4.3端口号选择5000-10000以上。
- 字节序有两种
5.1大端字节序==网络字节序(起始放低位)
5.2小端字节序==X86数据存储方式(起始放高位)
5.3字节序是指多字节数据在计算机内存中存储或网络传输时各字节的存储顺序。
5.4一个字节==8bit(位);一个字==两个字节==16bit(位);双字==四个字节==32bit(位);
5.5双字0x01 02 03 04;LE(小端存储):04 03 02 01;BE(大端存储):01 02 03 04
- socket服务器开发流程
- 创建套接字
//int socket(int domain, int type, int protocol);
其中int domain配为AF_INET表示互联网协议(TCP/IP族)
其中int type配为SOCK_STREAM表示TCP协议(配为SOCK_DGRAM表示UDP协议)
其中int protocol配为0表示内核默认以TCP协议来操作系统调用函数
int s_fd=socket(AF_INET,SOCK_STREAM,0);
- 为创建好的套接字添加信息(IP地址和端口号)
//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
其中int sockfd是socket返回的句柄标识符
其中 const struct sockaddr *addr是一个结构体指针,同等替换为struct sockaddr_in结构体,
配置结构体中.sin_family=AF_INET(表示互联网协议TCP/IP族);
配置结构体中.sin_port=htons(atoi(argv[2]))配置端口号
atoi(argv[2])函数表示把C语言文件的第三个参数字符转化成int型。
htons是一个把X86字节序转换成网络字节序的API。保证字节序格式的正确 性。否则传输的数据会乱码。
配置结构体中.sin_addr为inet_aton(argv[1],&saddr.sin_addr);配置IP地址
inet_aton函数把C语言文件的第二个参数转化成网络可以识别的形式放 入.sin_addr中
结构体中第四个成员可以不配置。
bind(s_fd,(struct sockaddr *)&saddr,sizeof(struct sockaddr));
- 监听网络连接
//int listen(int sockfd, int backlog);
其中int sockfd是socket返回的句柄标识符
其中int backlog是监听的设备个数可以是20
listen(s_fd,20);
- 监听到有客户端接入,接收一个连接
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
其中int sockfd是socket返回的句柄标识符
创建一个结构体struct sockaddr_in caddr;这个结构体是客户端与服务器数据交流的 数据容器
其中socklen_t *addrlen是结构体struct sockaddr_in的大小
c_fd=accept(s_fd,(struct sockaddr *)&caddr,&len);
- read/write读写操作
write(c_fd,msg,strlen(msg));
read(c_fd,Rbuf,1024);
其中c_fd是accept返回的句柄标识符
- 关闭套接字,断开连接
close(c_fd);
7.结构体struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family协议族*/
__be16 sin_port; /* Port number端口号*/
struct in_addr sin_addr; /* Internet address地址IP*/
/* Pad to size of `struct sockaddr'. 没有实际意义,只是为了与sockaddr格式对齐,不用配置*/
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
- 网络IP地址与字符串相转换函数
7.1 int inet_aton(const char *cp, struct in_addr *inp);//把字符串“192.168.0.1”转换为网 络可以识别的格式
7.2 char *inet_ntoa(struct in_addr in);//把网络IP地址转换为字符串“192.168.0.1”格式
8.端口号用的字节序转换函数(网络字节序与X86字节序相互转换)
8.1主机字节序转换->网络字节序(s表示短字节;常用/l表示长字节)
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
8.2网络字节序转换->主机字节序(s表示短字节;常用/l表示长字节)
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
9.服务器示例代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//int socket(int domain, int type, int protocol);
//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//int inet_aton(const char *cp, struct in_addr *inp);
//char *inet_ntoa(struct in_addr in);
//int listen(int sockfd, int backlog);
int main(int argc,char **argv)
{
int c_fd;
char Rbuf[1024]={0};
char msg[1024]={0};
int nr;
//1.socket
int s_fd=socket(AF_INET,SOCK_STREAM,0);
if(s_fd==-1)
{
perror("socket");
}
//2.bind
struct sockaddr_in saddr=
{
.sin_family=AF_INET,
.sin_port=htons(atoi(argv[2]))
};
inet_aton(argv[1],&saddr.sin_addr);
bind(s_fd,(struct sockaddr *)&saddr,sizeof(struct sockaddr));
//3.listen
listen(s_fd,20);
//4.accept
struct sockaddr_in caddr;
memset(&caddr,0,sizeof(struct sockaddr));
int len=sizeof(struct sockaddr);
while(1)
{
c_fd=accept(s_fd,(struct sockaddr *)&caddr,&len);
if(c_fd==-1)
{
perror("accept");
}else
{
puts("connect!");
}
printf("from %s\n",inet_ntoa(caddr.sin_addr));
if(fork()==0)
{
nr=read(c_fd,Rbuf,1024);
if(nr==-1)
{
perror("read");
}else
{
printf("read %d byte:%s\n",nr,Rbuf);
}
//5.read/write
if(fork()==0)
{
puts("please input!");
memset(msg,0,strlen(msg));
gets(msg);
write(c_fd,msg,strlen(msg));
}
}
}
//6.close
close(c_fd);
return 0;
}
- 客户端代码实现流程
A.创建套接字
//int socket(int domain, int type, int protocol);
其中int domain配为AF_INET表示互联网协议(TCP/IP族)
其中int type配为SOCK_STREAM表示TCP协议(配为SOCK_DGRAM表示UDP协议)
其中int protocol配为0表示内核默认以TCP协议来操作系统调用函数
int c_fd=socket(AF_INET,SOCK_STREAM,0);
B.连接服务器
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
其中int sockfd是socket返回的句柄标识符
其中 const struct sockaddr *addr是一个结构体指针,同等替换为struct sockaddr_in结构 体,配置结构体中.sin_family=AF_INET(表示互联网协议TCP/IP族);
配置结构体中.sin_port=htons(atoi(argv[2]))配置端口号
atoi(argv[2])函数表示把C语言文件的第三个参数字符转化成int型。
htons是一个把X86字节序转换成网络字节序的API。保证字节序格式的正确 性。 否则传输的数据会乱码。
配置结构体中.sin_addr为inet_aton(argv[1],&saddr.sin_addr);配置IP地址
inet_aton函数把C语言文件的第二个参数转化成网络可以识别的形式放 入.sin_addr中
结构体中第四个成员可以不配置。
connect(c_fd,(struct sockaddr *)&caddr,sizeof(struct sockaddr));
- read/write读写操作
write(c_fd,msg,strlen(msg));
read(c_fd,Rbuf,1024);
其中c_fd是accept返回的句柄标识符
D.关闭套接字,断开连接
close(c_fd);
11.客户端示例代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//int socket(int domain, int type, int protocol);
//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//int inet_aton(const char *cp, struct in_addr *inp);
//char *inet_ntoa(struct in_addr in);
//int listen(int sockfd, int backlog);
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
int main(int argc,char **argv)
{
char Rbuf[1024]={0};
int nr;
char msg[1024]={0};
//1.socket
int c_fd=socket(AF_INET,SOCK_STREAM,0);
if(c_fd==-1)
{
perror("socket");
}
//2.connect
struct sockaddr_in caddr=
{
.sin_family=AF_INET,
.sin_port=htons(atoi(argv[2]))
};
inet_aton(argv[1],&caddr.sin_addr);
connect(c_fd,(struct sockaddr *)&caddr,sizeof(struct sockaddr));
puts("connect!");
while(1)
{
if(fork()==0)
{
puts("please input!");
memset(msg,0,strlen(msg));
gets(msg);
write(c_fd,msg,strlen(msg));
}
//3.read/write
nr=read(c_fd,Rbuf,1024);
if(nr==-1)
{
perror("read");
}else
{
printf("read %d byte:%s\n",nr,Rbuf);
}
}
//6.close
close(c_fd);
return 0;
}