tcp/ip实现点对点通信(socket编程)

2024-03-11 17:32

本文主要是介绍tcp/ip实现点对点通信(socket编程),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我们日常的网络其实是由很多层来组成的,有硬件,有代码和一些约定好的通信协议组成还有我们使用的微信聊天,qq聊天等;其中分层主要有:OSI七层网络模型及TCP/IP的四层参考模型:

在这里插入图片描述

各层的主要作用及常用协议有:

(1)物理层:数据转换(把数字信号转为电信号)(arp,rarp)
(2)数据链路层:保证数据的正确传输(校验)
(3)网络层:寻找路由与确认主机(ip,icmp,igmp)
(4)传输层:按照规定协议来收发数据(tcp/ip,udp)
(5)会话层,表示层,应用层:这三层是自己通过socket编程,来设置客户端与主机间怎样去通信的网络编程的重点!!!


下面是关于Linux下socket编程的一些函数:

创建监听套接字函数:

//头文件
#include <sys/types.h>          
#include <sys/socket.h>//函数原型
int socket(int domain, int type, int protocol);//函数介绍
/****************************************************
函数作用:创建一个套接字,用于通信(socket翻译为插座,意为在通信协议与进程间通过一个API后便能进行通信)函数形参:domain:域(选择的通信协议)AF_INET/PF_INET:   网际协议,IPv4AF_INET6/PF_INET6: 网际协议,IPv6AF_UNIX/PF_UNIX: 本地协议,可写成AF_LOCAL/PF_LOCALtype:类型SOCK_STREAM:流式套接字,对应TCPSOCK_DGRAM:数据报套接字,对应UDPprotocol:协议(一般为0),因为主要的通信协议为tcp/ip或udp函数返回值:成功: 创建成功的套接字文件描述句柄失败: -1
***************************************************/


绑定套接字函数:

//头文件
#include <sys/types.h>        
#include <sys/socket.h>//函数原型
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//函数介绍
/****************************************************
函数作用:初始化默认端口及绑定套接字和地址函数形参:sockfd:使用socket函数申请成功的套接字文件句柄addr:初始化的端口及使用的协议和IP地址等(一般不使用struct sockaddr这个结构体,会使用struct sockaddr_in这个结构体)addrlen:addr的长度函数返回值:成功: 创建成功的套接字文件描述句柄失败: -1
***************************************************/
绑定端口及IP一般只在服务器上绑定,因为服务器是需要给很多个客户端来连接的,因此客户端可以不绑定,然后系统会给客户端分配一个空闲的端口等,可以在一定程度上减轻系统压力

由于struct sockaddr这个结构体是用于存放端口及IP等信息的结构体,但由于我们给它初始化时IP和端口及使用的协议都在sa_family里面,过于麻烦于是大牛们想了一个办法:用一个内存大小和这个结构体一样的来替换它,就是struct sockaddr_in这个结构体

struct sockaddr {sa_family_t sa_family;char        sa_data[14];}//===================用于替换的结构体=========================
特殊地址结构体 —— IPv4地址结构体:
struct sockaddr_in
{u_short sin_family; // 地址族u_short sin_port; // 端口struct in_addr sin_addr; // IPV4地址char sin_zero[8];
};struct in_addr
{
in_addr_t s_addr; // 无符号32位网络地址
};特殊地址结构体 —— IPv6地址结构体:
struct sockaddr_in6
{u_short sin6_family;    // 地址族__be16 sin6_port;      // 端口__be32 sin6_flowinfo; // 流信息
struct in6_addr sin6_addr; // IPv6地址__u32 sin6_scope_id;
};特殊地址结构体 ——UNIX域地址结构体:
struct sockaddr_un
{           u_short sun_family; // 地址族char sun_path[108]; // 套接字文件路径
};


监听连接请求函数:

//头文件
#include <sys/types.h>        
#include <sys/socket.h>//函数原型
int listen(int sockfd, int backlog);//函数介绍
/****************************************************
函数作用:把sockfd套接字句柄由主动状态转为被动连接状态函数形参:sockfd:使用socket函数申请成功的套接字文件句柄backlog: 当正在处理accept时候有客户端链接, 就入队列,表示队列长度(一般设置为5即可)函数返回值:成功: 0失败: -1
***************************************************/


接受连接函数:

//头文件
#include <sys/types.h>        
#include <sys/socket.h>//函数原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//函数介绍
/****************************************************
函数作用:接收其他客户端的连接请求,如果没有连接将会阻塞等待函数形参:sockfd:使用socket函数申请成功的套接字文件句柄addr: 用于保存当前连接的客户端的IP,端口等信息addrlen:addr这个结构体的大小(必须是一个变量的地址,不能使用sizeof(addr)!!!)函数返回值:成功: 返回当前连接的客户端的本地句柄失败: -1
***************************************************/


连接服务器函数:

//头文件
#include <sys/types.h>        
#include <sys/socket.h>//函数原型
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//函数介绍
/****************************************************
函数作用:接收其他客户端的连接请求,如果没有连接将会阻塞等待函数形参:sockfd:使用socket函数申请成功的套接字文件句柄addr: 包含被连接端的IP地址+端口号等addrlen:addr这个结构体的大小(必须是一个变量的地址,不能使用sizeof(addr)!!!)函数返回值:成功: 返回当前连接的客户端的本地句柄失败: -1
***************************************************/


发送数据函数:

常见的发送数据函数有: send() , write()、 sendto()–>常用于udp通信,这里只讲send,一般为客户端给服务器发送数据

//头文件
#include <sys/types.h>        
#include <sys/socket.h>//函数原型
ssize_t send(int sockfd, const void *buf, size_t len, int flags);//函数介绍
/****************************************************
函数作用:用于发送数据函数形参:sockfd:已连接套接字buf:即将被发送的数据len:数据长度flags:发送标志。MSG_NOSIGNAL:当对端已关闭时,不产生SIGPIPE信号MSG_OOB:发送紧急(带外)数据,只针对TCP连接函数返回值:成功: 返回成功发送的字节数失败: -1
***************************************************/


接收数据函数:

常见的发送数据函数有: recv(), read(), recvfrom()–》可接收发送方的IP+端口,这里只讲recvfrom,一般为客户端给服务器发送数据

//头文件
#include <sys/types.h>        
#include <sys/socket.h>//函数原型
ssize_t send(int sockfd, const void *buf, size_t len, int flags);//函数介绍
/****************************************************
函数作用:用于发送数据函数形参:sockfd:已连接套接字buf:即将被发送的数据len:数据长度flags:发送标志。MSG_NOSIGNAL:当对端已关闭时,不产生SIGPIPE信号MSG_OOB:发送紧急(带外)数据,只针对TCP连接函数返回值:成功: 返回成功发送的字节数失败: -1
***************************************************/

下面直接上代码

服务器端代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>void *client_recv(void* arg);
int main(int argc, char **argv)
{if(argc < 2){printf("Run: server port\n");exit(1);}//1.创建套接字int sockfd = socket(AF_INET,SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");exit(1);}//2.绑定-地址, 端口struct sockaddr_in my_addr;memset(&my_addr, 0, sizeof(my_addr));my_addr.sin_family=AF_INET;my_addr.sin_port=htons( atoi(argv[1]) );//网络字节序my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//网络字节序--在任意ip上绑定int ret  = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));if(ret < 0){perror("bind error");exit(1);}//3.监听--启动服务器ret = listen(sockfd, 5);if(ret < 0){perror("listen error");exit(1);}//4.接受连接//定义保存客户端地址的结构体struct sockaddr_in clientaddr;socklen_t len = sizeof(clientaddr);//4.接受链接--没有客户端链接是处于阻塞状态int  clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);if(clientfd < 0){perror("accept error");}//创建线程--用于接收客户端数据pthread_t id = 0;ret = pthread_create(&id, NULL, client_recv, (void*)&clientfd);//把与客户端通信用的套接字传递给线程if(ret <0){perror("create error");exit(1);}char buffer[128]={0};while(1){//给客户端发送数据scanf("%s", buffer);send(clientfd, buffer, strlen(buffer), 0);//清空buffermemset(buffer, 0, sizeof(buffer));}
}void *client_recv(void* arg)
{//获取客户端套接字int clientfd = *((int*)arg);char buffer[128]={0};while(1){//接收数据recv(clientfd, buffer, sizeof(buffer), 0);//输出printf("%s\n", buffer);//清空buffermemset(buffer, 0, sizeof(buffer));}}
客户端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
void *server_recv(void* arg);int main(int argc, char **argv)
{if(argc < 3){printf("Run: server ip port\n");exit(1);}//1.创建套接字int sockfd = socket(AF_INET,SOCK_STREAM, 0);if(sockfd < 0){perror("socket error");exit(1);}//2. 链接服务器struct sockaddr_in seraddr;memset(&seraddr,0, sizeof(seraddr));seraddr.sin_family = AF_INET;seraddr.sin_port = htons( atoi(argv[2]) );seraddr.sin_addr.s_addr = inet_addr( argv[1] );//服务器地址转网络字节序32位int ret = connect(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr));if(ret < 0){perror("connect error");exit(1);}//创建线程//创建线程--用于接收服务端数据pthread_t id = 0;ret = pthread_create(&id, NULL, server_recv, (void*)&sockfd);//把与客户端通信用的套接字传递给线程if(ret <0){perror("create error");exit(1);}//主线程发送数据char buffer[128]={0};while(1){//给客户端发送数据scanf("%s", buffer);send(sockfd, buffer, strlen(buffer), 0);//清空buffermemset(buffer, 0, sizeof(buffer));}
}void *server_recv(void* arg)
{//获取客户端套接字int sockfd = *((int*)arg);char buffer[128]={0};while(1){//接收数据recv(sockfd, buffer, sizeof(buffer), 0);//输出printf("%s\n", buffer);//清空buffermemset(buffer, 0, sizeof(buffer));}}

这篇关于tcp/ip实现点对点通信(socket编程)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/798531

相关文章

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构

基于Python实现一个图片拆分工具

《基于Python实现一个图片拆分工具》这篇文章主要为大家详细介绍了如何基于Python实现一个图片拆分工具,可以根据需要的行数和列数进行拆分,感兴趣的小伙伴可以跟随小编一起学习一下... 简单介绍先自己选择输入的图片,默认是输出到项目文件夹中,可以自己选择其他的文件夹,选择需要拆分的行数和列数,可以通过

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Python使用pip工具实现包自动更新的多种方法

《Python使用pip工具实现包自动更新的多种方法》本文深入探讨了使用Python的pip工具实现包自动更新的各种方法和技术,我们将从基础概念开始,逐步介绍手动更新方法、自动化脚本编写、结合CI/C... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核