TCP 最主要的特点

TCP 是面向连接的运输层协议。应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接

每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的(一对一)

TCP 提供可靠交付的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达

TCP 提供全双工通信。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据

面向字节流。TCP 中的“流”指的是流入到进程或从进程流出的字节序列

“面向字节流”的含义是:虽然应用程序和 TCP 的交互式一次一个数据块(大小不等),但 TCP 把应用程序交下来的数据仅仅看成是一连串的无结构的字节流。TCP 并不知道所传送的字节流的含义

TCP 不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系

例如,发送方应用程序交给发送方的 TCP 共10个数据块,但接收方的 TCP 可能只用了4个数据块就把收到的字节流交付上层的应用程序

接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全一样。接收方的应用程序必须有能力识别收到的字节流,把它还原成有意义的应用层数据

TCP套接字编程  实现客户端到服务端到客户端的消息传输,简单的讲就是模仿QQ互发消息。

客户端代码

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
using namespace std;
//协议头
struct head
{int type;//协议体类型int len;
};
//协议实体 登录
struct login
{int id;char passwd[17];
};
//协议实体 聊天
struct chat
{int sendid;int recvid;char message[50];
};
//反馈头
struct feedback
{int type;//协议体类型int len;int flag;
};int isRun = 0;//收线程 发线程 共享标记值
void* writepthread(void* p)
{int wfd = *(int*)p;head HD = { 0 };login LG = { 0 };chat CT = { 0 };//登录cout << "请输入账号" << endl;cin >> LG.id;cout << "请输入密码" << endl;cin >> LG.passwd;//设定头HD.len = sizeof(login);HD.type = 1;//放入缓存区char lbuf[sizeof(head) + sizeof(login)] = { 0 };memcpy(lbuf, &HD, sizeof(head));memcpy(lbuf + sizeof(head), &LG, sizeof(login));//流式传输write(wfd, lbuf, sizeof(lbuf));while (1){//收到服务端反馈将标记值改为1,客户端可以开始收发消息if (isRun == 1){cout << "请输入本人账号" << endl;cin >> CT.sendid;cout << "请输入目标账号" << endl;cin >> CT.recvid;cout << "请输入信息" << endl;cin >> CT.message;bzero(&HD, sizeof(head));HD.type = 2;HD.len = sizeof(chat);char wbuf[sizeof(head) + sizeof(chat)] = { 0 };memcpy(wbuf, &HD, sizeof(head));memcpy(wbuf + sizeof(head), &CT, sizeof(chat));int res = write(wfd, wbuf, sizeof(wbuf));if (res > 0){cout << "发送成功" << endl;}bzero(&CT, sizeof(chat));}}
}
void* readpthread(void* p)
{feedback FB = { 0 };int rfd = *(int*)p;while (1){//流式传输read(rfd, &FB, sizeof(feedback));cout << "msgtype=  " << FB.type << endl;cout << "msglen=  " << FB.len << endl;cout << "msgflag=  " << FB.flag << endl;if (FB.type == 1)//登录反馈{if (FB.flag == 1)//登录成功{//收到服务端反馈将标记值改为1,客户端可以开始收发消息isRun = 1;}else//退出登录使用{isRun = 0;}}else if (FB.type == 2)//消息反馈{chat fCT = { 0 };read(rfd, &fCT, FB.len);cout << "发给对方的消息: " << fCT.message << endl;}else if (FB.type == 3)//消息接收{chat rCT = { 0 };read(rfd, &rCT, FB.len);cout << rCT.sendid << "给你发的消息: " << rCT.message << endl;}}}int main()
{struct sockaddr_in s_addr;int socketfd = 0;int len = 0;int pid = 0;//初始化 1.IPV4 2.流式socketsocketfd = socket(AF_INET, SOCK_STREAM, 0);if (socketfd == -1){perror("socket error");}else{//IPV4s_addr.sin_family = AF_INET;//系统获取本机IPs_addr.sin_addr.s_addr = inet_addr("192.168.211.131");//10000以下端口为系统使用,自定义不可超过65535s_addr.sin_port = htons(10086);len = sizeof(s_addr);//连接服务器if (connect(socketfd, (struct sockaddr*)&s_addr, len) == -1){perror("connect error");}else{cout << "client connect server" << endl;pthread_t pthreadid1;pthread_t pthreadid2;pthread_create(&pthreadid1, NULL, writepthread,&socketfd);pthread_create(&pthreadid2, NULL, readpthread, &socketfd);while (1)//不让进程结束,进程结束会导致线程结束{}}}return 0;
}

服务端代码

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <map>
#include <pthread.h>
#include <iterator>
using namespace std;struct head
{int type;int len;
};struct login
{int id;char passwd[17];
};struct chat
{int sendid;int recvid;char message[50];
};struct feedback
{int type;int len;int flag;
};
//存储在线用户
map<int, int> online;void* pthread_function(void* p)
{chat sevchat = { 0 };login LG = { 0 };head HD = { 0 };feedback FB = { 0 };int acpfd = *(int*)p;while (1){read(acpfd, &HD, sizeof(head));cout << "msgtype=  " << HD.type << endl;cout << "msglen=  " << HD.len << endl;if (HD.type == 1)//登录业务{int res=read(acpfd, &LG, sizeof(login));if (res == HD.len)//检查{cout << "userid=  " << LG.id << endl;cout << "userpasswd=  " << LG.passwd << endl;online[LG.id] = acpfd;cout << "在线用户人数" << online.size() << endl;FB.type = 1;FB.len = 0;FB.flag = 1;//假设登录成功char lbuf[sizeof(feedback)] = { 0 };memcpy(lbuf, &FB, sizeof(feedback));//反馈结果write(acpfd, lbuf, sizeof(lbuf));}}else if (HD.type == 2){int res = read(acpfd, &sevchat, sizeof(chat));if (res == HD.len)//检查{bzero(&FB, sizeof(feedback));//发送信息给接收方int sendid = online[sevchat.recvid];FB.len = sizeof(sevchat);FB.type = 3;char wbuf[sizeof(feedback) + sizeof(chat)] = { 0 };memcpy(wbuf, &FB, sizeof(feedback));memcpy(wbuf + sizeof(feedback), &sevchat, sizeof(chat));write(sendid, wbuf, sizeof(wbuf));//反馈给自己告诉发送方发送成功bzero(&FB, sizeof(feedback));FB.type = 2;FB.len = sizeof(sevchat);char fbuf[sizeof(feedback) + sizeof(chat)] = { 0 };memcpy(fbuf, &FB, sizeof(feedback));memcpy(fbuf + sizeof(feedback), &sevchat, sizeof(chat));write(acpfd, fbuf, sizeof(fbuf));}}}}int main()
{//chat sevchat = { 0 };pthread_t pthreadid;struct sockaddr_in s_addr;int socketfd = 0;int len = 0;int acceptfd = 0;int pid = 0;//char buf[50] = { 0 };//初始化 1.IPV4 2.流式socketsocketfd = socket(AF_INET, SOCK_STREAM, 0);if (socketfd == -1){perror("socket error");}else{//IPV4s_addr.sin_family = AF_INET;//系统获取本机IPs_addr.sin_addr.s_addr = INADDR_ANY;//10000以下端口为系统使用,自定义不可超过65535s_addr.sin_port = htons(10086);len = sizeof(s_addr);if (bind(socketfd, (struct sockaddr*)&s_addr, len) == -1){perror("bind error");}//监听这个地址和端口有没有客户端来来连接if (listen(socketfd, 10) == -1){perror("listen error");}cout << "The network channel is ready" << endl;//死循环保证服务器长期在线while (1){cout << "wait for client" << endl;//阻塞函数 等待客户端连接acceptfd=accept(socketfd, NULL, NULL);cout << "acceptfd=  " << acceptfd <<endl;//每连接进来一个用户开一个线程处理该用户的业务//为什么要把acceptfd传给线程,因为每连接进来一个客户端这个变量都会变化pthread_create(&pthreadid, NULL, pthread_function, &acceptfd);}}return 0;
}

运行结果

注意点

为什么要使用多线程来处理业务

应用程序为什么使用多线程处理业务,因为开线程的开销远小于开进程,线程执行时开销也小,且进程切换时开销大,效率低,线程程间可进行资源共享,一个服务器需要服务大量的客户端。

接收方的应用程序必须有能力识别收到的字节流,把它还原成有意义的应用层数据

通过定义的协议来识别字节流,协议=协议头+协议体,协议头有数据的类型,以及数据的长度。

接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全一样

通过协议中定义的一些东西进行检查,例如协议头的数据长度,因为网络传输会出现丢包,粘包,半包的现象

TCP Socket套接字编程 附源码相关推荐

  1. 三菱Q系列PLC编程TCP Socket套接字程序

    三菱Q系列PLC编程TCP Socket套接字程序 用于和上位机通讯 支持掉线自动后自动重连 附详细注释和指令说明! 编号:969665940010231沉默物语

  2. 【网络编程】Socket套接字;UDP数据报套接字编程;TCP流套接字编程

    文章目录 1. 什么是网络编程 2. 网络编程中的基本概念 3. Socket套接字 4 UDP数据报套接字编程 4.1 客户端服务器交互流程 4.2 UDP版本的回显服务 4.3 英译汉服务 5. ...

  3. 计网实验原理-TCP/UDP套接字编程

    计算机网络自顶向下结构--第7版 第二章实验,套接字编程 代码运行环境:window10,python 3.8.对于书上代码略作修改. 进程与计算机网络之间的接口 多数应用程序是由通信进程队组成的,每 ...

  4. 基于UDP协议的socket套接字编程 基于socketserver实现并发的socket编程

    基于UDP协议 的socket套接字编程 1.UDP套接字简单示例 1.1服务端 import socketserver = socket.socket(socket.AF_INET,socket.S ...

  5. 自学Python 58 Socket套接字编程 (一)

    Python Socket套接字编程 (一) 文章目录 Python Socket套接字编程 (一) 一.库 Socket 内置函数和属性 二.Python中的socket通信逻辑   Socket又 ...

  6. TCP与UDP协议,socket套接字编程,通信相关操作

    文章目录 TCP与UDP协议 TCP协议 ==三次握手== ==四次挥手== UDP协议 TCP与UDP的区别 应用层 socket套接字 代码优化 循环通信 半连接池 粘包问题 TCP与UDP协议 ...

  7. TCP与UDP协议、socket套接字编程、通信相关操作(cs架构软件)、TCP黏包问题及解决思路

    OSI七层协议 传输层 1.PORT协议:前面讲过 2.TCP协议与UDP协议:规定了数据传输所遵循的规则(数据传输能够遵循的协议有很多,TCP和UDP是较为常见的两个) TCP协议 基于TCP传输数 ...

  8. Linux之socket套接字编程20160704

    介绍套接字之前,我们先看一下传输层的协议TCP与UDP: TCP协议与UDP协议的区别 首先咱们弄清楚,TCP协议和UCP协议与TCP/IP协议的联系,很多人犯糊涂了,一直都是说TCP/IP协议与UD ...

  9. 网络编程---TCP/UDP套接字编程原理

    本篇介绍的是Linux下的网络编程,故有些接口是不适用于Windows的,但是具体概念和实现方法是大体一致的 本篇重在讲解原理,具体实现请戳这里->UDP套接字编程实现 介绍 网络编程套接字(s ...

最新文章

  1. 特朗普“模仿”奥巴马?进阶版换脸技术DeepFakes来了
  2. python数据清理的实践总结_python 数据的清理行为实例详解
  3. 数据结构——队列(C语言实现)
  4. Django框架(24.Django中的模板的自定义过滤器)
  5. oracle orm 实例 java_Oracle数据库的JDBC查询实例
  6. Android实现小圆点显示未读功能
  7. 无法Debug SQL: Unable to start T-SQL Debugging. Could not attach to SQL Server process on
  8. MySQL 复制技术的发展
  9. LIO-SAM探秘第三章之代码解析(二) --- featureExtraction.cpp
  10. JAVA 项目中使用 H2 数据库
  11. 微服务 订单交易支付系统架构演进
  12. 一百行代码自制局域网双端快速传输文件系统
  13. 深度学习环境安装所需软件介绍cuda+cudnn+driver+anaconda+keras+tensorFlow+Pycharm+Jupyer(下载地址+配图)
  14. [转]铁路客车列车座位分布
  15. iOS开发:Protocol协议以及委托代理传值
  16. ValueError: only single character unicode strings can be converted to Py_UCS4, got length 0
  17. 马赛克颗粒感天空Canvasjs特效
  18. 阿里云机器学习PAI-快速上手指南
  19. 常用封装电阻的常用电阻阻值
  20. Android Mms专题之:Mms概览介绍

热门文章

  1. 计算机毕业设计源码案例基于ssm幼儿园管理系统
  2. 图像处理中不适定问题(ill posed problem)
  3. Quartus 11.0 Internal Error veriname_elab.cpp
  4. Android游戏: 五子棋-局域网版
  5. 34. 池化层 / 汇聚层
  6. 红外激光手指反射,对兴趣点的提取
  7. 手握低代码,得工具者得天下 低代码永远滴神
  8. PMP 考试 、PMP 备考方案
  9. 郑州高新区php平均收入,郑州公布各区平均工资,你又拖后腿了?
  10. 人民币汇改将是慢舞曲