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

相关文章

Ubuntu固定虚拟机ip地址的方法教程

《Ubuntu固定虚拟机ip地址的方法教程》本文详细介绍了如何在Ubuntu虚拟机中固定IP地址,包括检查和编辑`/etc/apt/sources.list`文件、更新网络配置文件以及使用Networ... 1、由于虚拟机网络是桥接,所以ip地址会不停地变化,接下来我们就讲述ip如何固定 2、如果apt安

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

java父子线程之间实现共享传递数据

《java父子线程之间实现共享传递数据》本文介绍了Java中父子线程间共享传递数据的几种方法,包括ThreadLocal变量、并发集合和内存队列或消息队列,并提醒注意并发安全问题... 目录通过 ThreadLocal 变量共享数据通过并发集合共享数据通过内存队列或消息队列共享数据注意并发安全问题总结在 J

SpringBoot+MyBatis-Flex配置ProxySQL的实现步骤

《SpringBoot+MyBatis-Flex配置ProxySQL的实现步骤》本文主要介绍了SpringBoot+MyBatis-Flex配置ProxySQL的实现步骤,文中通过示例代码介绍的非常详... 目录 目标 步骤 1:确保 ProxySQL 和 mysql 主从同步已正确配置ProxySQL 的

JS 实现复制到剪贴板的几种方式小结

《JS实现复制到剪贴板的几种方式小结》本文主要介绍了JS实现复制到剪贴板的几种方式小结,包括ClipboardAPI和document.execCommand这两种方法,具有一定的参考价值,感兴趣的... 目录一、Clipboard API相关属性方法二、document.execCommand优点:缺点:

nginx部署https网站的实现步骤(亲测)

《nginx部署https网站的实现步骤(亲测)》本文详细介绍了使用Nginx在保持与http服务兼容的情况下部署HTTPS,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值... 目录步骤 1:安装 Nginx步骤 2:获取 SSL 证书步骤 3:手动配置 Nginx步骤 4:测

Idea实现接口的方法上无法添加@Override注解的解决方案

《Idea实现接口的方法上无法添加@Override注解的解决方案》文章介绍了在IDEA中实现接口方法时无法添加@Override注解的问题及其解决方法,主要步骤包括更改项目结构中的Languagel... 目录Idea实现接China编程口的方法上无法添加@javascriptOverride注解错误原因解决方

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

MySql死锁怎么排查的方法实现

《MySql死锁怎么排查的方法实现》本文主要介绍了MySql死锁怎么排查的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录前言一、死锁排查方法1. 查看死锁日志方法 1:启用死锁日志输出方法 2:检查 mysql 错误