socket编程|TCP

一.套接字概念

套接字(Socket)是一种用于网络通信的编程接口,它提供了一种机制,使得不同计算机上的应用程序能够通过网络进行通信和交换数据。

套接字可以看作是应用程序和网络之间的端点,它定义了应用程序与网络之间的通信规则和数据格式。通过套接字,应用程序可以创建、连接、发送和接收数据,实现与其他计算机上的应用程序进行通信。

套接字通常基于TCP/IP协议栈进行通信,其中常用的套接字类型有两种:

  1. 流式套接字(Stream Socket):使用基于流的TCP协议,提供可靠的面向连接的通信。流式套接字通过建立连接,提供可靠的、无差错的数据传输,保证数据的有序性和完整性。

  2. 数据报套接字(Datagram Socket):使用基于数据报的UDP协议,提供无连接的通信。数据报套接字以短的数据包(数据报)的形式传输数据,适用于实时性要求较高但不需要可靠性的情况。

使用套接字进行通信通常需要进行以下步骤:

  1. 创建套接字:应用程序通过调用系统API来创建一个套接字。

  2. 绑定套接字:将套接字与本地网络地址绑定,以便其他应用程序可以通过网络找到它。

  3. 监听连接请求(对于服务器):在服务器端,套接字会监听传入的连接请求。

  4. 建立连接(对于客户端):在客户端,套接字会发起连接请求与服务器建立连接。

  5. 发送和接收数据:已建立连接的套接字可以通过发送和接收数据来进行通信。

  6. 关闭套接字:通信完成后,应用程序可以关闭套接字来释放资源。

套接字编程提供了灵活和强大的网络通信功能,广泛应用于互联网、网络通信和分布式计算等领域。
在这里插入图片描述

网络通信过程中,套接字一定是成对出现的,一个套接字描述符有两个缓冲区,分别是写缓冲区,读缓冲区

二.预备知识

2.1网络字节序

网络字节序(Network Byte Order)是一种规定的数据表示方式,在网络通信中用于保证不同计算机之间的数据交换的正确性和一致性。

在计算机内部,数据一般以主机字节序(Host Byte Order)存储和处理。主机字节序可以是大端字节序(Big-Endian)或小端字节序(Little-Endian),具体取决于计算机体系结构。大端字节序表示高位字节在前、低位字节在后;小端字节序表示低位字节在前、高位字节在后。

为了在不同的计算机之间进行数据交换,需要使用统一的网络字节序。网络字节序采用的是大端字节序,因此在网络通信中,数据会按照从高位到低位的顺序进行传输。

为了方便在不同字节序之间进行转换,可以使用以下函数进行字节序转换:

  • htons():将16位(2字节)主机字节序转换为网络字节序。
  • ntohs():将16位(2字节)网络字节序转换为主机字节序。
  • htonl():将32位(4字节)主机字节序转换为网络字节序。
  • ntohl():将32位(4字节)网络字节序转换为主机字节序。

使用这些函数进行字节序的转换可以确保在不同计算机之间进行数据交换时,数据的字节顺序是一致的。

网络字节序–>大端法–>高位存低地址
主机字节序–>小端法–>高位存高地址

大小端区别

例如十六进制数值 0x12345678,我们可以将其分解成四个字节进行表示。

在大端存储中,高位字节存储在低地址处,低位字节存储在高地址处。因此,0x12 是高位字节,0x34 是次高位字节,0x56
是次低位字节,0x78 是低位字节。所以在大端存储中,表示为:

高地址 -> 低地址:0x12 0x34 0x56 0x78

在小端存储中,低位字节存储在低地址处,高位字节存储在高地址处。因此,0x78 是低位字节,0x56 是次低位字节,0x34
是次高位字节,0x12 是高位字节。所以在小端存储中,表示为:

高地址 -> 低地址:0x78 0x56 0x34 0x12

大小端判断

使用 intchar 类型的联合体可以进行字节序的判断,而不使用数组。

以下是一个示例代码:

#include <stdio.h>union EndianTest {int value;char byte;
};int isLittleEndian() {union EndianTest test;test.value = 1;// 判断低字节的值是否为1return (test.byte == 1);
}int main() {if (isLittleEndian()) {printf("小端字节序 (Little-Endian)\n");} else {printf("大端字节序 (Big-Endian)\n");}return 0;
}

在这个示例中,我们定义了一个联合体 EndianTest,它包含一个 int 类型的成员 value 和一个 char 类型的数组成员 byte

函数 isLittleEndian() 中,我们将数值 1 赋值给 value,然后通过检查 byte[0] 的值是否为 1 来判断字节序。如果 byte[0] 的值为 1,则表示是小端字节序;如果不为 1,则表示是大端字节序。

通过这个方法,我们可以使用 intchar 类型的联合体来判断当前计算机的字节序是大端还是小端。

2.2 IP地址转换函数

主机字节序转换为网络字节序

不常用方法

在这里插入图片描述

常用方法
头文件: #include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
声明:
in_addr_t inet_addr(const char *strptr); //该参数是字符串

      typedef uint32_t in_addr_t;  (通过vi -t 追一下)struct in_addr { in_addr_t   s_addr;}; 功能:  主机字节序转为网络字节序
参数:  const char *strptr: 字符串
返回值: 返回一个无符号长整型数(无符号32位整数用十六进制表示), 否则NULL

网络字节序转换为主机字节序

不常用

在这里插入图片描述

常用

头文件 :    #include <arpa/inet.h>  #include<sys/socket.h>  #include<netinet/in.h>  
声明:   char *inet_ntoa(stuct in_addr inaddr);
功能:   将网络字节序二进制地址转换成主机字节序。 
参数:  stuct in_addr in addr  : 只需传入一个结构体变量
返回值:  返回一个字符指针, 否则NULL;

2.3 sockaddr数据结构

在这里插入图片描述

    头文件:#include<sys/types.h>  #include<sys/socket.h> #include<netinet/in.h>  #include<netinet/ip.h>ipv4通信结构体:struct sockaddr_in {sa_family_t    sin_family;   ----协议族in_port_t      sin_port;     ----端口struct in_addr sin_addr;     ----ip结构体};struct in_addr {uint32_t   s_addr;           --ip地址};

三.网络套接字函数

3.1 soket模型创建流程图

在这里插入图片描述

3.2 socket创建套接字

int socket(int domain, int type, int protocol);
头文件: #include <sys/types.h>#include <sys/socket.h>
功能:创建套接字
参数:domain:协议族AF_UNIX, AF_LOCAL  本地通信AF_INET            ipv4AF_INET6            ipv6type:套接字类型SOCK_STREAM:流式套接字SOCK_DGRAM:数据报套接字protocol:协议 - 填0 自动匹配底层 ,根据type系统默认自动帮助匹配对应协议传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)返回值:成功 文件描述符 0 -> 标准输入  1->标准输出  2->标准出错 3->socket失败 -1,更新errno

3.3 bind绑定套接字

头文件: #include<sys/types.h>  #include<sys/socket.h> #include<netinet/in.h>  #include<netinet/ip.h>功能:绑定协议, IP以及port声明: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);参数:sockfd:套接字addr:用于通信结构体 (提供的是通用结构体,需要根据选择通信方式,填充对应 结构体-通信当时socket第一个参数确定,需要强转)  addrlen:结构体大小   返回值:成功 0   失败-1,更新errno通用结构体:struct sockaddr {sa_family_t  sa_family;char        sa_data[14];}ipv4通信结构体:
struct sockaddr_in {sa_family_t    sin_family;  ----协议族in_port_t      sin_port;   ----端口struct in_addr sin_addr;     ----ip结构体
};
struct in_addr {uint32_t       s_addr;     --ip地址
};

3.4 listen监听

int listen(int sockfd, int backlog);
功能:监听,将主动套接字变为被动套接字
参数:sockfd:套接字backlog:同一时间可以响应客户端请求链接的最大个数,不能写0.不同平台可同时链接的数不同,一般写6-8个返回值:成功 0   失败-1,更新errno  

3.5 accept阻等待连接

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept(sockfd,NULL,NULL);
功能:阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,则accept()函数返回,返回一个用于通信的套接字文件;
参数:Sockfd :套接字addr: 链接客户端的ip和端口号如果不需要关心具体是哪一个客户端,那么可以填NULL;addrlen:结构体的大小如果不需要关心具体是哪一个客户端,那么可以填NULL;
返回值: 成功:文件描述符; //用于通信失败:-1,更新errno新errno 

3.6 recv

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能: 接收数据 
参数: sockfd: acceptfd ;buf  存放位置len  大小flags  一般填0,相当于read()函数MSG_DONTWAIT  非阻塞
返回值: < 0  失败出错  更新errno==0  表示客户端退出>0   成功接收的字节个数

​3.7 connect

connect:使用现有的socket与服务器建立连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:socket 函数返回值
addr:传入参数。地址结构(服务器的地址结构)
addlen:addr长度
成功:0
失败:-1 errno
如果不使用bind绑定客户端地址结构,采用"隐式绑定"

3.8 send

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
sockfd:指定要发送数据的socket描述符。
buf:指向要发送数据的缓冲区的指针。
len:指定要发送数据的长度(以字节为单位)。
flags:指定发送操作的标志,如`0`或`MSG_DONTWAIT`等。可以在调用时传递特定的标志,也可以用`0`表示无特殊标志。返回值:
成功: 发送的字节数,
错误:  -1。

需要注意的是,send()
函数并不保证立即将所有数据发送出去,它会尽力将数据发送给远程主机,但在网络条件不理想或发送缓冲区已满的情况下,可能会导致部分数据未能立即发送。

四.案例

利用socket编程。实现一个服务器,客户端发送文本数据给服务器后,服务器将字母大写转小写,小写转大写。转换完成后发送给客户端。客户端输入"quit#"后退出。并且客户端连接后,服务器端打印客户端的ip地址和端口号。

server.c

  #include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char const *argv[])
{//1.socket建立文件描述符int fd = socket(AF_INET, SOCK_STREAM, 0);if (fd < 0){perror("socket is err");}//2.bind绑定服务器ip地址和端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");int len = sizeof(caddr);int flage = bind(fd, (struct sockaddr *)(&saddr), sizeof(saddr));if (flage < 0){perror("bind is err");return -1;}//3.listen监听,设置最大同时连接数量flage = listen(fd, 5);if (flage < 0){perror("listen is err");return -1;}printf("listern is ok\n");//4.accept等待客户端连接int acceptfd = accept(fd, (struct sockaddr *)&caddr, &len);if (acceptfd < 0){perror("flage is err");return -1;}printf("ip:%s   port:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));//5. 不断读取客服端的内容,大小写转换后,发送给客户端char buf[1024] = {0};int size = 0;while (1){flage = recv(acceptfd, buf, sizeof(buf), 0);if (flage < 0){perror("recv is err");}else if (flage == 0){printf("ip:%s is close\n", inet_ntoa(caddr.sin_addr));}else{size = strlen(buf);for (int i = 0; i < size; ++i){if (buf[i] >= 'a' && buf[i] <= 'z')buf[i] = buf[i] + ('A' - 'a');elsebuf[i] = buf[i] + ('a' - 'A');}send(acceptfd, buf, sizeof(buf), 0);}}close(fd);return 0;
}

client.c

   #include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char const *argv[])
{//1.socket建立文件描述符int fd = socket(AF_INET, SOCK_STREAM, 0);if (fd < 0){perror("socket is err");}//2.connect连接服务器struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[2]));saddr.sin_addr.s_addr = inet_addr(argv[1]);int flage = connect(fd, (struct sockaddr *)(&saddr), sizeof(saddr));if (flage < 0){perror("connect is err");}//3.服务器端不断发送数据,接受服务器转化后的数据char buf[1024] = {0};while (1){//memset(buf,0,sizeof(buf));fgets(buf, sizeof(buf), stdin);if (strncmp(buf,"quit#",5)==0){break;}if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';send(fd, buf, sizeof(buf), 0);flage = recv(fd, buf, sizeof(buf), 0);if (flage < 0){perror("recv is err");}else{fprintf(stdout, "%s\n", buf);}}close(fd);return 0;
}

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

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

相关文章

maven运行报错解决

在IDEA上运行较大项目时&#xff0c;编译量很大&#xff0c;可能会报出 Error:java: java.lang.OutOfMemoryError: Java heap space 的错误&#xff0c;解决方法如下&#xff1a; java.lang.OutOfMemoryError是内存不足导致的&#xff0c;因此需要修改Idea运行项目的内存大小。…

html给下拉框添加搜索、分页功能(通过ajax从服务器获取搜索数据)

文章目录 下拉框搜索分页功能开发功能使用源码和Demo&#xff08;点个赞再走咯&#xff09;test.htmlsearchable-select.csssearchserver-select.js 下拉框搜索分页功能开发 最近需要开发一个下拉框从服务器通过Ajax请求搜索数据库并且分页的组件&#xff0c;源码和demo放在下面…

数据结构——二叉树

堆的简易建立 前言堆的顺序表实现一、Heap.h头文件向下调整算法复杂度向上调整算法复杂度 二、Heap.c功能函数文件三、Test.c测试函数文件四、运行结果展示五、完整代码展示 二叉树的链表实现回顾二叉树的概念链式二叉树代码模拟二叉树的遍历 总结 前言 现在我们开始学习堆的建…

C语言入门Day_19 初识函数

目录 1.函数的定义 2.函数的调用 3.易错点 4.思维导图 前言&#xff1a; printf()我们已经很熟悉了&#xff0c;它有一个特定的功能&#xff0c;就是在屏幕上输出一行文字。之前的课程我们都称呼printf()为一个功能&#xff0c;实际上ta在编程中有个特定的名字——函数。 …

获取Windows 10中的照片(旧版)下载

Windows 10中的新版照片应用&#xff0c;目前发现无法直接打开部分iOS设备上存储的照片。需要使用照片&#xff08;旧版&#xff09;才行。 但目前应用商店中无法直接搜索到照片&#xff08;旧版&#xff09;&#xff0c;因此笔者提供如下链接&#xff0c;可以直接访问并呼出W…

C++零碎记录(十三)

23. 多态 23.1 多态简介 ① 多态是C面向对象三大特性之一。 ② 多态分为两类&#xff1a; 1. 静态多态&#xff1a;函数重载和运算符重载属于静态多态&#xff0c;复用函数名。 2. 动态多态&#xff1a;派生类和 虚函数实现运行时多态。 ③ 静态多态和动态多态区别&#xff…

通过 chatgpt 协助完成网站数据破解

Chatgpt 的出现极大地提升了程序员的工作效率&#xff0c;常见的使用场景包括代码自动生成、代码静态检查等&#xff0c;那么 chatgpt 能否用于某些网站的数据破解工作呢&#xff1f; 问题 某天线上服务开始报警&#xff0c;原来是某个视频网站无法获取到其 cdn 地址导致的下…

K8s上安装gitlab-ce

文章目录 K8s上安装gitlab-ce操作如下gitlab-deployment.yml K8s上安装gitlab-ce 前言   使用pv-pvc来持久化gitlab的数据&#xff0c;配置&#xff0c;日志文件。   pod启动后需要需要修改external_url然后重启pod。 操作如下 mkdir -p /mnt/data01/gitlab ctr -n k8s.…

内外统一的边缘原生云基础设施架构——火山引擎边缘云

近日&#xff0c;火山引擎边缘云边缘计算架构师郭少巍在LiveVideoStack Con 2023上海站围绕火山引擎边缘云海量分布式节点和上百T带宽&#xff0c;结合边缘计算在云基础设施架构方面带来的挑战&#xff0c;分享了面对海量数据新的应用形态对低时延和分布式架构的需求&#xff0…

数据通信网络之IPv6以太网二层交换

文章及资源归档至【AIShareLab】&#xff0c;回复 通信系统与网络 可获取。 文章目录 一、目的二、环境及网络拓扑三、需求四、步骤 一、目的 掌握 VLAN、Trunk 的基础配置。掌握以太网链路聚合的基础配置。掌握路由器子接口的配置&#xff0c;以及通过子接口实现 VLAN 之间互…

4.1.8- Web 应用程序使用的组件进行指纹识别

Web 应用程序使用的组件进行指纹识别 IDWSTG-INFO-08 总结 毫不夸张地说&#xff0c;几乎所有可以想象的Web应用程序的想法都已经投入开发。随着全球大量自由和开源软件项目的积极开发和部署&#xff0c;应用程序安全测试很可能会遇到完全或部分依赖于这些知名应用程序或框架…

[uniapp]踩坑日记 unexpected character > 1或‘=’>1 报错

在红色报错文档里下滑&#xff0c;找到Show more 根据提示看是缺少标签&#xff0c;如果不是缺少标签&#xff0c;看看view标签内容是否含有<、>、>、<号,把以上符合都进行以<号为例做{{“<”}}处理