系统操作和维护
前面提到的进程间通信的几种方法:消息信号、管道管道、消息队列msg、共享内存shm、信号量sem。都只适用于一台主机上的进程间通信,那么如何实现两台计算机之间的进程间通信呢?那么,让我们来学习一下远程进程通信。
1远程过程通信
协议层为双方的主机通信进程分配“端口”和缓冲区,方便异地进程间的通信。
1.1TCP/IP协议
以下是OSI参考模型和TCP/IP参考模型之间的对应关系:
TCP/IP协议族
TCP/IP协议组大致分为三个部分:
1.互联网协议(IP)
2.传输控制协议和用户数据报协议
3.一组基于TCP和UDP的专门开发的应用程序。它们包括:TELNET、文件传输协议(FTP)、域名服务(dns)和简单邮件传输程序(SMTP)以及许多其他协议。
应用层协议
用于远程联接服务的标准协议或者实现此协议的软件(可为动词)
文件传输协议(FTP和TFTP)
简单文件传输协议(SMTP)
域名服务(DNS)和其他协议
2网络编程基础
套接字标准扩展到窗口套接字和unix套接字。
linux下的网络编程是通过socket接口实现的。
Socket不仅是一个特殊的IO,也是一个文件描述符。
一个完整的套接字有一个相关的描述(协议;本地地址;本地端口;远程地址;远程端口)。每个套接字都有一个由操作系统分配的本地唯一套接字编号。
2.1插座分类
流式套接字(SOCK_STREAM)
流式套接字可以提供可靠的、面向连接的通信流。它使用TCP协议。TCP保证了数据传输的正确性和顺序性。
数据报套接字(SOCK_DGRAM)
数据报套接字定义了无连接服务。数据通过独立的消息传输,这是无序的,不能保证可靠性和无错性。使用数据报协议UDP协议。
原装插座。
原始套接字允许直接访问IP或ICMP等底层协议,主要用于测试新网络协议的实现。
2.2编程过程
传输控制协议(Transmission Control Protocol)
用户数据报协议(User Datagram Protocol)
特定功能的用法是人。
2.2.1套接字地址结构
关注套接字地址结构:
#包含netinet/in.h
结构足球
{
无符号短sa _ family/*地址族,AF_xxx */
char sa _ data[14];/* 14字节的协议地址*/
};
sa_family的值,一般来说IPV4用的是“AF_INET”。
Sa_data包含了一些远程电脑的地址、端口、插座的数量,里面的数据混在一起。一般我们不使用这种结构,因为我们通常使用的地址是IP端口号。例如:IP192.168.159.2端口3306。这是记录地址的方法。所以一般使用下面的地址结构,知道数据类型是等价的,可以互相转换。
#包含netinet/in.h
struct sockaddr_in {
short int sin _ family/*互联网地址系列*/
无符号短整型sin _ port/*端口号*/
结构in _ addr sin _ addr/*互联网地址*/
无符号字符sin _ zero[8];/*添加0(与struct sockaddr大小相同)*/
};
字节序列转换
因为每台机器中变量的字节存储顺序不同(有的系统是高位在前,低位在后,有的系统是低位在前,高位在后),所以网络传输的数据必须是统一的顺序。因此,有必要为不同于内部字节表示顺序和网络字节顺序的机器转换数据。
htons()——“主机到网络短路”
主机字节顺序转换为网络字节顺序(无符号短整型为2字节)
htonl()—— "主机到网络的长度"
主机字节顺序转换为网络字节顺序(无符号长类型为4字节)
ntohs()—— "网络到主机短路"
网络字节顺序转换为主机字节顺序(无符号短整型为2字节)
ntohl()—— "网络到主机长"
网络字节顺序转换为主机字节顺序(无符号长类型为4字节)
地址格式转换
-linux提供了一个转换函数,将点格式的地址转换为长整数。
inet _地址()能够把一个用数字和点表示互联网协议(互联网协议)地址的字符串转换成一个无符号长整型。
inet_ntoa()能够把网络字节顺序转换为地址结构的数据。
2.2.4基本套接字调用
套接字()绑定()连接()
监听()接受()发送()
接收()发送到()关闭()
recvfrom() close() getsockopt()
setsockopt() getpeername()
getsockname() gethostbyname()
gethostbyaddr() getprotobyname()
fcntl()
练习1-TCP
传输控制协议(传输控制协议)连接,等待客户端输入,将内容发送给服务器,并获取客户端地址。
这里,getsocketname()表示获得本地(自己)的地址;
getpeername()表示获得连接上的客户端的地址(源互联网协议(互联网协议)地址)。
英国铁路公司
server.c
#包含sys/types.h
#包含sys/socket.h
#包含netinet/in.h //sockaddr_in
#包含标准视频
#包含字符串。h
int main()
{
int fd
内部客户端f
内部ret
pid _ t pid
int addrLen=0;
char AC buf[20]=;
struct sockaddr _ in addr={ 0 };//自己的地址
clientAddr={0}中的struct sockaddr _ in//连上的客户端的地址
//1.socket()
fd=socket(PF_INET,SOCK_STREAM,0);
如果(fd==-1)
{
佩罗(插座);
return-1;
}
//2.bind()
地址. sin _ family=AF _ INET
地址。sin _ port=htons(1234);
地址。sin _ addr。s _ addr=inet _ addr(192。168 .159 .6);
ret=bind(fd,(struct sockaddr *)addr,sizeof(struct sockaddr _ in));
if(ret==-1)
{
佩罗(绑定);
return-1;
}
//3.listen()
ret=listen(fd,10);
if(ret==-1)
{
佩罗(听);
return-1;
}
//4.阻塞等待接受()
clientfd=accept(fd,NULL,NULL);
if(clientfd==-1)
{
佩罗(接受);
return-1;
}
//获取客户端地址
addrLen=sizeof(struct sockaddr _ in);
ret=getpeername(clientfd,(struct sockaddr *)clientAddr,addrLen);
if(ret==-1)
{
perror(getpeername);
return-1;
}
printf(客户端登录\\nip: %s,port: %d\\\\n,inet_ntoa(clientAddr.sin_addr),ntohs(client addr。sin _ port));
//5.通信
while(1)
{
memset(acbuf,0,20);
if (read(clientfd,acbuf,20) 0)
{
printf(receive: %s\\\\n,acbuf);
}
}
//6.close()
关闭(FD);
返回0;
}
客户端。c
#包含sys/types.h
#包含sys/socket.h
#包含netinet/in.h //sockaddr_in
#包含标准视频
#包含字符串。h
int main()
{
int fd
内部ret
char AC buf[20]=;
struct sockaddr _ in serAddr={ 0 };
//1 .socket();
fd=socket(PF_INET,SOCK_STREAM,0);
如果(fd==-1)
{
佩罗(插座);
return-1;
}
//2.连接连接()服务器的地址
serAddr.sin _ family=AF _ INET
塞拉德。sin _ port=htons(1234);
塞拉德。sin _ addr。s _ addr=inet _ addr(192。168 .159 .6);
ret=connect(fd,(struct sockaddr *)serAddr,sizeof(struct sockaddr _ in));
if(ret==-1)
{
佩罗(连接);
return-1;
}
//3.通信
while(1)
{
printf(发送:);
fflush(stdout);
scanf(%s,acbuf);
if(strcmp(acbuf,exit)==0)
{
打破;
}
write(fd,acbuf,strlen(AC buf));
}
//4.close()
关闭(FD);
返回0;
}
运行结果:
做个改进,以上代码,只能一个客户端连接上。因为传输控制协议(传输控制协议)是基于点对点的,一个接受()对应一个连接().要想连接多个客户端,就得使用fork(),一个进程用来专门阻塞等待客户端的连接,一个用来处理与已连接上客户端的通信。
代码如下:
server.c
int main()
{
int fd
内部客户端f
内部ret
pid _ t pid
int addrLen=0;
char AC buf[20]=;
char client _ addr[100]=;
struct sockaddr _ in addr={ 0 };//自己的地址
clientAddr={0}中的struct sockaddr _ in//连上的客户端的地址
signal(SIGCHILD,SIG _ IGN);
//1.socket()
fd=socket(PF_INET,SOCK_STREAM,0);
如果(fd==-1)
{
佩罗(插座);
return-1;
}
//会出现没有活动的套接字仍然存在,会禁止绑定端口,出现错误:地址已在使用中。
//由传输控制协议(传输控制协议)套接字时间_等待引起,绑定返回EADDRINUSE,该状态会保留2-4分钟
//if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,reuse,sizeof(reuse)) 0)
//{
//perror(setsockopet错误\ \ \ \ n);
//return-1;
//}
//2.bind()
地址. sin _ family=AF _ INET
地址。sin _ port=htons(1234);
地址。sin _ addr。s _ addr=inet _ addr(192。168 .159 .6);
ret=bind(fd,(struct sockaddr *)addr,sizeof(struct sockaddr _ in));
if(ret==-1)
{
佩罗(绑定);
return-1;
}
//3.listen()
ret=listen(fd,10);
if(ret==-1)
{
佩罗(听);
return-1;
}
while(1)
{
//4.阻塞等待接受()
clientfd=accept(fd,NULL,NULL);
if(clientfd==-1)
{
佩罗(接受);
return-1;
}
PID=fork();//父进程负责继续监听等待,子进程父子与已连接客户端通信
如果(pid==-1)
{
佩罗(叉);
return-1;
}
if(pid==0) //子进程
{
//获取客户端地址
addrLen=sizeof(struct sockaddr _ in);
ret=getpeername(clientfd,(struct sockaddr *)clientAddr,addrLen);
if(ret==-1)
{
perror(getpeername);
return-1;
}
sprintf(客户端地址,ip: %s,端口:%d\\\\n,\\
inet_ntoa(clientAddr.sin_addr),ntohs(client addr。sin _ port));
printf(客户登录\\\ n % s \\\ n,client _ addr);
//5.通信
while(1)
{
memset(acbuf,0,20);
if (read(clientfd,acbuf,20)==0) //客户端退出
{
//结束相应的计算机网络服务器进程
close(clientfd);
退出(0);//僵尸进程
}
printf(从% s接收:% s \ \ \ \ n \ \ \ \ n,client_addr,acbuf);
}
}
else //父进程
{
//返回虽然,继续等待
}
}
//6.close()
关闭(FD);
返回0;
}
这里一定要注意,每结束一个客户端,一定要关掉相应的文件描述符,并且结束掉子进程(僵尸进程),不然,随着客户端的增加,进程数会越来越大
客户端。c
int main()
{
int fd
内部ret
内部地址
char AC buf[20]=;
struct sockaddr _ in serAddr={ 0 };
myAddr={0}中的结构sockaddr _ in
//1 .socket();
fd=socket(PF_INET,SOCK_STREAM,0);
如果(fd==-1)
{
佩罗(插座);
return-1;
}
//2.连接连接()服务器的地址
serAddr.sin _ family=AF _ INET
塞拉德。sin _ port=htons(1234);
塞拉德。sin _ addr。s _ addr=inet _ addr(192。168 .159 .6);
ret=connect(fd,(struct sockaddr *)serAddr,sizeof(struct sockaddr _ in));
if(ret==-1)
{
佩罗(连接);
return-1;
}
//获取自己的地址
addrLen=sizeof(struct sockaddr _ in);
ret=getsockname(fd,(struct sockaddr *)myAddr,addrLen);
if(ret==-1)
{
perror(getsockname);
return-1;
}
printf(客户端ip: %s,端口:%d\\\\n,\\
inet_ntoa(myAddr.sin_addr),ntohs(myaddr。sin _ port));
//3.通信
while(1)
{
printf(发送:);
fflush(stdout);
scanf(%s,acbuf);
if(strcmp(acbuf,exit)==0)
{
打破;
}
write(fd,acbuf,strlen(AC buf));
}
//4.close()
关闭(FD);
返回0;
}
运行结果:
练习2-UDP
使用用户数据报协议(用户数据报协议)连接,完成上述内容。但是发现,使用UDP,因为是面向无连接的,所以在没有收到或者发送包之前,是无法得知源互联网协议(互联网协议)地址的。
那用户数据报协议(用户数据报协议)如何知道客户端的互联网协议(互联网协议)地址和端口呢?
1、由客户端显示地高速服务器互联网协议(互联网协议)地址和端口,发消息。
2、隐式的。服务器从收到的包头中得到源互联网协议(互联网协议)和端口号。
server.c
int main()
{
int sockfd
内部ret
char AC buf[20]=;
char client _ addr[100]=;
struct sockaddr _ in addr={ 0 };
clientAddr={0}中的结构sockaddr _ in
int addrLen=sizeof(struct sockaddr _ in);
int reuse=0;
//1.socket()
sockfd=socket(PF_INET,SOCK_DGRAM,0);
if(sockfd==-1)
{
佩罗(插座);
return-1;
}
//2.bind()
地址. sin _ family=AF _ INET
地址。sin _ port=htons(1235);
地址。sin _ addr。s _ addr=inet _ addr(127。0 .0 .1);
ret=bind(sockfd,(struct sockaddr *)addr,addrLen);
if(ret==-1)
{
佩罗(绑定);
return-1;
}
//3.通信
while(1)
{
memset(acbuf,0,20);
if(recvfrom(sockfd,acbuf,100,0,(struct sockaddr *)clientAddr,addrLen)==-1)
{
perror(recvfrom);
return-1;
}
//收到客户端的数据包之后,就可以知道客户端地址
sprintf(客户端地址,ip: %s,端口:%d\\\\n,\\
inet_ntoa(clientAddr.sin_addr),ntohs(client addr。sin _ port));
printf(接收自%s: %s\\\\n,client_addr,acbuf);
}
//4 .关闭
关闭(sockfd);
返回0;
}
客户端。c
int main()
{
int sockfd
内部ret
int addrLen=sizeof(struct sockaddr _ in);
char AC buf[20]=;
struct sockaddr _ in serAddr={ 0 };
myAddr={0}中的结构sockaddr _ in
//1.socket()
sockfd=socket(PF_INET,SOCK_DGRAM,0);
if(sockfd==-1)
{
佩罗(插座);
return-1;
}
serAddr.sin _ family=AF _ INET
塞拉德。sin _ port=htons(1235);
塞拉德。sin _ addr。s _ addr=inet _ addr(127。0 .0 .1);
//2.通信
while(1)
{
printf(发送:);
fflush(stdout);
scanf(%s,acbuf);
if(strcmp(acbuf,exit)==0)
{
打破;
}
sendto(sockfd,acbuf,20,0,(struct sockaddr *)serAddr,addrLen);
//获取自己的地址
ret=getsockname(sockfd,(struct sockaddr *)myAddr,addrLen);
if(ret==-1)
{
perror(getsockname);
return-1;
}
printf(客户端ip: %s,端口:%d\\\\n\\\\n,\\
inet_ntoa(myAddr.sin_addr),ntohs(myaddr。sin _ port));
}
//3 .关闭
关闭(sockfd);
返回0;
}
运行结果:
会发现,此时是可以直接运行多个客户端的,因为,UDP是面向无连接的,可以是一对多,多对一,多对多的,只要客户端知道服务器地址,就可以连上。
英国铁路公司
Ps:本人理解有限,还未学习完,有错请指出。
更多关于云服务器,域名注册,虚拟主机的问题,请访问西部数码代理商官网:www.chenqinet.cn