一. 引入
Linux进程间通信包括多种机制,如管道、消息队列、信号、共享内存和信号量。这些机制都依赖于Linux内核提供的支持,用于实现不同进程之间的数据交换和同步。然而,这些通信方式在本地进程间通信中非常有用,但无法直接用于跨机器间的通信。
二. 网络通信
在网络通信中,通信的基本要素包括地址和数据,其中:
地址:IP地址用于标识设备的位置,端口号用于标识设备上运行的具体服务。
数据:通信数据需要遵循特定的协议,如HTTP、TCP、UDP等。不同协议有不同的特点和用途,适用于不同类型的通信需求。
Socket网络编程:Socket是实现网络通信的接口,允许进程通过网络与其他进程进行通信。在Socket编程中,常用的协议有TCP和UDP,分别适用于可靠连接和无连接通信。
三. 字节序
当数据在不同平台之间传输或交换时,需要考虑字节序的问题,以确保数据能够正确解释和处理。
1. litte-endian(x86系列CPU都是litte-endian的字节序):
将低序字节存储在起始位置
2. Big-endian(网络字节序=Big-endian):
将高序字节存储在起始位置
3. 例子:在内存中 0x01020304 的存储方式
内存地址 4000&4001&4002&4003
LE 04 03 02 01
BE 01 02 03 04
4.编程实现判断大小字节序
#include <stdio.h>
union Data{
char buf[4];
int num;
};
int main(){
union Data d1;
d1.num = 0x11223344;
printf("0x%x\n",d1.num);
printf("0x%x%x%x%x\n",d1.buf[0],d1.buf[1],d1.buf[2],d1.buf[3]);
return 0;
}
运行结果:
0x11223344
0x44332211
四. 网络编程API
1、socket()函数
用于创建网络套接字
#include<sys/types>
#include<sys/socket.h>
int socket(int domain, int type, int protocol); //网络类型
//数据协议
//一般为0,让系统配置适合前面俩个参数的协议
返回值:成功返回一个socket文件描述符,失败返回-1
2、 bind()函数
将给定的网络地址及端口号绑定到指定的socket套接字上。(注意:当我们调用函数把IP地址和端口号绑定到网络套接字之前,需要进行转化,如大小序转化)
#include<sys/types>
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
//addrlen: sizeof(struct sockaddr)的大小
my_addr: 一个指向strcut sockaddr的指针,根据socket不同的协议类型,其结构也不同。
我们一般使用另一个结构体sockaddr_in这个结构体。
cd /usr/include/ grep "struct sockaddr_in {" * -nir vi linux/in.h +184 :
#include <linux/in.h> //这俩个头文件存在一个即可 #include <netinet/in.h>
/* IPv4*/
struct sockaddr_in
{
uint16 sin_family; /* 网络类型,如IPV4区域网*/
uint16 sin_port; /* 端口号 */
uint32 sin_addr.s_addr; /* ID地址 */
unsigned char sin_zero[8]; /* 结构体占用内存大小 */
};
//注意:当我们调用函数把IP地址和端口号绑定到网络套接字之前,需要进行转化,如大小序转化
/* Ditto, for IPv6. */
struct sockaddr_in6
{
uint16 sin6_family; /* 网络类型*/
uint16 sin6_port; /* Transport layer port # */
uint32 sin6_flowinfo; /* IPv6 flow information */
uint8 sin6_addr[16]; /* IPv6 address */
uint32 sin6_scope_id; /* IPv6 scope-id */
};
返回值:成功返回0,失败返回-1
3、listen()函数
将socket套接字变为监听套接字,准备接受客户端的连接。
#include<sys/types>
#include<sys/socket.h>
int listen(int socket,int num);
//num:内核监听队列的最大长度。
返回值:执行成功返回0,失败返回-1
4. connect()函数
客户端主动发送连接请求给服务器。
#include<sys/types>
#include<sys/socket.h>
int connect(int socket,struct sockaddr* addr,sockelen_t len );
返回值:执行成功返回0,失败返回-1
5、accept()函数
服务器阻塞等待客户端的连接。
#include<sys/types>
#include<sys/socket.h>
int accept(int socket,struct sockaddr* addr,sockelen_t* len );
返回值:执行成功则返回一个新的socket套接字,我们可以利用这个套接字进行接下来的操作,使得旧的套接字继续执行别的客户端操作。
例子:
服务端Socket.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int main(){
//1.创建socket网络套接字
//int socket(int domain, int type, int protocol);
int s_fd = socket(AF_INET,SOCK_STREAM,0); //配置网络类型为为因特尔网
//配置数据传输的协议为TCP协议
//一般写0,根据前俩个配置的参数自动去选择合适的协议
if(s_fd == -1){
perror("socket");
exit(-1);
}
//2.绑定本地IP地址和端口号到socket网络套接字上
// int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
//我们一般不用第二个结构体,用的是下面这个结构体
// struct sockaddr_in{
// uint16 sin_family; /*网络类型*/
// uint16 sin_port; /* Port number. */
// uint32 sin_addr.s_addr; /* Internet address. */
// unsigned char sin_zero[8]; /* Pad to size of `struct sockaddr'. */
// };
struct sockaddr_in s_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8989); //端口号
inet_aton("192.168.190.130",&s_addr.sin_addr);
bind(s_fd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in));
//3.将socket套接字变为监听套接字,准备接受客户端的连接
//int listen(int socket,int num);
listen(s_fd,10); //最多接受10个用户的连接
//4.服务器阻塞等待客户端的连接。
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); addr客户端的信息
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
socklen_t len = sizeof(struct sockaddr_in);
int c_fd = accept(s_fd,(struct sockaddr*)&c_addr,&len);
if(c_fd == -1){
perror("accept"); //输出表示最近一次 socket() 系统调用的错误信息
exit(-1);
}
printf("Success Connected\n");
//打印客户端的IP地址
printf("get connect :%s\n",inet_ntoa(c_addr.sin_addr));
//5.读取客户端发送过来的消息
char readBuf[128];
int n_read = read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}else{
printf("get message:%d,%s\n",n_read,readBuf);
}
//6.向客户端发送一条消息
write(c_fd,"I get your message",sizeof("I get your message"));
return 0;
}
客户端User.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int main(){
//1.创建socket网络套接字
//int socket(int domain, int type, int protocol);
int c_fd = socket(AF_INET,SOCK_STREAM,0); //配置网络类型为为因特尔网
//配置数据传输的协议为TCP协议
//一般写0,根据前俩个配置的参数自动去选择合适的协议
if(c_fd == -1){
perror("socket");
exit(-1);
}
//2.连接服务端
//int connect(int socket,struct sockaddr* addr,sockelen_t len ); addr包含服务端的地址信息
struct sockaddr_in s_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET; //注意,这句话至关重要
inet_aton("192.168.190.130",&s_addr.sin_addr); //IP地址
s_addr.sin_port = htons(8989); //端口号
if(connect(c_fd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in)) == -1){
perror("connect");
exit(-1);
}
//打印服务端的IP地址
printf("get connect from Socket:%s\n",inet_ntoa(s_addr.sin_addr));
//3.发送信息到服务端
char *msg="msg from client";
write(c_fd,msg,strlen(msg));
//4.读取服务端发来的消息
char readBuf[128];
int n_read = read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}else{
printf("get message from Socket :%d,%s\n",n_read,readBuf);
}
return 0;
}