赞
踩
目录
1、确保你的网卡里面有两个虚拟网卡,你的有线网卡的型号也在这里面看 在更改适配器里面看是否有VMnet1 VMnet8
如果两个计算机想要通信,那么这两台计算机必须在同一个局域网在一个局域网之内,如果想要进行通信,那么这两者都必须要有唯一一个ID --- IP地址
现在我们用的是IPV4 -> 4个字节来表示一台主机,由于这个IPV4资源有限,所以现在扩充到了IPV6
IPV4四个字节,如果一个局域网内将所有的ip分完,那么可以分配给2 ^ 32 个主机
在世界上面来说,局域网的数量也不少,在互联网上面是不同的局域网之内的用户在进行通信,因此我们去找到另外一台主机的时候,除了去区分这个主机以外,还需要区分局域网(局域网+主机)
因此我们的ip地址分为两个部分:网络号 + 主机号
网络号:用来标识我们的局域网
主机号:用来标识我们局域网里面的主机
对于一个ip地址来说,我需要去区分哪些是网络号,哪些是主机号
我们通过4个字节,前面用全部的1构成,后面用全部的0构成
只要我们设置的足够好,那么我就可以使用这些1和0来区分,你的ip地址上面**哪些是网络号,哪些是主机号,这个东西我们就叫子网掩码。
举个例子:
一个点分式ip地址为:192.168.31.249
子网掩码为: 255.255.255.0
---------------->网络号:192.168.31
---------------->主机号:249(注意:主机号一般不为0和255)
11111111 11111111 11111111 11111111 -> 全是网络号,没有主机,因此没有什么作用
00000000 00000000 00000000 00000000 -> 全部是主机,没有网络,可以的,这是世界最大的网
注意:掩码的左边部分一定要是全为1且中间不能有0出现
比方说将255.255.248.0转为二进制是 11111111.11111111.11111000.00000000,可以看到左边都是1,在1的中间没有0出现(0都在1的右边),这样就是一个有效的掩码。我们再来看254.255.248.0,转成二进制是 11111110.11111111.11111000.00000000,这不是一个正确有效的掩码,因为在1中间有一个0的存在。
由于一台电脑里面可能运行多个网络程序,但是他们对应的网卡都是一个,因此ip地址都是共用的,因此我们必须还要有一个机制去区分网络程序 ----- 端口,端口号用于去区分网络程序的,现在是2个字节
0 ~ 1023 -> 知名端口
1024 ~ 49151 -> 注册端口,随便用,只要不冲突就行了
后面一节是私有端口,暂时不要用
所以:网络程序=ip+端口
网络编程都是对应两端的
1 服务端 --- 服务器
2 客户端 --- 客户端的应用程序
客户端发起服务请求,服务器响应这个请求
我们的通信有两种
1 TCP --- 面向连接的,可靠通信,保证数据的无丢失无失序,有重发机制,也就是数据没有成功到达,会重发
2 UDP --- 面向无连接的,通信是不可靠的,但是效率要高于TCP,大量的实时不可靠通信我们就用UDP
服务端 -> tcp_server.c
1. 创建套接字 ---- 什么都是文件,因此你的网络通信也是一个文件,这个文件我们就叫套接字, 是一个特殊的文件描述符 sockfd
2. 我们需要将服务器的IP地址绑定到套接字
3. 监听连接 ---- 创建一个监听队列.建立5个10个可以了
4. 循环等待客户端的连接,如果没有连接则等待,如果有连接则返回一个连接套接字
5. 和客户端进行正常收发
6. 关闭套接字
客户端 -> tcp_client.c
1. 创建套接字
2. 准备服务器的ip地址,准备连接
3. 连接服务器
4. 和服务器进行正常收发
5. 关闭套接字
服务端 -> udp_server.c
1. 创建套接字
2. 绑定地址
3. 等待客户端给你发送信息
如果等待了客户端的信息,服务器会知道客户端的地址
根据刚刚得到的客户端的地址给客户端发送信息即可
4. 最后弄完了关闭套接字
客户端 udp_client.c
1. 创建套接字
2. 准备服务器地址,准备给服务器发送信息
3. 给服务器发送信息,然后可根据我们自己的项目逻辑来进行收发
4. 最后弄完了关闭套接字
- NAME
- socket - create an endpoint for communication
- 创建一个套接字
- SYNOPSIS
- #include <sys/types.h> /* See NOTES */
- #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被设置
- 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
struct sockaddr_in { //这个类型是面向网络的 因此我们需要选用这个类型 sa_family_t sin_family; /* 地址族: AF_INET */ u_int16_t sin_port; /* 按网络字节次序的端口 */ struct in_addr sin_addr; /* internet地址 */ };sin_family:这里就是socket第一个参数的填法
sin_port :端口号 在注册端口里面选用一个即可
sin_addr : ip地址
/* Internet地址. */
struct in_addr {
u_int32_t s_addr; /* 按网络字节次序的地址 */ };s_addr:可以看到ip是一个整数,和之前讲的点分式(如192.168.31.249) 不同,因为点分式只是为了更加方便人看,192.168.31.249转换成 整数就是0xC0A81FF9。那什么又是网络字节序呢?这与大端小端 字节序有关:
①大端字节序(Big Endian):最高有效位存于最低内存地址处, 最低有效位存于最高内存处;
②小端字节序(Little Endian):最高有效位存于最高内存地址,
最低有效位存于最低内存处。UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这 就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时, 发送的第一个字节是该数值在内存中的起始地址处对应的那个字节, 也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送 的第一个高位字节
所以:网络字节序就是大端字节序。
但是我们系统的本机字节序是小端字节序,因此需要对应的接口进 行转换addrlen: 地址的长度 实际上就是struct sockaddr_in这个类型的长度
返回值:成功返回0,失败返回-1,同时errno被设置
- SYNOPSIS
- #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 ;s------short;l----long。
htons()--"Host to Network Short"
htonl()--"Host to Network Long"
ntohs()--"Network to Host Short"
ntohl()--"Network to Host Long"
现在我们需要将主机字节序转换为网络字节序,选用短整型就可以,所以我们选用htons
上面有讲到点分式,网络字节序,主机字节序。这些都是ip在不同应用中的格式。
以ip地址127.0.0.1
为例:
第一步 127 . 0 . 0 . 1 把IP地址每一部分转换为8位的二进制数。
第二步 01111111 00000000 00000000 00000001 = 2130706433 (主机字节序)
然后把上面的四部分二进制数从右往左按部分重新排列,那就变为:
第三步 00000001 00000000 00000000 01111111 = 16777343 (网络字节序)
以下就是一些地址转换函数:
- SYNOPSIS
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
-
- int inet_aton(const char *cp, struct in_addr *inp);
- in_addr_t inet_addr(const char *cp);//如果是结构体里面用结构体 采用这个就可以了
- in_addr_t inet_network(const char *cp);
- char *inet_ntoa(struct in_addr in);//将得到的ip地址转换成点分式字符串
- struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host);
- in_addr_t inet_lnaof(struct in_addr in);
- NAME 名称
- listen - listen for connections on a socket 在一个套接字上倾听连接
- //创建一个监听队列,监听我们的连接
- SYNOPSIS 概述
- #include <sys/socket.h>
- int listen(int s, int backlog);
参数解释:
s:表示在哪个套接字上面进行监听
backlog:你需要创建多长的队列,在这里我们需求不大,队列5 -10就可以
返回值:成功返回0,失败返回-1,同时errno被设置
- 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被设置
- 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被设置
- 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);
TCP上面三个函数都可以用,但是UDP是无连接的,因此只能使用recvfrom
recvfrom参数解释:
前面三个参数请参数read的
flags:一个标志 填0即可
src_addr:保存对方的ip地址
addrlen:保存对方的ip地址长度
返回值:成功返回实际读到的字节数
失败返回-1,同时errno被设置
- #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);
TCP上面三个函数都可以用,UDP只能使用sendto
参数解释:flags:一个标志
to:对方的ip地址 你要把这个消息发送给谁
tolen:你的地址的长度
返回值:成功返回实际发送的字节数
失败返回-1,同时errno被设置
- SYNOPSIS
- #include <sys/socket.h>
- int close(int fd);//关闭套接字的读写
- int shutdown(int sockfd, int how);
参数解释:
sockfd:套接字
how:怎么关闭
SHUT_RD 关闭读
SHUT_WR 关闭写
SHUT_RDWR 关闭读写
进入更改适配器选项-> 右击你的有线网卡 -> 属性 -> TCP/IPV4 -> 使用下面ip地址 ->设置你的ip -> 确定
其中: 192.168.1.x -> 电脑
192.168.1.y -> 乌班图
192.168.1.z -> 开发板
x,y,z在10 ~ 250之间,不相同即可
子网掩码 255.255.255.0
网关:192.168.1.1
首先关闭ubuntu
以管理员方式打开VMware,不要打开乌班图(选中这个乌班图即可)
palyer版本 -> player -> 虚拟机设置 -> 网络适配器 -> 选择桥接模式 -> 适配适配器
-> 一定要选择你的有线网卡 -> 前面的√只打这一个 -> 确定 -> 开启这个乌班图
pro版本 -> 编辑 -> 虚拟网络编辑器 -> 桥接至 你的有线网卡(不要选择自动) -> 确定
开启乌班图
右上键电源那个地方点击 -> 有线连接 -> 有线关闭 -> 右边的齿轮点进去-> IPV4 -> 手动 -> 按照windows填的方式填(但是ip不能和window一样) -> 应用 -> 打开有线连接
在终端我们可以看一下自己的ip是否改好,ifconfig命令
可以ping我们的windows的ip
ping 192.168.1.x -> 没有看到time是多少的就是没有通
进入开发板的profile文件,写入 ifconfig eth0 192.168.1.z netmask 255.255.255.0 up(将192.168.1.z改成你自己设的具体的IP),这样开发板每次开机都会自己开网。
tcp_client.c
- //客户端
- #include <stdio.h>
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <pthread.h>
- #include <string.h>
- //全局的套接字
- int sockfd = -1;
-
- //将套接字创建好 并且绑定 监听
- //将ip地址和端口号传进来 端口号释放需要时间(轮询机制)
- void TcpInit(const char * ipaddr,unsigned short port)
- {
- //1 创建套接字 ---- 什么2都是文件,因此你的网络通信也是一个文件
- sockfd = socket(AF_INET,SOCK_STREAM,0);
- if(-1 == sockfd)
- {
- perror("server socket error");
- exit(1);//没有必要运行了
- }
- //2 我们需要将服务器的IP地址绑定到套接字
- struct sockaddr_in sa;
- sa.sin_family = AF_INET;//协议族
- sa.sin_port = htons(port);//端口号 内存是小端的 我们要转大端
- sa.sin_addr.s_addr = inet_addr(ipaddr);//将我们点分式的ip地址转换为一个大端整数
-
-
- //连接服务器
- int r = connect(sockfd,(struct sockaddr *)&sa,sizeof(sa));
- if(-1 == r)
- {
- perror("connect error");
- exit(2);
- }
-
- }
-
-
- //开始正常的收发
- void function(void)
- {
- char buf[1024] = {0};
- while(1)
- {
- printf("输入你要发送的信息(输入quit退出):");
- fflush(stdout);
- scanf("%s",buf);
- if(!strcmp(buf,"quit"))
- {
- break;
- }
- send(sockfd,buf,strlen(buf) + 1,0);
-
- recv(sockfd,buf,1024,0);
- printf("服务器回我的信息是:%s\n",buf);
-
- }
- }
-
-
- int main(int argc,char * argv[])
- {
- if(argc < 3)
- {
- printf("参数都不齐\n");
- return -1;
- }
- TcpInit(argv[1],atoi(argv[2]));
- function();
-
- close(sockfd);
- return 0;
- }
tcp_sever.c
- //服务器
- #include <stdio.h>
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <pthread.h>
- #include <string.h>
-
- //全局的套接字
- int sockfd = -1;
-
- //将套接字创建好 并且绑定 监听
- //将ip地址和端口号传进来 端口号释放需要时间(轮询机制)
- void TcpInit(const char * ipaddr,unsigned short port)
- {
- //1 创建套接字 ---- 神马都是文件,因此你的网络通信也是一个文件
- sockfd = socket(AF_INET,SOCK_STREAM,0);
- if(-1 == sockfd)
- {
- perror("server socket error");
- exit(1);//没有必要运行了
- }
- //2 我们需要将服务器的IP地址绑定到套接字
- struct sockaddr_in sa;
- sa.sin_family = AF_INET;//协议族
- sa.sin_port = htons(port);//端口号 内存是小端的 我们要转大端
- sa.sin_addr.s_addr = inet_addr(ipaddr);//将我们点分式的ip地址转换为一个大端整数
- //printf("ipaddr = %x port = %x\n",inet_addr(ipaddr),htons(port));
- int r = bind(sockfd,(struct sockaddr *)&sa,sizeof(sa));
- if(-1 == r)
- {
- perror("server bind error");
- exit(2);//没有必要运行了
- }
-
- //3 监听连接 ---- 创建一个监听队列 建立5个10个可以了
- listen(sockfd,5);
-
- }
-
- //专门用于去服务一个客户的线程
- void * ClinetFunction(void * arg)
- {
-
- pthread_detach(pthread_self());//将其分离
-
- int * accceptfd = (int *)arg;
- printf(" * accceptfd = %d\n", * accceptfd);
- char buf[1024] = {0};
-
- //你发什么信息过来 我就在这个信息之前加上一节 然后回发给你
- while(1)
- {
- char sendbuf[1024] = {"SB250->"};
- int r = recv(*accceptfd,buf,1024,0);//阻塞等待数据过来
- if(-1 == r)
- {
- perror("recv error");
- break;
- }
- else if(0 == r)//客户端已经断了
- {
- printf("对方断开连接了\n");
- break;
- }
- else//接收到信息了
- {
- printf("recv buf = %s\n",buf);
- strcat(sendbuf,buf);
- send(*accceptfd,sendbuf,strlen(sendbuf) + 1,0);
- }
- }
-
-
- close(*accceptfd);
- free(accceptfd);
-
- }
-
-
- //等待客户端的连接
- void waitconnect(void)
- {
- //我们要基于这个连接套接字去通信
- struct sockaddr_in sa;
- socklen_t addrlen = sizeof(sa);
- while(1)
- {
- printf("一直等待对方的连接.......\n");
- int * accceptfd = malloc(4);//避免释放 因此我们要动态内存分配才可以
- *accceptfd = accept(sockfd,(struct sockaddr *)&sa,&addrlen);
- printf("连接者为:%s %d\n",inet_ntoa(sa.sin_addr),ntohs(sa.sin_port));
- //开一个线程出去 让它去服务与我的连接
- pthread_t thread;
- if(pthread_create(&thread,NULL,ClinetFunction,(void *)accceptfd) != 0)
- {
- perror("create thread error");
- continue;
- }
- }
-
- close(sockfd);
- }
-
-
- //通过main函数的参数 我们将这个ip地址和端口给进去
- //./a.out 192.168.31.251 8888
- int main(int argc,char * argv[])
- {
- if(argc < 3)
- {
- printf("参数都不齐\n");
- return -1;
- }
- TcpInit(argv[1],atoi(argv[2]));
-
- waitconnect();
-
- return 0;
- }
参考博客:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。