目录

1.TCP和UDP区别

2.TCP通讯程序的编写流程

3.实现一次循环通信

4.改进---多进程方式实现多次通信

5.改进---多线程方式实现多次通信

6.改进---多线程方式+字典方式实现多次通信


1.TCP和UDP区别

TCP:面向连接、可靠传输、面向字节流  应用场景:文件传输(安全性高于实时性)

UDP:无连接、不可靠、面向数据报  应用场景:视频、音频(实时性高于安全性)

2.TCP通讯程序的编写流程

服务器端:

  1. 创建套接字
  2. 为套接字绑定地址信息
  3. 开始监听---将套接字状态置为LISTEN 告诉系统这个套接字可以开始处理连接,tcp服务器会为每个客户端创建一个新的套接字用于与指定客户端通信
  4. 获取新建连接socket的套接字描述符
  5. 收发数据
  6. 关闭套接字

客户端:

  1. 创建套接字
  2. 绑定地址信息(不推荐客户端主动绑定地址信息)
  3. 向服务器发起数据
  4. 收发数据
  5. 关闭套接字

3.实现一次循环通信

代码如下:

tcp_socket.hpp:

#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>#define MAX_LISTEN 5
#define CHECK_RES(q) if((q)==false) { return -1;}
class TcpSocket{private:int _sockfd;public:TcpSocket():_sockfd(-1){}//创建套接字  bool Socket(){_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(_sockfd < 0){perror("socket error");return false;}return true;}//绑定地址信息bool Bind(const std::string &ip,uint16_t port){struct sockaddr_in addr;//先定义一个ipv4的地址结构addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip.c_str());socklen_t len = sizeof(struct sockaddr_in);int ret = bind(_sockfd,(struct sockaddr*)&addr,len);if(ret<0){perror("bind error");return false;}return true;}//向服务器发起连接bool Connect(const std::string &ip,uint16_t port){struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip.c_str());socklen_t len = sizeof(struct sockaddr_in);int ret = connect(_sockfd,(struct sockaddr*)&addr,len);if(ret < 0){perror("connect error");return false;}return true;}//服务器开始监听bool Listen(int backlog = MAX_LISTEN){int ret = listen(_sockfd,backlog);if(ret < 0){perror("listen error");return false;}return true;}//获取新建连接bool Accept(TcpSocket *sock,std::string *ip=NULL,uint16_t *port=NULL){struct sockaddr_in addr;socklen_t len = sizeof(struct sockaddr_in);int newfd = accept(_sockfd,(struct sockaddr*)&addr,&len);if(newfd<0){perror("accept error");return false;}sock->_sockfd = newfd;if(ip != NULL){*ip = inet_ntoa(addr.sin_addr);}if(port != NULL){*port = ntohs(addr.sin_port);}return true;}//接受数据bool Recv(std::string *body){char tmp[4096] = {0};int ret = recv(_sockfd,tmp,4096,0);if(ret < 0){perror("recv error");return false;}else if(ret == 0){std::cout<<"peer shutdown!\n";return false;}body->assign(tmp,ret);//从tmp中截取ret长度大小的数据return true;}//发送数据bool Send(const std::string &body){int ret;ret = send(_sockfd,body.c_str(),body.size(),0);if(ret < 0){perror("send error");return false;}return true;}//关闭套接字bool Close(){if(_sockfd!= -1){close(_sockfd);}return true;}};

tcp_srv.cpp:

#include "tcp_socket.hpp"
int main()
{TcpSocket lst_sock;//创建套接字CHECK_RES(lst_sock.Socket());//绑定地址信息CHECK_RES(lst_sock.Bind("192.168.164.128",20000));//开始监听CHECK_RES(lst_sock.Listen());while(1){//获取新建连接TcpSocket conn_sock;std::string cliip;uint16_t cliport;bool ret = lst_sock.Accept(&conn_sock,&cliip,&cliport);if(ret < 0){continue;}std::cout<<"new connect:"<<cliip<<":"<<cliport<<std::endl;//使用新建连接与客户端通信std::string buf;ret = conn_sock.Recv(&buf);if(ret == false){conn_sock.Close();continue;}std::cout<<"client say:"<<buf<<std::endl;std::cout<<"server say:";fflush(stdout);std::cin>>buf;ret = conn_sock.Send(buf);if(ret == false){conn_sock.Close();continue;}}// 关闭套接字lst_sock.Close();return 0;}

tcp_cli.cpp:

#include "tcp_socket.hpp"int main(int argc,char* argv[])
{if(argc!= 3){std::cout<<"please input server address!\n";std::cout<<"USsage:./tcp_cli 192.168.164.128 20000\n";return -1;}std::string srv_ip = argv[1];uint16_t srv_port = std::stoi(argv[2]);TcpSocket cli_sock;//创建套接字CHECK_RES(cli_sock.Socket());//绑定地址信息(客户端不推荐)//向服务器发起连接CHECK_RES(cli_sock.Connect(srv_ip,srv_port));while(1){//与服务器通信std::string buf;std::cout<<"client say:";fflush(stdout);std::cin>>buf;bool ret = cli_sock.Send(buf);if(ret == false){cli_sock.Close();return -1;}buf.clear();ret = cli_sock.Recv(&buf);if(ret == false){cli_sock.Close();return -1;}std::cout<<"server say:"<<buf<<std::endl;}//关闭套接字cli_sock.Close();return 0;}

makefile:

all:tcp_srv tcp_cli
tcp_cli:tcp_cli.cppg++ -std=c++11 $^ -o $@
tcp_srv:tcp_srv.cppg++ -std=c++11 $^ -o $@

测试结果如下:

先运行服务器端程序:

再运行客户端程序:

 客户端发送数据:

服务器端收到客户端发送的数据并进行回复:

客户端收到了服务器端的回复:

4.改进---多进程方式实现多次通信

服务器端代码改动 (cp tcp_srv.cpp process_srv.cpp):

process_srv.cpp代码如下:

#include "tcp_socket.hpp"
#include<signal.h>int new_worker(TcpSocket &conn_sock)
{pid_t pid = fork();if(pid < 0){perror("fork error");return -1;}else if(pid == 0){while(1){std::string buf;bool ret = conn_sock.Recv(&buf);if(ret == false){conn_sock.Close();break;}std::cout<<"client say:"<<buf<<std::endl;std::cout<<"server say:";fflush(stdout);std::cin>>buf;ret = conn_sock.Send(buf);if(ret == false){conn_sock.Close();break;}}conn_sock.Close();exit(-1);}
return 0;
}int main()
{//显示忽略子进程退出信号,相当于告诉系统,子进程退出直接释放资源signal(SIGCHLD,SIG_IGN); TcpSocket lst_sock;//创建套接字CHECK_RES(lst_sock.Socket());//绑定地址信息CHECK_RES(lst_sock.Bind("192.168.164.128",20000));//开始监听CHECK_RES(lst_sock.Listen());while(1){//获取新建连接TcpSocket conn_sock;std::string cliip;uint16_t cliport;bool ret = lst_sock.Accept(&conn_sock,&cliip,&cliport);if(ret < 0){continue;}std::cout<<"new connect:"<<cliip<<":"<<cliport<<std::endl;//使用新建连接与客户端通信new_worker(conn_sock);//父进程必须关闭套接字,否则连接的客户端越多,创建的套接字越多//但父进程本质并不与客户端通信,所以最后会资源耗尽//因此必须关闭,而父子进程数据独有,因此父进程的关闭并不影响子进程conn_sock.Close();} // 关闭套接字lst_sock.Close();return 0;}

makefile改动:

all:tcp_srv tcp_cli process_srv
process_srv:process_srv.cppg++ -std=c++11 $^ -o $@
tcp_cli:tcp_cli.cppg++ -std=c++11 $^ -o $@
tcp_srv:tcp_srv.cppg++ -std=c++11 $^ -o $@

测试结果(可实现多次通信):

 当前存在的问题:

若客户端1和客户端2都向服务器发送了数据,服务器在客户端1发送了数据之后并没有回复,在2发了之后才回复,此时回复的是客户端1。

测试结果如下(1向服务器发送tianyijingheile,2向服务器发送ganjinxiakeba,服务器端二者都可收到,回复xiake是回复给了客户端1):

客户端1:

客户端2:

 服务器端:

5.改进---多线程方式实现多次通信

cp tcp_srv.cpp thread_srv.cpp

thread_srv.cpp:

#include "tcp_socket.hpp"
#include<pthread.h>void* entry(void* arg)
{TcpSocket *conn_sock = (TcpSocket*)arg;while(1){  std::string buf;bool ret = conn_sock->Recv(&buf);if(ret == false){conn_sock->Close();break;}std::cout<<"client say:"<<buf<<std::endl;std::cout<<"server say:";fflush(stdout);std::cin>>buf;ret = conn_sock->Send(buf);if(ret == false){conn_sock->Close();break;}}conn_sock->Close();delete conn_sock;return NULL;
}
bool new_worker(TcpSocket *conn_sock)
{pthread_t tid;int ret = pthread_create(&tid,NULL,entry,(void*)conn_sock);if(ret != 0){std::cout<<"thread create error\n";return false;}pthread_detach(tid);return true;}int main()
{TcpSocket lst_sock;//创建套接字CHECK_RES(lst_sock.Socket());//绑定地址信息CHECK_RES(lst_sock.Bind("192.168.164.128",20000));//开始监听CHECK_RES(lst_sock.Listen());while(1){//获取新建连接,在堆上申请TcpSocket *conn_sock = new TcpSocket();std::string cliip;uint16_t cliport;bool ret = lst_sock.Accept(conn_sock,&cliip,&cliport);if(ret < 0){continue;}std::cout<<"new connect:"<<cliip<<":"<<cliport<<std::endl;//使用新建连接与客户端通信new_worker(conn_sock);}// 关闭套接字lst_sock.Close();return 0;}

makefile:

all:tcp_srv tcp_cli process_srv thread_srv
thread_srv:thread_srv.cppg++ -std=c++11 $^ -o $@ -lpthread
process_srv:process_srv.cppg++ -std=c++11 $^ -o $@
tcp_cli:tcp_cli.cppg++ -std=c++11 $^ -o $@
tcp_srv:tcp_srv.cppg++ -std=c++11 $^ -o $@

测试结果和多进程方式一样,存在的问题也相同。

6.改进---多线程方式+字典方式实现多次通信

cp thread_srv.cpp robot_srv.cpp

robot_srv.cpp:

#include "tcp_socket.hpp"#include<pthread.h>
#include<unordered_map>std::unordered_map<std::string,std::string>_dictionaries={{"nihao","你好"},{"leihou","雷猴"},{"hello","hi"}};std::string get_rsp(const std::string &key)
{ auto it = _dictionaries.find(key);if(it!= _dictionaries.end()){return it->second;}return "不要乱说话";
}void* entry(void* arg)
{TcpSocket *conn_sock = (TcpSocket*)arg;while(1){  std::string buf;bool ret = conn_sock->Recv(&buf);if(ret == false){conn_sock->Close();break;}std::string data = get_rsp(buf);  ret = conn_sock->Send(data);if(ret == false){conn_sock->Close();break;}}conn_sock->Close();delete conn_sock;return NULL;
}
bool new_worker(TcpSocket *conn_sock)
{pthread_t tid;int ret = pthread_create(&tid,NULL,entry,(void*)conn_sock);if(ret != 0){std::cout<<"thread create error\n";return false;}pthread_detach(tid);return true;}int main()
{TcpSocket lst_sock;//创建套接字CHECK_RES(lst_sock.Socket());//绑定地址信息CHECK_RES(lst_sock.Bind("192.168.164.128",20000));//开始监听CHECK_RES(lst_sock.Listen());while(1){//获取新建连接,在堆上申请TcpSocket *conn_sock = new TcpSocket();std::string cliip;uint16_t cliport;bool ret = lst_sock.Accept(conn_sock,&cliip,&cliport);if(ret < 0){continue;}std::cout<<"new connect:"<<cliip<<":"<<cliport<<std::endl;//使用新建连接与客户端通信new_worker(conn_sock);}// 关闭套接字lst_sock.Close();return 0;}

makefile:

all:tcp_srv tcp_cli process_srv thread_srv robot_srv
robot_srv:robot_srv.cppg++ -std=c++11 $^ -o $@ -lpthread
thread_srv:thread_srv.cppg++ -std=c++11 $^ -o $@ -lpthread
process_srv:process_srv.cppg++ -std=c++11 $^ -o $@
tcp_cli:tcp_cli.cppg++ -std=c++11 $^ -o $@
tcp_srv:tcp_srv.cppg++ -std=c++11 $^ -o $@

测试结果 客户端:

TCP通讯程序的编写相关推荐

  1. 威纶tk6070ik与台达变频器vdf-s485通讯程序 自己编写的威纶触摸屏与台达变频器的通讯

    威纶tk6070ik与台达变频器vdf-s485通讯程序 自己编写的威纶触摸屏与台达变频器的通讯程序,可以直接控制变频器正反转,启动停止,监视变频器的电流电压等及错误代码等. ID:981558944 ...

  2. 三菱fx3u通讯手册_三菱FX3U与变频器通讯程序如何编写

    前面我们讲了如何用串口调试工具通讯变频器,也学习了Modbus的报文格式.今天老冯教你们怎么用PLC写通讯程序. 首先看我们要准备的硬件: 1.三菱FX3U 2.三菱FX3U-485BD板 3.台达V ...

  3. 在C++ Builder中用socket api来写网络通讯程序(同时支持TCP和UDP协议)

    原标题:在C++ Builder中用socket api来写网络通讯程序(同时支持TCP和UDP协议) 原文:  http://www.csdn.net/develop/read_article.as ...

  4. java tcp 编程实例_Java实现基于TCP的通讯程序实例解析

    Java中的TCP通信程序 TCP可以实现两台计算机之间的数据交互通信的两端,要严格区分客户端与服务端 两端通信时的步骤: 1.服务端程序,需要事先启动,等待客户端连接 2.客户端主动连接服务器端,才 ...

  5. python socket recv超时_python使用多线程编写tcp客户端程序,你还没掌握吗?

    这篇文章主要为大家详细介绍了python使用多线程编写tcp客户端程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 在网上浏览的时候发现很多关于此题目的程序都只能接收数据,所以随便找了个程序研究 ...

  6. VC++编写USB接口通讯程序

    用VC++编写USB接口通讯程序 摘要:详细介绍Visual C++ 6.0环境下利用Windows API(Application Program Interface)函数来实现与符合HID设备类的 ...

  7. 《 Python程序设计项目案例》— 用Python开发的基于TCP通讯协议的私人聊天室 (期末大作业、结课作业、课程设计、毕业设计)

    基于Python与TCP协议的私人聊天室(GUI交互界面,用户注册.用户登录.实时聊天,文件上传与下载) 用Python开发的基于TCP通讯协议的实时聊天通讯和文件共享应用 目录 基于Python与T ...

  8. S7-Smart200-和变频器Modbus通讯 程序由smart200 编写,东源变频器的modbus通讯

    S7-Smart200-和变频器Modbus通讯 程序由smart200 编写,东源变频器的modbus通讯,控制启停由外部端子控制,通过通讯写入频率和读取运行电流的操作,软件版本smart V2.4 ...

  9. TCP/IP协议的编写《转载》

    基于HHARM9-EDU的TCP/IP(UDP)协议的实现 原文网址:http://blog.csdn.net/lhj0503/article/details/3323788 摘 要:嵌入式技术的发展 ...

最新文章

  1. pm2-zabbix 安装与配置
  2. 一个项目搞定支付宝,微信支付!
  3. python 线程同步_Python并发编程-线程同步(线程安全)
  4. 前端学习(1668):前端系列实战课程之限制范围拖拽思路
  5. 理解CSS3 max/min-content及fit-content等width值
  6. C语言 memcpy和memcpy_s区别 - C语言零基础入门教程
  7. 一个前端UI资源共享网站
  8. BZOJ 1034: [ZJOI2008]泡泡堂BNB
  9. 利用SPI编写类似sockscap的代理工具
  10. 职称计算机考试题库word2003,全国职称计算机考试题库(Word2003模块)
  11. 百度地图android去除logo,百度地图 Android版 隐藏logo
  12. 互联网变迁-真实化信息的转移
  13. flask form表单
  14. 360P 480P 720P 1080P 1080i 说明
  15. html头像动画,用CSS3实现头像旋转效动画
  16. 高仿滴滴打车 android,仿滴滴出行页面Demo
  17. JavaScript实现弹出浏览器的三种提示框:提示信息框、确认框和输入文本框
  18. 大蕉毕业三周年了,有话对你说 No.103
  19. 块设备驱动、bio理解
  20. windows与Linux实时传输数据,I01 物理隔离条件下Windows与Linux服务器的文件传输脚本...

热门文章

  1. 【python文字识别OCR】
  2. android歌词同步源码,Android KTV歌房歌词同步实现流程
  3. 艾司博讯:拼多多参加活动的价格怎么改
  4. 优雅的循环迭代和泛函数编程-purr packages 和 map 函数
  5. js逆向 签名参数解析 驾考数据科目一科目三题库爬虫分享 python scrapy
  6. 微机保护装置在江汉油田项目能源站中的应用
  7. 域名被用来干嘛的?你知道吗?
  8. 将数据写入json文件,并且读取json文件中的数据
  9. 嵌入式开发系列005-嵌入式产品开发体验
  10. 德国访问学者申请签证事宜