电子商务网站软件建设核心,电子商务网站推广的方法有哪些,简洁 wordpress,求职网站排名(꒪ꇴ꒪ )#xff0c;Hello我是祐言QAQ我的博客主页#xff1a;C/C语言#xff0c;数据结构#xff0c;Linux基础#xff0c;ARM开发板#xff0c;网络编程等领域UP#x1f30d;快上#x1f698;#xff0c;一起学习#xff0c;让我们成为一个强大的攻城狮#xff0…(꒪ꇴ꒪ )Hello我是祐言QAQ我的博客主页C/C语言数据结构Linux基础ARM开发板网络编程等领域UP快上一起学习让我们成为一个强大的攻城狮送给自己和读者的一句鸡汤集中起来的意志可以击穿顽石!作者水平很有限如果发现错误请在评论区指正感谢 Socket编程是通过使用Socket API接口来实现的这些接口允许开发人员创建网络应用程序实现数据的传输和通信。下面我们就一些常见的Socket API接口示例和讲解来实现TCP/IP通信。
一、TCP通信相关函数 ① socket()创建套接字 socket(socket_family, socket_type, protocol0)函数用于创建一个套接字对象。socket_family表示地址族例如socket.AF_INET表示IPv4socket_type表示套接字类型例如socket.SOCK_STREAM表示TCP套接字protocol通常默认为0。
// 创建套接字socketint ser_socket socket(AF_INET, SOCK_STREAM, 0);if (ser_socket -1){perror(socket);return -1;} ②初始化地址结构体
// 初始化地址结构体 // IP地址PORT端口号struct sockaddr_in addr;addr.sin_family AF_INET; //地址簇addr.sin_port 56789; //端口一般以传参的传进来addr.sin_addr.s_addr inet_addr(192.168.159.128); //IP地址// addr.sin_addr.s_addr htonl(INADDR_ANY); //用特殊的0.0.0.0这个IP来绑定本机IP地址 ③ bind()绑定本机地址和端口 bind((host, port))函数用于将套接字与本地地址和端口绑定以便在特定地址和端口上监听连接请求。 // 绑定地址结构体bindint b bind(ser_socket, (struct sockaddr *)addr, sizeof(struct sockaddr_in));if(b -1){perror(bind);return -1;}printf(绑定成功\n); ④ connect()建立连接 connect((host, port))函数用于建立到远程主机的TCP连接。它连接到指定的远程地址和端口。
// 建立连接connectint c connect(cli_socket, (struct sockaddr *)addr, sizeof(struct sockaddr_in));if (c -1){perror(connect);return -1;}printf(连接成功\n);⑤ listen()设置监听端口 listen(backlog)函数用于设置套接字为监听模式backlog指定了可以等待连接的最大客户端数量。 // 开启监听listenint l listen(ser_socket, 3);if (l -1){perror(listen);return -1;}printf(监听成功\n);//ser_socket由 待链接套接字 变成 监听套接字 ⑥ accept()接受TCP连接 accept()函数用于接受客户端的TCP连接请求并返回一个新的套接字对象以便与客户端进行通信。 // 等待连接acceptstruct sockaddr_in c_addr; //用来存放客户端链接成功之后的IP加端口int addrlen sizeof(c_addr);int new_socekt accept(ser_socket, (struct sockaddr *)c_addr, addrlen);if (new_socekt -1){perror(accept);return -1;}// new_socekt 链接成功之后用来通信的套接字printf(客户端[%s][%u]连接成功\n, inet_ntoa(c_addr.sin_addr), c_addr.sin_port);//客户端的IP跟端口IP是你客户端本身自带的但是端口是系统随机分配的 ⑦ recv(), read()数据接收 这些函数用于从套接字接收数据。recv()和read()用于TCP套接字,它们接收数据并将其存储在指定的缓冲区中。 int new_socekt *((int *)arg);while(1){sem_wait(space);//P操作 空间资源-1char buf[1024];bzero(buf, sizeof(buf));read(new_socekt, buf, sizeof(buf));printf(服务器接收到消息: %s\n, buf);sem_post(data);//V操作 数据资源1} ⑧ send(), write()数据发送 这些函数用于向套接字发送数据。send()和write()用于TCP套接字,它们将数据从缓冲区发送到目标套接字。
int new_socekt *((int *)arg);while(1){sem_wait(data);//P操作 数据资源-1char buf[1024];printf(请输入发送给客户端的消息);fgets(buf, sizeof(buf), stdin);write(new_socekt, buf, strlen(buf));sem_post(space);//V操作 空间资源1} 二、网络地址转换和套接字属性的管理
在网络编程中有时候需要进行网络地址的转换以及设置和获取套接字的属性。这些操作是网络通信中的基本需求之一。在本部分中我们将介绍与网络地址转换相关的函数以及如何获取和设置套接字属性。
2.1 网络地址转换相关函数
1. inet_aton( )和 inet_addr( )
这两个函数允许将点分十进制字符串形式的IP地址转换为32位网络字节序的二进制地址。 inet_aton(const char *cp, struct in_addr *inp)将点分十进制字符串 cp 转换为32位网络字节序的二进制地址并存储在 inp 结构中。 inet_addr(const char *cp)将点分十进制字符串 cp 转换为32位网络字节序的二进制地址并返回结果。
2. inet_network( ) inet_network(const char *cp) 函数用于将点分十进制字符串形式的IP网络地址例如 192.168.1.0转换为32位网络字节序的二进制地址。
3. inet_ntoa( ) inet_ntoa(struct in_addr in) 函数用于将32位网络字节序的二进制地址转换为点分十进制字符串形式的IP地址例如将 “11111111111111111111111111111111”转换为 192.168.1.47。
4. inet_lnaof( ) 和 inet_netof( ) 这两个函数用于从 struct in_addr 结构中提取主机位和网络位的部分。inet_lnaof(struct in_addr in) 返回主机位inet_netof(struct in_addr in) 返回网络位。
2.2 获取和设置套接字属性
1. getsockopt( ) getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) 函数用于获取套接字 sockfd 的属性。你可以指定属性的级别 level 和选项名 optname并将结果存储在 optval 中。optlen 用于指定 optval 缓冲区的大小。
2. setsockopt( ) setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) 函数用于设置套接字 sockfd 的属性。你可以指定属性的级别 level 和选项名 optname并提供要设置的值 optval 以及值的大小 optlen。 这些函数是在网络编程中非常有用的它们允许你在应用程序中轻松地进行网络地址的转换和管理套接字属性。网络编程需要处理各种网络相关操作这些函数提供了一种有效的方式来处理这些需求。无论是将字符串形式的IP地址转换为二进制形式还是获取和设置套接字的属性这些函数都是网络程序员的强大工具。
三、TCP/IP实现服务器与客户端相互通信 服务器与客户端对比图 服务器端和客户端分别创建了两个线程task1 和 task2分别用于接收和发送消息。它们之间通过有名信号量 space 和 data 进行同步和通信。在客户端中通过 connect 连接到服务器。最终实现服务器与客户端相互通信 server.c
#include stdio.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include unistd.h
#include stdlib.h
#include strings.h
#include string.h
#include pthread.h
#include fcntl.h
#include sys/stat.h
#include semaphore.h#define SPACE /my_space // 使用唯一的路径名
#define DATA /my_datasem_t *space;
sem_t *data;//线程任务函数1
void *task1(void *arg)
{ int new_socekt *((int *)arg);while(1){sem_wait(space);//P操作 空间资源-1char buf[1024];bzero(buf, sizeof(buf));read(new_socekt, buf, sizeof(buf));printf(服务器接收到消息: %s\n, buf);sem_post(data);//V操作 数据资源1}
}//线程任务函数2
void *task2(void *arg)
{ int new_socekt *((int *)arg);while(1){sem_wait(data);//P操作 数据资源-1char buf[1024];printf(请输入发送给客户端的消息);fgets(buf, sizeof(buf), stdin);write(new_socekt, buf, strlen(buf));sem_post(space);//V操作 空间资源1}
}int main(int argc, char const *argv[])
{// 删除已存在的同名信号量如果有的话sem_unlink(SPACE);sem_unlink(DATA);// 打开有名信号量space sem_open(SPACE, O_CREAT, 0777, 1);data sem_open(DATA, O_CREAT, 0777, 0);// (1)创建套接字socketint ser_socket socket(AF_INET, SOCK_STREAM, 0);if (ser_socket -1){perror(socket);return -1;}// (2)初始化地址结构体 // IP地址PORT端口号struct sockaddr_in addr;addr.sin_family AF_INET; //地址簇addr.sin_port htons(56789); //端口一般以传参的传进来addr.sin_addr.s_addr inet_addr(192.168.159.128); //IP地址// addr.sin_addr.s_addr INADDR_ANY; //用特殊的0.0.0.0这个IP来绑定本机IP地址// (3)绑定地址结构体bindint b bind(ser_socket, (struct sockaddr *)addr, sizeof(struct sockaddr_in));if(b -1){perror(bind);return -1;}printf(绑定成功\n);// (4)开启监听listenint l listen(ser_socket, 3);if (l -1){perror(listen);return -1;}printf(监听成功\n);//ser_socket由 待链接套接字 变成 监听套接字while(1){// (5)等待连接acceptstruct sockaddr_in c_addr; //用来存放客户端链接成功之后的IP加端口int addrlen sizeof(c_addr);int new_socekt accept(ser_socket, (struct sockaddr *)c_addr, addrlen);if (new_socekt -1){perror(accept);return -1;}// new_socekt 链接成功之后用来通信的套接字printf(客户端【%s】【%u】连接成功\n, inet_ntoa(c_addr.sin_addr), c_addr.sin_port);// 创建两个线程用于双向通信pthread_t pid1, pid2;pthread_create(pid1, NULL, task1, new_socekt);pthread_create(pid2, NULL, task2, new_socekt);}// (7)关闭套接字close/shutdownclose(ser_socket);// shutdown(new_socekt, SHUT_RDWR);return 0;
}client.c
#include stdio.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include unistd.h
#include stdlib.h
#include strings.h
#include string.h
#include pthread.h
#include fcntl.h
#include sys/stat.h
#include semaphore.h#define SPACE /my_space // 使用唯一的路径名
#define DATA /my_datasem_t *space;
sem_t *data;//线程任务函数1
void *task1(void *arg)
{ int cli_socket *((int *)arg);while(1){sem_wait(space);//P操作 空间资源-1char buf[1024];printf(请输入消息);fgets(buf, sizeof(buf), stdin);write(cli_socket, buf, strlen(buf));sem_post(data);//V操作 数据资源1}
}//线程任务函数2
void *task2(void *arg)
{ int cli_socket *((int *)arg);while(1){sem_wait(data);//P操作 数据资源-1char buf[1024];bzero(buf, sizeof(buf));read(cli_socket, buf, sizeof(buf));printf(服务器返回消息: %s\n, buf);sem_post(space);//V操作 空间资源1}
}int main(int argc, char const *argv[])
{// 删除已存在的同名信号量如果有的话sem_unlink(SPACE);sem_unlink(DATA);// 打开有名信号量space sem_open(SPACE, O_CREAT, 0777, 1);data sem_open(DATA, O_CREAT, 0777, 0);// (1)创建套接字socketint cli_socket socket(AF_INET, SOCK_STREAM, 0);if (cli_socket -1){perror(socket);return -1;}// (2)初始化地址结构体服务器的 // IP地址PORT端口号struct sockaddr_in addr;addr.sin_family AF_INET; //地址簇addr.sin_port htons(56789); //服务器端的端口一般以传参的传进来addr.sin_addr.s_addr inet_addr(192.168.159.128); //服务器端的IP地址一般以传参的传进来// (3)建立连接connectint c connect(cli_socket, (struct sockaddr *)addr, sizeof(struct sockaddr_in));if (c -1){perror(connect);return -1;}printf(连接成功\n);// 创建两个线程用于双向通信pthread_t pid1, pid2;pthread_create(pid1, NULL, task1, cli_socket);pthread_create(pid2, NULL, task2, cli_socket);while(1);// (5)关闭套接字close/shutdownclose(cli_socket);// shutdown(cli_socket, SHUT_RDWR);return 0;
}如果遇到 bind:地址已被使用我们只需在服务器初始化addr之前加上这段代码来实现地址的复用即可。
int reuse 1;if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(void *)reuse,sizeof(reuse))0){perror(setsockopt error);return -1;}
四、 TCP协议如何保证可靠传输 TCP实现可靠传输的关键机制包括
序列号每个TCP报文段都有一个唯一的序列号接收端根据序列号来重组数据确保数据的顺序。检验和TCP在数据报文段中包含一个检验和字段用于检测数据在传输过程中是否发生损坏以便丢弃损坏的数据。确认应答信号接收端会向发送端发送确认应答告知发送端哪些数据已成功接收。如果发送端未收到确认应答它会重发数据。重发控制如果发送端未收到确认应答或检测到丢失的数据它会重新发送这些数据直到接收端确认接收。连接管理TCP建立连接时使用三次握手确保双方都准备好通信而关闭连接时使用四次挥手确保数据的完整传输。窗口控制TCP使用滑动窗口机制来控制数据的流动确保发送端不会发送过多数据从而防止接收端被淹没。流量控制TCP通过接收端发送窗口大小来告知发送端可以发送多少数据以避免接收端缓冲区溢出。拥塞控制TCP使用拥塞控制算法来避免网络拥塞包括慢开始、拥塞避免、快重传、快恢复等算法。
五、 当网络发生拥塞时TCP协议如何处理 TCP使用拥塞控制算法来应对网络拥塞情况以防止网络丢包率过高、延迟增加等问题。主要的拥塞控制算法包括 慢开始Slow Start在连接初始阶段发送端发送的数据段数量较小随着时间的推移逐渐增加以便观察网络状况。 拥塞避免Congestion Avoidance一旦慢开始阶段完成TCP进入拥塞避免阶段每个传输轮次中发送的数据段数量逐渐增加直到发生拥塞。 快重传Fast Retransmit如果接收端连续收到相同序列号的数据段表明某个数据段丢失它会立即向发送端发送重复确认触发发送端的快速重传。 快恢复Fast Recovery当发送端接收到快重传的重复确认时它不会进入慢开始而是将拥塞窗口减半然后继续逐渐增加。 这些算法共同确保TCP在遇到网络拥塞时能够降低发送速率以减少网络负载同时也通过快速重传和快速恢复来尽快恢复到正常传输状态。TCP的拥塞控制机制是维护网络稳定性和性能的关键部分。 更多C/C语言、Linux系统、数据结构和ARM板实战相关文章关注专栏 手撕C语言 玩转linux 脚踢数据结构 系统、网络编程 探索C 6818ARM开发板实战
写在最后
今天的分享就到这啦~觉得博主写的还不错的烦劳 一键三连喔~感谢关注