当前位置: 首页 > news >正文

网络编程之TCP编程

基于  C/S :客户端(client)/服务器端(server)

1.流程

       

2.  函数接口

所有函数所需头文件:

#include <sys/types.h> 
#include <sys/socket.h>

系统定义好了用来存储网络信息的结构体

ipv4通信使用的结构体:struct sockaddr_in

我们只需要直接定义结构体变量即可

2.1 创建套接字socket()

int socket(int domain, int type, int protocol);
功能:创建套接字
参数:domain:协议族AF_UNIX, AF_LOCAL  本地通信AF_INET            ipv4AF_INET6            ipv6type:套接字类型SOCK_STREAM:流式套接字SOCK_DGRAM:数据报套接字SOCK_RAW:原始套接字protocol:协议  一般填0 自动匹配底层 根据type系统默认自动帮助匹配对应协议传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)返回值:成功 文件描述符-- > sockfd (用于连接)失败 -1,更新errno

 注意:TCP服务器端有两类文件描述符 !!!
        一类用于连接的文件描述符(sockfd-->socket函数返回值) 只有一个
        一类用于通信的文件描述符(acceptfd-->accept函数返回值) 可以多个

2.2绑定套接字bind()

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:绑定
参数:socket:套接字addr:用于通信结构体 (提供的是通用结构体,需要根据选择通信方式,填充对应结构体-通信当时socket第一个参数确定)   addrlen:结构体大小   
返回值:成功 0   失败-1,更新errno

由于系统定义好的记录网络信息的结构体是struct sockaddr_in类型,因此,bind第二个参数使用时结构体变量地址的时候要强制类型转换

 

2.3监听listen()

int listen(int sockfd, int backlog);
功能:监听,将主动套接字变为被动套接字
参数:sockfd:套接字backlog:(目前已无具体作用,写个正数即可)同时响应客户端请求链接的最大个数,不能写0.不同平台可同时链接的数不同,一般写6-8个(队列1:保存正在连接)(队列2,连接上的客户端)返回值:成功 0   失败-1,更新errno 

注意:listen作用:主动套接字变为被动套接字!!!

2.4接收客户端连接请求 accept()

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

2.5接受消息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   成功接收的字节个数

2.6发送消息send()

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送数据
参数:sockfd:socket函数的返回值buf:发送内容存放的地址len:发送内存的长度flags:如果填0,相当于write();

2.7连接服务器connect()

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:用于连接服务器;
参数:sockfd:socket函数的返回值addr:填充的结构体是服务器端的;addrlen:结构体的大小
返回值 -1 失败,更新errno正确 0 

2.8 关闭套接字 close()

即关闭套接字文件

close(文件描述符);

3.服务器端

 按照流程:

(1)创建流式套接字socket()

     

(2)指定网络信息

 (3)绑定套接字bind()

(4) 监听listen()

(5) 等待客户连接信息accept()

 注意:

在服务器端使用客户的网络信息时:

(6)收发消息 send() recv()

(7) 关闭套接字

 

源代码:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>//  $$ 服务器端 $$  int main(int argc, char const *argv[])
{/*创建流式套接字*/int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket err");return -1;}else{printf("创建套接字成功\n");}/*指定服务器网络信息 使用的协议族(IPv4-->AF_INET)、IP地址、端口号等*/// 服务器的网络信息通过一个系统定义好的结构体来描述struct sockaddr_in saddr;                     // 定义一个结构体变量saddr.sin_family = AF_INET;                   // 确定协议族-->IPv4saddr.sin_port = htons(atoi(argv[1]));        // 确定使用的端口号saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 确定服务器IP地址/*绑定套接字*/int t1 = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));if (t1 < 0){perror("bind err");return -1;}else{printf("绑定套接字成功\n");}/*监听*/int t2 = listen(sockfd, 6); // 将默认的主动套接字变为被动套接字if (t2 < 0){printf("listen err");return -1;}else{printf("监听中\n");}int acceptfd;char buf[128] = "";int ret;// 定义一个结构体变量来存接收到的客户信息struct sockaddr_in caddr;int len = sizeof(caddr); // len是记录客户信息的结构体的大小while (1){/*阻塞等待接收客户端的连接请求,并将连接成功的客户端信息写入到结构体变量caddr中*/acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);if (acceptfd < 0){printf("accept err");return -1;}else{printf("等待接收客户端请求\n");}printf("客户IP:%s 端口号:%d \n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));while (1){/*接收消息*/ret = recv(acceptfd, buf, 128, 0); // 0-->相当于read(acceptfd,buf,128)if (ret < 0){perror("recv err");return -1;}else if (ret == 0){printf("客户退出\n");break;}else{printf("%s 接收成功\n", buf);memset(buf, 0, sizeof(buf));}}close(acceptfd);}/* 关闭套接字 */close(sockfd);return 0;
}

4.客户端

按照流程:

(1)创建流式套接字socket()

(2)指定服务器网络信息

(3)连接服务器connect()

(4)发送接受消息 send()  recv()

(5)关闭套接字

源代码:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
//  $$ 客户端 $$  int main(int argc, char const *argv[])
{/*创建流式套接字*/int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket err");return -1;}else{printf("创建套接字成功\n");}/*指定服务器的网络信息*/struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr(argv[2]);/* 请求连接服务器*/int t = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));if (t < 0){perror("connect err");return -1;}else{printf("connect success\n");}char buf[128] = "";/* 发送消息 */while (1){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n'){buf[strlen(buf) - 1] = '\0';}if (strcmp(buf, "quit") == 0){break;}send(sockfd, buf, sizeof(buf), 0); // 0-->相当于write(sockfd,buf,sizeof(buf))}/* 关闭套接字 */close(sockfd);return 0;
}

 

5.TCP粘包问题

 

tcp粘包

 

tcp拆包

6.三次握手四次挥手

 

 

相关文章:

  • 动态能力赋能:凯禾瑞华家务服务虚拟仿真实训室创新建设方案
  • centos 9/ubuntu 一次性的定时关机
  • CentOS7 + JDK8 虚拟机安装与 Hadoop + Spark 集群搭建实践
  • ubuntu 常用操作指令(与域控制器交互相关)
  • Kafka深度技术解析:架构、原理与最佳实践
  • 解决:如何在Windows adb使用dmesg | grep检查内核日志
  • Open SSL 3.0相关知识以及源码流程分析
  • @Prometheus 监控-MySQL (Mysqld Exporter)
  • 【openssl】升级为3.3.1,避免安全漏洞
  • C++算法训练营 Day8 字符串(1)
  • docker的基本命令
  • 前端工具库lodash与lodash-es区别详解
  • Python Flask中启用AWS Secrets Manager+AWS Parameter Store配置中心
  • 【HarmonyOS Next之旅】DevEco Studio使用指南(三十)
  • window 显示驱动开发-提供视频解码功能(二)
  • STM32----IAP远程升级
  • 前端面试三之控制语句
  • 4.1 HarmonyOS NEXT原生AI能力集成:盘古大模型端侧部署与多模态交互实战
  • OpenCV CUDA模块图像处理------创建一个模板匹配(Template Matching)对象函数createTemplateMatching()
  • OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
  • crm系统的销售管理功能包括/西安seo优化培训
  • 做挂网站吗/谷歌关键词推广怎么做
  • 房地产十大营销手段/郴州网站seo
  • 公司网站注销/百度视频免费下载
  • asp.net做网站源代码/给网站做seo的价格
  • html做分页的网站/论文收录网站排名