赞
踩
嵌入式之路,贵在日常点滴
---阿杰在线送代码
目录
如何准确地定位网络上一台或多台主机以及定位主机上的特定的应用?
前面对于进程间通信我们讲了:管道、消息队列、共享内存、信号、信号量
这5种通信方式都是依靠Liunx内核。这也造成了他们的一个缺点就是,这些通信方式只能在单机运行。
那么如何实现多机通信呢?这就需要引入网络编程。
那么网络有哪些呢?
就从嵌入式出发,有Linux与Android、IOS、各种单片机,或者是Android对Android等等。
答:通过通信双方地址。地址由IP地址和端口号组成。
IP地址:负责定位主机。
端口号:负责定位主机上的特定应用。
一台拥有IP地址的主机可以提供许多服务,比如Web服务、 FTP服务、SMTP服务等。
这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。
实际上是通过"IP地址+端口号"来区分不同的服务的。
端口提供了一种访问通道,
服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21.每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件 传送协议)服务器的UDP端口号都是69
Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。
本质为内核借助缓冲区形成的伪文件。既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。
在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。
1、TCP(面向连接)双方必须连接成功才能发数据,类似打电话,主要用于对传输数据精确的情况,如传输指令。
2、UDP(面向报文)双方无须连接成功就能发数据,类似发短信,主要用于传输大数据量的情况,如视频。
1、TCP面向连接(如打电话要先拨号建立连接) ;UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多, 多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
大端字节序:将高序字节存储在起始地址,如:TCP/IP协议字节序
小端字节序:将低序字节存储在起始地址,如:X86系列CPU字节序
TCP/IP协议规定,网络数据流应采用大端字节序
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换:
- #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);
- //h表示host,n表示network,l表示32位长整数,s表示16位短整数。
- //如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
1、连接协议:(约法三章,确定协议)
原型:int socket(int domain, int type, int protocol);
(1)domain:通常是AF_INET,指IPV4因特网域
(2)type:指定socket类型,SOCK_STREAM它使用TCP协议,SOCK_DGRAM使用UDP协议
(3)protocol:0默认选择type对应的协议
创建成功返回涛姐号,创建失败返回-1.
2、连接地址
bind()函数,约定IP地址和端口号
原型:int bind(int sockfd, const struct sockaddr *maddr,socklen_t addrlen);
(1)sockfd:网络标识符
(2)struct sockaddr_in{
sa_family_t sin_family; //指定AF_***,表示使用什么协议族的ip格式
in_port_in sin_port; //设置端口号
struct in_addr sin_addr; //设置ip
unsigned char sin_zero[8];
};
(3)结构体大小
地址转换API
int inet_aton(const char* straddr,struct in_addr *addrp);
把字符串形式的“192.168.1.123”转为网络能识别的格式
char* inet_ntoa(struct in_addr inaddr);
把网络格式的ip地址转为字符串形式
3、监听
原型:int listen(int sockfd,int backlog)
(1)sockfd:网络连接号
(2)backlog:指定请求队列中同时最大请求数
4、连接
原型:int accept(int sockfd,struct sockaddr *addr,socklen_t addrlen)
(1)网络连接号
(2)客户端地址,NULL
(3)客户端地址长度,NULL(指针) 返回值是新建立的通道。
5、客户端用connect连接主机
connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
(1)目的服务器端口号
(2)服务器的IP地址和端口号的结构体
(3)地址长度
6、数据收发
socket编程步骤:
(1)创建套接字//类似内存空间
(2)为套接字添加信息(IP地址和端口号)
(3)监听网络连接
(4)监听到有客户端接入,接受一个连接
(5)数据交互
(6)关闭套接字,断开连接
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
-
- int main()
- {
- int s_fd;
- char readbuf[128];
- char* msg = "I Get Your message";
- struct sockaddr_in s_addr;
- struct sockaddr_in c_addr;
- memset(&s_addr,0,sizeof(struct sockaddr_in));
- memset(&c_addr,0,sizeof(struct sockaddr_in));
-
- //1.socket
- s_fd = socket(AF_INET,SOCK_STREAM,0);
- if(s_fd == -1)
- {
- perror("sccket");
- exit(-1);
- }
-
- //2.bind
- s_addr.sin_family = AF_INET;
- s_addr.sin_port = htons(8989);
- inet_aton("192.168.22.123",&s_addr.sin_addr);
- bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
-
- //3.listen
- listen(s_fd,10);
-
- //4.accept
- int clen;
- clen = sizeof(struct sockaddr_in);
- int c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
- //如果没有连接就会堵塞在这里
- if(c_fd == -1)
- {
- perror("accept");
- }
- printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
-
- //5.read
- int n_read = read(c_fd,readbuf,128);
- if(n_read == -1){
- perror("read");
- }else{
- printf("read msg:%d %s\n",n_read,readbuf);
- }
-
- //6.write
- write(c_fd,msg,strlen(msg));
-
- return 0;
- }
使用telnet编译调试服务端代码是否成功编写
运行结果
创建服务器,可同时连接10个,连接完成后要从这个地址读数据,无数据则堵塞,需要从命令行写数据,然后才读,读完以后,给个消息。
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
-
- int main()
- {
- int c_fd;
- char readbuf[128];
-
- char* msg = "msg from client";
- struct sockaddr_in c_addr;
- memset(&c_addr,0,sizeof(struct sockaddr_in));
-
- //1.socket
- c_fd = socket(AF_INET,SOCK_STREAM,0);
- if(c_fd == -1)
- {
- perror("sccket");
- exit(-1);
- }
-
- //2.connect
- c_addr.sin_family = AF_INET;
- c_addr.sin_port = htons(8989);
- inet_aton("192.168.22.123",&c_addr.sin_addr);
- //连接时会一直阻塞在这里,知道连接成功
- if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1)
- {
- perror("connect");
- exit(-1);
- }
-
- //3.send
- write(c_fd,msg,strlen(msg));
-
- //4.read
- int n_read = read(c_fd,readbuf,128);
-
- //4.read
- int n_read = read(c_fd,readbuf,128);
- if(n_read == -1){
- perror("read");
- }else{
- printf("read messasge from sever:%d %s\n",n_read,readbuf);
- }
-
- return 0;
- }
将服务端和客户端结合起来:
运行结果:
服务端
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
-
- int main(int argc,char **argv)
- {
- int s_fd;
- int c_fd;
- char readbuf[128] = {0};
- char msg[128] = {0};
- struct sockaddr_in s_addr;
- struct sockaddr_in c_addr;
- memset(&s_addr,0,sizeof(struct sockaddr_in));
- memset(&c_addr,0,sizeof(struct sockaddr_in));
-
- if(argc != 3){//参数判断
- printf("param is not good\n");
- exit(-1);
- }
-
- //1.socket
- s_fd = socket(AF_INET,SOCK_STREAM,0);
- if(s_fd == -1)
- {
- perror("sccket");
- exit(-1);
- }
-
- //2.bind
- s_addr.sin_family = AF_INET;
- s_addr.sin_port = htons(atoi(argv[2]));
- inet_aton(argv[1],&s_addr.sin_addr);
- bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
-
- //3.listen
- listen(s_fd,10);
-
- //4.accept
- int clen;
- clen = sizeof(struct sockaddr_in);
- //为实现双方聊天的关键点,死循环,一直执行在这里,不让程序退出,调用进程函数fork来实现聊天功能
- while(1){
- c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
- printf("sever c_fd:%d\n",c_fd);
- if(c_fd == -1)
- {
- perror("accept");
- }
- printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
-
- if(fork() == 0)
- {//fork() == 0子进程用来发
- if(fork()==0){
- while(1)
- {
- memset(msg,0,sizeof(msg));
- printf("please input:\n");
- gets(msg);
- write(c_fd,msg,strlen(msg));
- }
- }
- }
- while(1)//父进程while(1)用来读
- {
- memset(readbuf,0,sizeof(readbuf));
- int n_read = read(c_fd,readbuf,128);
- if(n_read == -1)
- {
- perror("read");
- exit(-1);
- }
- else
- {
- printf("read msg:%d %s\n",n_read,readbuf);
- }
- }
- }
-
- return 0;
- }
客户端
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
-
- int main(int argc,char **argv)
- {
- int c_fd;
- char readbuf[128] = {0};
- char msg[128] = {0};
- struct sockaddr_in c_addr;
- memset(&c_addr,0,sizeof(struct sockaddr_in));
-
- if(argc != 3){
- printf("param is not good\n");
- exit(-1);
- }
-
- //1.socket
- c_fd = socket(AF_INET,SOCK_STREAM,0);
- if(c_fd == -1)
- {
- perror("sccket");
- exit(-1);
- }
-
- //2.connect
- c_addr.sin_family = AF_INET;
- c_addr.sin_port = htons(atoi(argv[2]));
- inet_aton(argv[1],&c_addr.sin_addr);
- if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1)
- {
- perror("connect");
- exit(-1);
- }
- while(1)
- {
- if(fork() == 0)
- {//fork() == 0子进程用来发
- while(1)
- {
- memset(msg,0,sizeof(msg));
- printf("please input your msg:\n");
- gets(msg);
- write(c_fd,msg,strlen(msg));
- }
- }
- while(1)//父进程while(1)用来读
- {
- memset(readbuf,0,sizeof(readbuf));
- int n_read = read(c_fd,readbuf,128);
- if(n_read != -1)
- {
- printf("read messasge from sever:%d %s\n",n_read,readbuf);
- }
- }
- }
-
- return 0;
- }
运行结果:
(光用目前学过的父子进程来做 非常麻烦 不好实现 等后期进一步学习完再来补充这一块知识)
ping用于确定本地主机是否能与另一台主机成功交换(发送与接收)数据包,再根据返回的信息,就可以推断TCP/IP参数是否设置正确,以及运行是否正常、网络是否通畅等。ping用于确定本地主机是否能与另一台主机成功交换(发送与接收)数据包,再根据返回的信息,就可以推断TCP/IP参数是否设置正确,以及运行是否正常、网络是否通畅等。
如:常识用windows与Linux
Telnet协议是TCP/IP协议族中的一员,是Internet远程登录服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序,用它连接到服务器。终端使用者可以在telnet程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个telnet会话,必须输入用户名和密码来登录服务器。Telnet是常用的远程控制Web服务器的方法。
windows底下telnet遇到如下问题解决方法:
在include地下搜索struct sockaddr_in结构体
进入头文件的目录:cd /usr/include/
寻找包含该内容的头文件和行号
grep “struct sockaddr_in” * -nir(r是递归的意思,n是找出行号,i是不区分大小写)
可以搜索这个结构体定义的地方 vi linux/in.h +184找到结构的体定义初始位置。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。