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

在Linux系统上实现TCP(socket)通信

一.什么TCP

TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

二.TCP通信流程

三. TCP 服务器端  

1 创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM tcp通信2 绑定(bind)
struct sockaddr_in myaddr;
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(8888);
myaddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY 自动提取本机ip地址
bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr));3 监听 (设置允许同时连接的客户端的最大值 同时连接:已经连上的,不算同时连接)
int listen(int sockfd, int backlog);
listen(sockfd, 5);4 阻塞等待连接 accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd :socket 的返回值
addr :客户端的ip地址和端口号存在addr中 (通常为NULL)//udp recvfrom倒数第二个参数一
样
addrlen :客户端的ip地址和端口号长度 (通常为NULL)//udp recvfrom倒数第一个参数一样
返回值(重点)
是一个newfd :(一个新的fd,此fd用来标识客户端,第一个连接的 4,下一个5,....)5 接收数据 (阻塞接收)
ssize_t recv(int newfd, void *buf, size_t len, int flags);
参数: newfd accept的返回值, newfd
buf 接收的数据存放的位置
len 将要接收的数据的长度
flags 暂时为0
返回值: 实际接收的数据的长度,如果<=0,则证明客户端已经断开连接6 发送数据
ssize_t send(int newfd, const void *buf, size_t len, int flags);
参数: newfd accept的返回值, newfd
buf 发送数据首地址
len 发送数据长度
flags 暂时为0
返回值: 实际发送的数据的长度7 关闭socket
close(newfd);
close(fd);
实例 :
server.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{int fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in myaddr;myaddr.sin_family = AF_INET;myaddr.sin_port = htons(8888);myaddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY 得到当前计算机的ip地址int ret = bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr));printf("ret %d\n",ret);listen(fd, 5);int newfd = accept(fd, NULL, NULL); //等待客户端连接char buf[100] = { 0 };ret = recv(newfd, buf, sizeof(buf), 0); //newfd 代表连接的客户端printf("ret %d, newfd is %d, buf is %s\n",ret, newfd, buf);close(newfd);close(fd);
}
执行:gcc hello.c -o server      ./server
另外起一个终端,执行:nc 127.0.0.1 8888 ( 模拟出一个客户端 ),在这里发送信息,服务器端就会收到信息。

四. TCP 客户端

1 创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM tcp通信 2 连接服务端
struct sockaddr_in youaddr;
youaddr.sin_family = AF_INET;
youaddr.sin_port = htons(8888);
youaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd socket的返回值
addr 保存对方的ip地址和端口
addrlen sizeof(youaddr);
返回值:成功 返回0 失败返回 -1
一旦连接成功,服务端解除阻塞(accpet)3 发送数据
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
sockfd socket的返回值
buf 发送的数据存放的位置,
len 发送的数据的长度(以字节为单位)
flags 暂时为04 接收数据 //同样用recv()5 关闭socket
close(fd);
实例 :
client.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{int fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in youaddr;youaddr.sin_family = AF_INET;youaddr.sin_port = htons(8888);youaddr.sin_addr.s_addr = inet_addr("127.0.0.1");connect(fd, (struct sockaddr *)&youaddr, sizeof(youaddr));char buf[] = "hello";send(fd, buf, sizeof(buf), 0);close(fd);
}
执行:gcc hello.c -o client      ./client      
另外起一个终端,执行:nc -l 8888(模拟服务器)
之后在执行./client的终端就可以给模拟的服务器发消息了。

五.练习

  实现服务器端循环收数据打印,客户端从 main函数的 参数中提取 ip 地址和端口号 , 可以循环从键盘输入数据,发数据, 如果客户端输入 ‘0’ ,则客户端退出。
server.c   (服务器端)
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{int fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in myaddr;myaddr.sin_family = AF_INET;myaddr.sin_port = htons(8888);myaddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY 得到当前计算机的ip地址int ret = bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr));listen(fd, 5);int newfd = accept(fd, NULL, NULL); //等待客户端连接while(1){char buf[100] = { 0 };if(recv(newfd, buf, sizeof(buf), 0) > 0) //newfd 代表连接的客户端printf("buf is %s\n", buf);elsebreak;}close(fd);
}

client.c   (客户端)

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{int fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in youaddr;youaddr.sin_family = AF_INET;youaddr.sin_port = htons(atoi(argv[2]));youaddr.sin_addr.s_addr = inet_addr(argv[1]);connect(fd, (struct sockaddr *)&youaddr, sizeof(youaddr));char buf[100] = "hello";while(1){gets(buf);if (buf[0] =='0'){break;}send(fd, buf, sizeof(buf), 0);}close(fd);
}

起两个终端一个执行服务器,一个执行客户端。

一个执行:gcc hello.c -o server      ./server

另一个执行:gcc hello.c -o client      ./client    192.168.133.5    8080     
//这里需要输入你的本地IP地址和端口号
这样客户端就能发送消息给服务器,服务器能一直接收消息。(实测成功)

六. 结语

这就是TCP套接字在Linux上使用的方法与步骤,本次的代码分享到此结束,感谢大家观看,希望大家点点赞,点点关注,后续还会发Linux系统上的TCP并发服务器(服务器能同时连多个客户端),谢谢!

相关文章:

  • c++20协程详解(三)
  • 19、差分矩阵
  • (Oracle)SQL优化技巧(一):分页查询
  • 计算机基础系列合集
  • 面试准备 集合 List
  • Python 新手最容易踩的坑
  • Scrapy 爬取m3u8视频
  • 基于springboot实现墙绘产品展示交易平台管理系统项目【项目源码+论文说明】
  • 基于BP神经网络的时间序列预测模型matlab代码
  • Spark-Scala语言实战(11)
  • loopvar 改动不同版本的影响-并发
  • 4.2.k8s的pod-标签管理、镜像拉取策略、容器重启策略、资源限制、优雅终止
  • Clion 输出乱码 解决方案
  • LeetCode热题100
  • 编程:不只是工作,是我生活的一部分
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • AHK 中 = 和 == 等比较运算符的用法
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • HashMap ConcurrentHashMap
  • HTTP--网络协议分层,http历史(二)
  • Java 最常见的 200+ 面试题:面试必备
  • java小心机(3)| 浅析finalize()
  • Koa2 之文件上传下载
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • MD5加密原理解析及OC版原理实现
  • vue-cli在webpack的配置文件探究
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • 复习Javascript专题(四):js中的深浅拷贝
  • 计算机在识别图像时“看到”了什么?
  • 漂亮刷新控件-iOS
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 首页查询功能的一次实现过程
  • 小程序测试方案初探
  • 学习笔记DL002:AI、机器学习、表示学习、深度学习,第一次大衰退
  • 正则与JS中的正则
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (C语言)fread与fwrite详解
  • (PHP)设置修改 Apache 文件根目录 (Document Root)(转帖)
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (九十四)函数和二维数组
  • (七)理解angular中的module和injector,即依赖注入
  • (三) diretfbrc详解
  • (一)80c52学习之旅-起始篇
  • (原創) 如何安裝Linux版本的Quartus II? (SOC) (Quartus II) (Linux) (RedHat) (VirtualBox)
  • (转)菜鸟学数据库(三)——存储过程
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • **PHP二维数组遍历时同时赋值
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例