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

相关文章

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont

Python实现PDF按页分割的技术指南

《Python实现PDF按页分割的技术指南》PDF文件处理是日常工作中的常见需求,特别是当我们需要将大型PDF文档拆分为多个部分时,下面我们就来看看如何使用Python创建一个灵活的PDF分割工具吧... 目录需求分析技术方案工具选择安装依赖完整代码实现使用说明基本用法示例命令输出示例技术亮点实际应用场景扩

java如何实现高并发场景下三级缓存的数据一致性

《java如何实现高并发场景下三级缓存的数据一致性》这篇文章主要为大家详细介绍了java如何实现高并发场景下三级缓存的数据一致性,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 下面代码是一个使用Java和Redisson实现的三级缓存服务,主要功能包括:1.缓存结构:本地缓存:使

如何在Java Spring实现异步执行(详细篇)

《如何在JavaSpring实现异步执行(详细篇)》Spring框架通过@Async、Executor等实现异步执行,提升系统性能与响应速度,支持自定义线程池管理并发,本文给大家介绍如何在Sprin... 目录前言1. 使用 @Async 实现异步执行1.1 启用异步执行支持1.2 创建异步方法1.3 调用

Spring Boot配置和使用两个数据源的实现步骤

《SpringBoot配置和使用两个数据源的实现步骤》本文详解SpringBoot配置双数据源方法,包含配置文件设置、Bean创建、事务管理器配置及@Qualifier注解使用,强调主数据源标记、代... 目录Spring Boot配置和使用两个数据源技术背景实现步骤1. 配置数据源信息2. 创建数据源Be

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

linux批量替换文件内容的实现方式

《linux批量替换文件内容的实现方式》本文总结了Linux中批量替换文件内容的几种方法,包括使用sed替换文件夹内所有文件、单个文件内容及逐行字符串,强调使用反引号和绝对路径,并分享个人经验供参考... 目录一、linux批量替换文件内容 二、替换文件内所有匹配的字符串 三、替换每一行中全部str1为st

SpringBoot集成MyBatis实现SQL拦截器的实战指南

《SpringBoot集成MyBatis实现SQL拦截器的实战指南》这篇文章主要为大家详细介绍了SpringBoot集成MyBatis实现SQL拦截器的相关知识,文中的示例代码讲解详细,有需要的小伙伴... 目录一、为什么需要SQL拦截器?二、MyBATis拦截器基础2.1 核心接口:Interceptor

SpringBoot集成EasyPoi实现Excel模板导出成PDF文件

《SpringBoot集成EasyPoi实现Excel模板导出成PDF文件》在日常工作中,我们经常需要将数据导出成Excel表格或PDF文件,本文将介绍如何在SpringBoot项目中集成EasyPo... 目录前言摘要简介源代码解析应用场景案例优缺点分析类代码方法介绍测试用例小结前言在日常工作中,我们经