网络编程-TCP
网络通信基础
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; // 地址族IPV4
addr.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; // 地址族IPV4
addr.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 10001
void* 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; // 地址簇IPV4
addr.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;
}