网络通信基础
1、网络通信的协议:TCP
、UDP
、IP
2、网络通信模型:七层模型、四层模型
3、网络通信理论:socket
、IP
、端口号、字节序
4、网络IO
模型:4种
5、网络超时处理
6、网络的广播、组播、单播
网络通信的特征
(局域网)不同设备在通信时,要求其IP
地址必须处于同一网段
网络通信协议
TCP\IP
(传输控制协议\因特网互联协议)
TCP
:主要是数据发送数据时,若出现数据发送失败,可控制进行重新发送
IP
:用于通信过程的IP
地址
UDP
(用户数据报协议)
UDP
:没有数据重发机制
TCP | UDP |
---|---|
可靠通信 | 不可靠通信 |
通信效率低 | 通信效率高 |
面向连接 | 面向非连接 |
网络通信模型
七层模型 | 四层模型 | ||
---|---|---|---|
应用层 | 领导口述一段话 | ||
表示层 | 秘书记录口述内容形成信件 | ||
会话层 | 将信件放在公司前台 | 应用层 | 领导自己写信放置前台 |
传输层 | 快递员从前台取件 | 传输层 | 快递员从前台取件 |
网络层 | 分拣快递理清发送地址 | 网络层 | 分拣快递理清发送地址 |
数据链路层 | 快递传输过程 | ||
物理层 | 将信件送至收件人 | 网络链路层 | 投送快递到收件人 |
网络通信理论
socket
:套接字,特殊的文件描述符,不允许使用open
打开
IP
:当前所用的IP
地址都是32位的点分式,用于区分设备
端口号:2字节的短整型(1~65535)自己写的程序中端口号一般设置大于10000即可,用于区分应用
字节序:在x86
体系下,操作系统一般是小端存储模式,对于网络通信一般是大端存储模式
网络通信高级
TCP通信流程
客户端 | 服务器 |
---|---|
保证有一台手机int cfd = socket | 保证有一台手机int sdf = socket |
拨打号码connect(cfd) | 需要绑定一个手机号bind(sfd) |
畅聊,相当于收发数据send(cfd) | 设定手机铃声,用于监视listen(sfd) |
挂断电话close(cfd) | 等待来电,等待客户端连接请求int cfd = accept(sfd) |
畅聊,相当于收发数据recv(cfd) | |
挂断电话close(cfd)close(sfd) |
//实现一个客户端给服务器发送 服务器负责接收
//head.h
#ifndef _HEAD_H
#define _HEAD_H#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>#endif
//服务端
// ./server 1277.0.0.1 10001
int main(int argc, char* argv[])
{// 参数1:代表IPV4// 参数2:代表TCP// 参数3:默认为0,一般不用// 返回值:返回服务端的套接字,失败返回-1// 1、创建套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);if(sfd < 0){perror("socket error");return -1;}// 知道创建操作完成printf("socket success\n");// 2、服务端绑定IP地址和端口号struct sockaddr_in addr;addr.sin_family = AF_INET; // 地址族IPV4addr.sin_port = atoi(argv[2]); // 设置端口号// 将字符串形式的IP地址直接转换成网络地址int ret = inet_pton(AF_INET, argv[1], (struct in_addr*)&addr.sin_addr);if(ret <= 0){perror("inet_pton error");return -1;}ret = bind(sfd, (struct sockaddr*)&addr, sizeof(addr));if(ret < 0){perror("bind error\n");return -1;}// 知道绑定操作完成printf("bind success\n");//3、实现监听操作ret = listen(sfd, 2);if(ret < 0){perror("listen error");return -1;}//知道监听完成printf("listen success\n");//4、等待客户端的连接请求struct sockaddr_in cld_addr; //出参:待会连接客户端的IP端口号int len = sizeof(cld_addr);int cfd = accept(sfd, (struct sockaddr*)&cld_addr, &len);if(cfd < 0){perror("accept error");return -1;}//知道连接完成printf("accept success!\n");//5、接收客户端的数据char buf[100];while(1){bzero(buf, sizeof(buf));recv(cfd, buf, sizeof(buf)-1, 0);if(strstr(buf, "quit") != NULL){break;}printf("from %d info is:%s\n", cfd, buf);}//6、关闭套接字close(cfd);close(sfd);printf("server closed!\n");return 0;
}//客户端
// ./client 127.0.0.1 10001
int main(int argc, char* argv[])
{// 1、创建套接字int cfd = socket(AF_INET, SOCK_STREAM, 0);if(cfd < 0){perror("socket error");return -1;}// 知道创建操作完成printf("socket success\n");// 2、向服务端发送连接申请struct sockaddr_in addr;addr.sin_family = AF_INET; // 地址族IPV4addr.sin_port = atoi(argv[2]); // 设置端口号// 将字符串形式的IP地址直接转换成网络地址int ret = inet_pton(AF_INET, argv[1], (struct in_addr*)&addr.sin_addr);if(ret <= 0){perror("inet_pton error");return -1;}ret = connect(cfd, (struct sockaddr*)&addr, sizeof(addr));if(ret < 0){perror("connect error\n");return -1;}// 知道绑定操作完成printf("connect success\n");//3、向服务端的数据char buf[100];while(1){bzero(buf, sizeof(buf));fgets(buf, sizeof(buf), stdin);send(cfd, buf, sizeof(buf)-1, 0);if(strstr(buf, "quit") != NULL){break;}}close(cfd);printf("client closed!\n");return 0;
}
//实现多个客户端给服务端发送请求
//服务端
// ./server 1277.0.0.1 10001void* handle_client(void* arg)
{int cfd = *((int*)arg);/* 5、接收客户端的数据 */char buf[100];while(1){bzero(buf, sizeof(buf));recv(cfd, buf, sizeof(buf)-1, 0);if (strstr(buf, "quit") != NULL){printf("client [%d] is quit!\n", cfd);close(cfd);pthread_exit(NULL);}if (strlen(buf) != 0){printf("from %d info is: %s\n", cfd, buf);}}}// ./server 127.0.0.1 10001
int main(int argc,char *argv[])
{/* 1、创建套接字 */int sfd = socket(AF_INET, SOCK_STREAM, 0);if (sfd < 0){perror("socket error");return -1;}printf("socket success!\n");/* 2、服务端绑定IP地址和端口号 */struct sockaddr_in addr;addr.sin_family = AF_INET; // 地址簇IPV4addr.sin_port = atoi(argv[2]); // 设置端口号// 将字符串形式的IP地址,直接转换为网络地址int ret = inet_pton(AF_INET, argv[1], (struct in_addr*)&addr.sin_addr);if (ret <= 0){perror("inet_pton error");return -1;}ret = bind(sfd, (struct sockaddr *)&addr, sizeof(addr));if (ret < 0){perror("bind error!\n");return -1;}printf("bind success!\n");/* 3、实现监听操作 */ret = listen(sfd, 5);if (ret < 0){perror("listen error!\n");return -1;}printf("listen success!\n");/* 4、等待客户端的连接请求 */struct sockaddr_in cld_addr; // 出参: 带回连接客户端的IP和端口号int len = sizeof(cld_addr);// 主循环,接受客户端连接int cfd;pthread_t thread_id;while (1) {cfd = accept(sfd, (struct sockaddr *)&cld_addr, &len);if (cfd < 0){// 当前客户端请求失败后,只报错,不能退出程序// 因为其他进程还要进行连接。perror("accept error!\n");}else{// 连接请求成功后,在多线程中进行读写操作printf("client socket = [%d]: accept success!\n", cfd);if (pthread_create(&thread_id, NULL, handle_client, (void*)&cfd) != 0) {// 如果线程创建失败表明无法与客户端实现数据通信,则将客户端的套接字关闭。printf("client socket = [%d]: could not create thread: %s\n", cfd, strerror(errno));close(cfd);}}// 分离线程,让线程自主运行pthread_detach(thread_id);}/* 6、关闭套接字 */// 这个一般执行不到,因为服务端需要一值运行close(cfd);close(sfd);printf("server closed!\n");return 0;
}