socket编程(2) -- TCP通信

TCP通信

  • 2. 使用 Socket 进行TCP通信
    • 2.1 socket相关函数介绍
    • socket()
    • bind()
    • listen()
    • accept()
    • connect()
    • 2.2 TCP协议 C/S 模型
    • 基础通信代码
  • 最后

2. 使用 Socket 进行TCP通信

Socket通信流程图如下:
在这里插入图片描述

  这里服务器段listen是监听socket套接字的监听文件描述符。如果客户端有连接请求,服务器端会自动和客户端建立连接,这里的accept函数,只是从已经建立了连接的已连接队列中取出一个建立的客户端连接,并返回用于数据传输的文件描述符。

2.1 socket相关函数介绍

socket()

//socket函数
#include <sys/types.h>
#include <sys/socket.h>int socket(int domain, int type, int protocol);
返回值:成功返回socket的文件描述符,失败返回-1,并且通过设置错误信息errno
参数:domain:可以选取下面的参数,常用的是AF_INET,AF_INET6,AF_UNIXName                Purpose                          Man pageAF_UNIX, AF_LOCAL   Local communication              unix(7)AF_INET             IPv4 Internet protocols          ip(7)AF_INET6            IPv6 Internet protocols          ipv6(7)AF_IPX              IPX - Novell protocolsAF_NETLINK          Kernel user interface device     netlink(7)type:可以选取下面的参数,常用的是用于tcp通信的SOCK_STREAM,和udp通信的数据包SOCK_DGRAMSOCK_STREAM     Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.SOCK_DGRAM      Supports datagrams (connectionless, unreliable messages of a fixed maximum length).SOCK_SEQPACKET  Provides  a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is required to read an entire packet with each input system call.SOCK_RAW        Provides raw network protocol access.SOCK_RDM        Provides a reliable datagram layer that does not guarantee ordering.SOCK_PACKET     Obsolete and should not be used in new programs; see packet(7).上面的参数还可以或上(|)下面的两个参数来添加额外属性:SOCK_NONBLOCK   Set the  O_NONBLOCK file status flag on the new open file description.  Using this flag saves extra calls to fcntl(2) to achieve the same result.SOCK_CLOEXEC   Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor.  See the  description  of  the  O_CLOEXEC flag in open(2) for reasons why this may be useful.protocol: 指定这个socket类型使用的协议,如果这个socket类型只有一个协议,那么这个参数设置为0

bind()

#include <sys/types.h> 
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值:成功返回0,失败返回-1,并设置errno值
参数:sockfd:	使用socket函数成功返回的文件描述符addr:socket地址结构体,这里使用sockaddr_in结构体代替,可以接受的客户端ip和端口struct sockaddr {sa_family_t sa_family;char        sa_data[14];}struct sockaddr_in {sa_family_t    sin_family; /* address family: AF_INET */in_port_t      sin_port;   /* port in network byte order */struct in_addr sin_addr;   /* internet address */};/* Internet address. */struct in_addr {uint32_t       s_addr;     /* address in network byte order */};addrlen:sockaddr_in结构体大小,sizeof(addr)

listen()

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int listen(int sockfd, int backlog);
返回值:成功返回0,失败返回-1,并设置errno
参数:sockfd:socket文件描述符,同上backlog:排队建立3次握手队列和刚刚建立3次握手队列的链接数和,例如可以设置为1024

accept()

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:成功,系统掉用会返回一个非负的整数,这个整数就是已经连接的socket文件描述符,失败返回-1,并设置errno值。
参数:sockfd:同上addr:传出参数,取出的这个连接的socket文件描述符的客户端地址参数,设置为NULL表示不需要传出addrlen:传出地址结构体的大小, sizeof(addr),前面为NULL,则它设为NULL

connect()

#include <sys/types.h> 
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值:成功返回0,失败返回-1,并设置errno
参数:sockfd: 客户端socket文件描述符addr: 传入参数,指定服务器的地址和端口addrlen: 上面结构体的大小 sizeof(addr)

2.2 TCP协议 C/S 模型

在这里插入图片描述

为了方便错误处理,可以对上面函数进行封装后使用

//wrap.h
#ifndef __WRAP_H_
#define __WRAP_H_
void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
int Connect(int fd, const struct sockaddr *sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
int Close(int fd);
#endif//wrap.c
#include <wrap.h>void perr_exit(const char *s)
{perror(s);exit(1);
}
// 确定这是一个什么类型的socket,可以接收哪种协议
int Socket(int family, int type, int protocol)
{int sfd;if ((sfd = socket(family, type, protocol)) < 0)perr_exit("socket error");return sfd;
}// 绑定sfd的ip和端口,成功返回0,失败返回-1
int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{int n;// 成功返回0if ((n = bind(fd, sa, salen)) < 0)perr_exit("bind error");return n;
}// 监听sfd并自动与连接请求建立连接,监听成功返回0,失败返回-1
int Listen(int fd, int backlog)
{int n;if ((n = listen(fd, backlog)) < 0)perr_exit("listen error");return n;
}/* 取出一个已经和服务器sfd的socket建立连接的连接队列中取出一个客户端sfd,
后两个都是传出参数,是客户端socket的信息
返回客户端文件描述符*/
int Accept(int sfd, struct sockaddr *sa, socklen_t *clientsocketlenptr)
{int n;
reaccept:if ((n = accept(sfd, sa, clientsocketlenptr)) < 0){// 防止该阻塞函数被无关的信号打断if ((errno == ECONNABORTED) || (errno == EINTR))goto reaccept;elseperr_exit("accept error");}return n;
}/*客户端发起连接,sfd为客户端socket文件描述符,
后两个参数是服务器端的ip和端口
连接成功返回0,失败返回-1*/
int Connect(int sfd, const struct sockaddr *sa, socklen_t salen)
{int n;if ((n = connect(sfd, sa, salen)) < 0)perr_exit("connect error");return n;
}/*从cfd文件描述符中读取数据到 buf 中
成功,返回读取到的字符串长度,如果返回0表示读到末尾,失败返回-1
*/
ssize_t Read(int cfd, void *buf, size_t buflen)
{ssize_t n;
readagain:if ((n = read(cfd, buf, buflen)) == -1){if (errno == EINTR)goto readagain;elsereturn -1;}else if (n == 0){printf("read end of file\n");}return n;
}ssize_t Write(int cfd, const void *buf, size_t buflen)
{ssize_t n;
writeagain:if ((n = write(cfd, buf, buflen)) == -1){if (errno == EINTR)goto writeagain;elsereturn -1;}else if (n == 0){printf("write end of file\n");}return n;
}int Close(int fd)
{int n;if ((n = close(fd)) == -1)perr_exit("close error");return n;
}

基础通信代码

服务器单进程处理客户端连接和数据通信,主要通过while循环来实现。

//server.c
#include <wrap.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>void showClient(const struct sockaddr_in *clientaddr)
{char buf[16];memset(buf, 0x00, sizeof(buf));inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, buf, sizeof(buf));printf("client family is[%d], ip is[%s] ,port is [%d]----connected\n", clientaddr->sin_family, buf, ntohs(clientaddr->sin_port));
}int main()
{int sfd = Socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addr;bzero(&addr, 0x00);addr.sin_family = AF_INET;addr.sin_port = htons(8888);addr.sin_addr.s_addr = htonl(INADDR_ANY);Bind(sfd, (struct sockaddr *)&addr, sizeof(addr));Listen(sfd, 1024);struct sockaddr_in clientaddr;bzero(&clientaddr, 0x00);int cfd;char buf[64];int n;socklen_t len;while (1){n = 0;len = sizeof(clientaddr);cfd = Accept(sfd, (struct sockaddr *)&clientaddr, &len);showClient(&clientaddr);while (1){memset(buf,0x00,sizeof(buf));n = Read(cfd, buf, sizeof(buf));if (n==0){break;}printf("[%d] byte word,client send say:[%s]\n", n, buf);int i = 0;for (i = 0; i < n; i++){buf[i] = toupper(buf[i]);}Write(cfd, buf, n);}}close(cfd);close(sfd);return 0;
}
//client.c
#include <wrap.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>void showClient(const struct sockaddr_in *clientaddr)
{char buf[16];memset(buf, 0x00, sizeof(buf));inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, buf, sizeof(buf));printf("client family is[%d], ip is[%s] ,port is [%d]----connected\n", clientaddr->sin_family, buf, clientaddr->sin_port);
}int main()
{int cfd = Socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addr;bzero(&addr, 0x00);addr.sin_family = AF_INET;addr.sin_port = htons(8888);inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr);Connect(cfd, (struct sockaddr *)&addr, sizeof(addr));char words[64];int n;while (1){memset(words, 0x00, sizeof(words));// scanf("%s", words);//读标准输入数据n = read(STDIN_FILENO, words, sizeof(words));Write(cfd, words, strlen(words));n = Read(cfd, words, sizeof(words));printf("server reply [%s],byte is [%d]\n", words, n);}return 0;
}

最后

推荐一个零声教育学习教程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/46015.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

运维-Docker-黑马

运维-Docker-黑马 编辑时间&#xff1a;2024/7/15 来源&#xff1a;黑马程序员 docker&#xff1a;快速构建&#xff0c;运行&#xff0c;管理应用的工具 Docker安装 部署mysql 命令解读

[Cesium for Supermap] 加载3dTiles,点击获取属性

代码&#xff1a; // 设为椭球var obj [6378137.0, 6378137.0, 6356752.3142451793];Cesium.Ellipsoid.WGS84 Object.freeze(new Cesium.Ellipsoid(obj[0], obj[1], obj[2]));var viewer new Cesium.Viewer(cesiumContainer);var scene viewer.scenescene.lightSource.ambi…

彻底改变时尚:使用 GAN 实现 AI 的未来

彻底改变时尚&#xff1a;使用 GAN 实现 AI 的未来 一、介绍 想象一下&#xff0c;在这个世界里&#xff0c;时装设计师永远不会用完新想法&#xff0c;我们穿的每一件衣服都是一件艺术品。听起来很有趣&#xff0c;对吧&#xff1f;好吧&#xff0c;我们可以在通用对抗网络 &a…

鸿蒙基本工程目录

工程级目录 AppScope 中存放应用全局所需要的资源文件。entry 是应用的主模块&#xff0c;存放 HarmonyOS 应用的代码、资源等。oh_modules 是工程的依赖包&#xff0c;存放工程依赖的源文件。build-profile.json5 是工程级配置信息&#xff0c;包括签名、产品配置等。hvigorf…

品牌产业出海指南如何搭建国际化架构的跨境电商平台?

在“品牌&产业出海指南 – 成功搭建跨境电商平台”系列中&#xff0c;我们将从电商分销系统、跨境平台商城/多商户商城系统和国际化架构三个方面对帮助您梳理不同平台模式的优缺点、应用场景、开发重点和运营建议。 在“品牌&产业出海指南 – 成功搭建跨境电商平台”系…

【漏洞复现】Rejetto HTTP文件服务器——远程命令执行(CVE-2024-23692)

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 Rejetto HTTP文件服务器是一个轻量级的HTTP服务器软件&#xff…

vue项目1分钟实现自定义右键菜单,懒人的福音

高效实现需求&#xff0c;避免重复造轮子&#xff0c;今天给大家分享的是&#xff0c;如何在最短的时间内实现右键菜单&#xff0c;方法也很简单&#xff0c;一个插件就可以搞定&#xff0c;话不多说&#xff0c;上效果图&#xff1a; 1. 效果图&#xff1a; 2. 安装&#xff…

SCI丨中三区

无线网络遥感图像和视频处理技术在xxxxx析基于智能物联网的xxxxx养老模式可持续发展基于心理行为大数据分类算法xxxxxx研究基于云计算xxxxx行为分析及客户感知体系的构建基于机器学习的xxxxx金钢时效行为研究 基于机器视觉的xxxxx检测系统研究 机器学习的电子显微镜xxxxx材料的…

【React Hooks原理 - forwardRef、useImperativeHandle】

概述 上文我们聊了useRef的使用和实现&#xff0c;主要两个用途&#xff1a;1、用于持久化保存 2、用于绑定dom。 但是有时候我们需要在父组件中访问子组件的dom或者属性/方法&#xff0c;而React中默认是不允许父组件直接访问子组件的dom的&#xff0c;这时候就可以通过forwa…

类和对象的简述(c++篇)

开局之前&#xff0c;先来个小插曲&#xff0c;放松一下&#xff1a; 让我们的熊二来消灭所有bug 各位&#xff0c;在这祝我们&#xff1a; 放松过后&#xff0c;开始步入正轨吧。爱学习的铁子们&#xff1a; 目录&#xff1a; 一类的定义&#xff1a; 1.简述&#xff1a; 2…

【JavaScript 算法】贪心算法:局部最优解的构建

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、贪心算法的基本概念贪心算法的适用场景 二、经典问题及其 JavaScript 实现1. 零钱兑换问题2. 活动选择问题3. 分配问题 三、贪心算法的应用四、总结 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种逐步构建解…

mybatisPlus和mybatis的版本冲突问题、若依换成MP、解决git无法推送、使用若依框架的swagger、以后再遇到团队项目应该怎么做。

20240716 一. mybatisPlus和mybatis的版本冲突问题1. 使用前的准备2. 我遇到了一个很严重的问题。3. 解决问题&#xff0c;好吧也没解决&#xff0c;发现问题&#xff01;&#xff01; 二、该死的git&#xff01;&#xff01;&#xff01;&#xff01;1. 解决无法在idea中使用g…

【Outlook】从Outlook新版回归经典版全攻略

引言 在微软宣布计划于2024年底淘汰邮件应用&#xff08;Mail app&#xff09;之后&#xff0c;许多用户发现新版Outlook应用&#xff08;Outlook (new)&#xff09;在他们的Windows 11/10系统上自动启动。如果您更倾向于使用经典版Outlook&#xff08;Outlook (classic)&…

webpack优化

优化方向 热更新 概念 /** hmr: hot module replacement 热模块替换 / 模块热更新作用&#xff1a; 一个模块发生改变&#xff0c;只会重新打包这一个模块&#xff08;而不是打包所有模块&#xff09;&#xff0c;极大的提升了构建速度样式文件&#xff1a; 可以使用hmr功能…

Facebook:数字时代的社交瑰宝

在当今数字化飞速发展的时代&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分&#xff0c;而Facebook作为其中的领军者&#xff0c;不仅连接了全球数十亿的用户&#xff0c;更深刻地改变了人们的社交方式和生活方式。本文将探讨Facebook如何成为数字时代的社交瑰宝…

再谈有关JVM中的四种引用

1.强引用 强引用就是我们平时使用最多的那种引用&#xff0c;就比如以下的代码 //创建一个对象 Object obj new Object();//强引用 这个例子就是创建了一个对象并建立了强引用&#xff0c;强引用一般就是默认支持的当内存不足的时候&#xff0c;JVM开始垃圾回收&#xff0c…

防火墙的冗余基础知识+实验检测

将之前先理清需要注意的知识点&#xff1a; 1、注意防火墙冗余时的会话表必须保持一致&#xff0c;这里HRP技术已经做到 2、vrrp是自动开启抢占的&#xff0c;且是根据优先级进行抢占的 3、免费ARP的作用&#xff1a;告诉交换机的某个IP的mac地址变成了我的这个mac地址 4、HRP …

C++ | Leetcode C++题解之第231题2的幂

题目&#xff1a; 题解&#xff1a; class Solution { private:static constexpr int BIG 1 << 30;public:bool isPowerOfTwo(int n) {return n > 0 && BIG % n 0;} };

强化学习——多臂老虎机问题(MAB)【附python代码】

文章目录 一、问题描述1.1 问题定义1.2 形式化描述1.3 累积懊悔1.4 估计期望奖励 二、解决方法2.1 ϵ-贪婪算法2.2 上置信界算法2.3 汤普森采样算法2.4 小结 一、问题描述 1.1 问题定义 有一个用于 K 根拉杆的老虎机&#xff0c;每一根拉杆都对应一个关于奖励的概率分布 R 。每…

【C++题解】1154. 数组元素的查找

问题&#xff1a;1154. 数组元素的查找 类型&#xff1a;数组找数 题目描述&#xff1a; 给你 m 个整数&#xff0c;查找其中有无值为 n 的数&#xff0c;有则输出该数第一次出现的位置,没有则输出 −1 。 输入&#xff1a; 第一行一个整数 m 代表数的个数 ( 0≤m≤100 ) 。…