一、TCP/IP协议简介

什么是TCP/IP

TCP/IP协议是一种用于因特网的通信协议。TCP指传输控制协议(Transmission Control Protocol),IP指网际协议(Internet Protocol)。

TCP/IP协议簇中的协议

TCP/IP协议是一个协议簇,其中包含许多协议,囊括了应用层、传输层、网络层以及网络访问层。

  • 应用层包括:
    ①超文本传输协议(HTTP),万维网的基本协议
    ②TFTP文件传输协议
    ③Telnet远程登录协议
    ④SNMP网络管理协议
    ⑤DNS域名解析
  • 网络层包括:
    ①网际(IP)协议
    ②因特网消息控制协议ICMP
    ③ARP地址解析协议
    ④RARP反向地址解析协议
  • 网络访问层:
    网络访问层是TCP/IP协议簇的最底层,它提供物理网络的接口,实现对复杂数据的发送和接收。网络访问层协议为网络接口、数据传输提供了对应的技术规范。TCP/IP中的网络访问层对应OSI七层网络模型中的物理层和数据链路层。

TCP、UDP的区别

TCP、UDP都是TCP/IP协议簇中的通信协议,其中TCP(Transmission Control Protocol)是面向连接的协议,而UDP(User Data Protocol)则是一个非连接的协议。

二、TCP协议应用

TCP的三次握手

TCP协议在进行数据通信之前必须建立连接,而TCP建立连接采用“三次握手”的方式。

三次握手的过程就像打电话,如下:
小刘打电话给王哥,这个打电话的动作即发起连接请求(第一次握手),王哥听到来电铃声接起电话并说:“喂,小刘啊,听得到吗?”,这一步骤就如上图服务端响应客户端的连接请求(第二次握手),这时小刘听见电话里传来王哥的声音,知道王哥已经响应,便说道:“王哥,可以听到。”,这一步即上图中客户端对服务端的第三次握手,此时连接便已成功建立,小刘(客户端)和王哥(服务端)就可以进行通信了。

TCP的四次挥手


如同三次挥手像打电话一样,四次挥手也可以像挂断电话一般。如下:
小刘(客户端):王哥,事情大概就是这个样子。
王哥(服务端):好,这个事情你放心,没问题。
王哥(服务端):小刘,没啥事的话我就挂了啊。
小刘(客户端):好嘞,再见王哥。
此时连接断开。
拨打/挂断电话跟TCP的三次握手/四次挥手过程不完全一致,但可以用这个过程去初步理解,初步理解后再根据其原理进行深入理解。

TCP服务器

TCP服务器建立的流程是创建套接字、绑定套接字、监听套接字,然后进行收发消息、处理消息的操作。

1、创建套接字

函数原型,TCP协议的通信依靠套接字,在使用套接字之前,需要先使用socket()函数创建套接字,使用该函数时需要传入地址族(af)、数据传输方式/套接字类型(type)、传输协议三个参数(protocol)。
af为地址族(Address Family),即IP地址类型,常用的有AF_INET和AF_INET6。AF是"Address Family"的简写,INET是"Internet"的简写。AF_INET表示的是IPV4地址,AF_INET6则表示的是IPV6地址。(也可以使用PF(Protocol Family)前缀,与AF一致)
type为数据传输方式/套接字类型,常用的有SOCK_STREAM(数据流式套接字/面向连接的套接字)和SOCK_DGRAM(数据报式套接字/无连接的套接字)。
protocol为传输协议,常用的有IPPROTO_TCP和IPPROTO_UDP,分别表示TCP传输协议和UDP传输协议。在使用TCP或UDP协议时,由于SOCK_STREAM和SOCK_DGRAM分别只能用于TCP、UDP,因此该参数可以填0,系统会自行适配传输协议。

int socket(int af, int type, int protocol);
INT sockfd = 0;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(0 > sockfd)
{SaveLog(MODULE_TCPSERVER, "ERROR:Create socket fail\r\n");return -1;
}

2、绑定、监听套接字

函数原型,sock为socket文件描述符,addr为sockaddr结构体变量的指针,addrlen为addr变量的大小。

int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
struct sockaddr_in ServerAddr = {0};
memset(&ServerAddr, 0, sizeof(ServerAddr));
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(SERVER_PORT);
ServerAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
if(0 > bind(sockfd, (struct sockaddr*)&ServerAddr,sizeof(ServerAddr)))
{SaveLog(MODULE_TCPSERVER, "ERROR:Bind failed\r\n");return -1;
}

函数原型,sock为socket文件描述符,backlog为请求队列的最大长度。

int listen(int sock, int backlog);
if(0 < listen(sockfd, SERVER_MAX_CON))
{SaveLog(MODULE_TCPSERVER, "ERROR:Listen failed\r\n");return -1;
}
return sockfd;

3、接收连接

TCP服务器完成套接字的创建、绑定与监听操作后,需要调用accept函数接收客户端的连接,函数原型为:

int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);

accpet()函数的参数与listen()函数的一致,不过返回值为客户端的socket描述符。

INT connfd = 0;
connfd = accept(sockfd, NULL, NULL);
if(0 > connfd)
{SaveLog(MODULE_TCPSERVER, "ERROR:accept failed\r\n");
}

4、数据传输

连接建立后,即可进行数据的传输。主要使用recv()函数和send()函数。函数原型如下:

int send(SOCKET sock, const char *buf, int len, int flags);
int recv(SOCKET sock, char *buf, int len,int flags);

其中,sock为套接字,buf为要发送或者存储接收数据的缓冲区地址,len为发送/接收的数据的字节数,flags为发送/接收数据的选项(一般为0或者NULL即可)。recv()函数和send()函数的返回值都为接收/发送的实际字节数。

struct sockaddr ClientAddr = {0};
struct sockaddr_in ClientAddrIn = {0};
INT iRes = 0;
ULONG ulNameLen = 0;
CHAR *szBuf[1024] = {0};memset(&ClientAddr, 0, sizeof(ClientAddr));
memset(&ClientAddrIn, 0, sizeof(ClientAddrIn));
ulNameLen = sizeof(struct sockaddr);
connfd = (INT *)void_sockfd;if(0 == getsockname(connfd, &ClientAddr, (socklen_t *)&ulNameLen))
{memcpy(&ClientAddrIn, &ClientAddr, ulNameLen);SaveLog(MODULE_TCPSERVER, "INFO:Client connect,IP:%s:%d\r\n",inet_ntoa(ClientAddrIn.sin_addr),ntohs(ClientAddrIn.sin_port));
}
else
{SaveLog(MODULE_TCPSERVER, "ERROR:Get client IP address failed\r\n");
}
while(1)
{iRes = recv(connfd, szBuf, sizeof(szBuf), 0);if(0 > iRes){SaveLog(MODULE_TCPSERVER, "ERROR:Recv buf From %s failed,iRes:%d\r\n", inet_ntoa(ClientAddrIn.sin_addr), iRes);            }else if(iRes > 0){SaveLog(MODULE_TCPSERVER, "INFO:Recv buf:%s From:%s\r\nLen:%d\r\n", szBuf, inet_ntoa(ClientAddrIn.sin_addr), iRes);}else{SaveLog(MODULE_TCPSERVER, "INFO:Connect From %s stop\r\n", inet_ntoa(ClientAddrIn.sin_addr));return;}
}

服务器实例:

#include "tcp_server.h"
#include "../log/log.h"VOID main()
{INT sockfd = 0;sockfd = Socket_Init();if(0 > sockfd){printf("exec failed\r\n");return;}Socket_Process(sockfd);
}INT Socket_Init()
{INT sockfd = 0;    struct sockaddr_in ServerAddr = {0};memset(&ServerAddr, 0, sizeof(ServerAddr));sockfd = socket(AF_INET, SOCK_STREAM, 0);if(0 > sockfd){SaveLog(MODULE_TCPSERVER, "ERROR:Create socket fail\r\n");return -1;}ServerAddr.sin_family = AF_INET;ServerAddr.sin_port = htons(SERVER_PORT);ServerAddr.sin_addr.s_addr = inet_addr("0.0.0.0");if(0 > bind(sockfd, (struct sockaddr*)&ServerAddr,sizeof(ServerAddr))){SaveLog(MODULE_TCPSERVER, "ERROR:Bind failed\r\n");return -1;}if(0 < listen(sockfd, SERVER_MAX_CON)){SaveLog(MODULE_TCPSERVER, "ERROR:Listen failed\r\n");return -1;}return sockfd;
}VOID Socket_Process(INT sockfd)
{INT connfd = 0; pthread_t th_process;while(1){connfd = accept(sockfd, NULL, NULL);if(0 > connfd){SaveLog(MODULE_TCPSERVER, "ERROR:accept failed\r\n");}/* 连接成功后进入线程 */pthread_create(&th_process, NULL, Server_Process, (VOID *)connfd);        }
}VOID Server_Process(VOID *void_sockfd)
{INT connfd = 0;struct sockaddr ClientAddr = {0};struct sockaddr_in ClientAddrIn = {0};INT iRes = 0;ULONG ulNameLen = 0;CHAR *szBuf[1024] = {0};memset(&ClientAddr, 0, sizeof(ClientAddr));memset(&ClientAddrIn, 0, sizeof(ClientAddrIn));ulNameLen = sizeof(struct sockaddr);connfd = (INT *)void_sockfd;if(0 == getsockname(connfd, &ClientAddr, (socklen_t *)&ulNameLen)){memcpy(&ClientAddrIn, &ClientAddr, ulNameLen);SaveLog(MODULE_TCPSERVER, "INFO:Client connect,IP:%s:%d\r\n",inet_ntoa(ClientAddrIn.sin_addr),ntohs(ClientAddrIn.sin_port));}else{SaveLog(MODULE_TCPSERVER, "ERROR:Get client IP address failed\r\n");}while(1){iRes = recv(connfd, szBuf, sizeof(szBuf), 0);if(0 > iRes){SaveLog(MODULE_TCPSERVER, "ERROR:Recv buf From %s failed,iRes:%d\r\n", inet_ntoa(ClientAddrIn.sin_addr), iRes);            }else if(iRes > 0){SaveLog(MODULE_TCPSERVER, "INFO:Recv buf:%s From:%s\r\nLen:%d\r\n", szBuf, inet_ntoa(ClientAddrIn.sin_addr), iRes);}else{SaveLog(MODULE_TCPSERVER, "INFO:Connect From %s stop\r\n", inet_ntoa(ClientAddrIn.sin_addr));return;}}
}

TCP客户端

如同服务器,TCP客户端在数据传输之前也需要创建套接字,不同于服务器的是,客户端创建套接字后无需绑定、监听操作,直接使用connect()函数连接至服务器即可。

/************************
函数名:connect
参数:sock  套接字server_addr  服务器地址结构体addrlen  服务器地址结构体大小
返回值:0为成功,小于0失败
************************/
int connect(int sock, struct sockaddr *server_addr, socklen_t addrlen);

客户端实例

VOID main()
{INT sockfd = 0;   sockfd = ConnectServer();Client_Process(sockfd);
}VOID Client_Process(INT sockfd)
{INT i = 0;INT iRes   = 0;INT iMsgLen = 0;CHAR szBuf[1024] = {0};CHAR szHead[TCP_MSG_HEAD_LEN] = {0};sprintf(szBuf, "This is a hello msg!\r\nIs used to do test for server");/* 组装消息头 */iMsgLen = strlen(szBuf);PackMsgHead(MSG_PRINT, iMsgLen, szHead);/* 发送消息头 */iRes = send(sockfd, szHead, TCP_MSG_HEAD_LEN, 0);if(0 > iRes){SaveLog(MODULE_TCPCLIENT, "ERROR:Send buf failed,iRes:%d\r\n", iRes);return;}SaveLog(MODULE_TCPCLIENT, "INFO:Send head:%s\r\nLen:%d\r\n", szHead, iRes);sleep(1);/* 发送消息体 */iRes = send(sockfd, szBuf, iMsgLen, 0);if(0 > iRes){SaveLog(MODULE_TCPCLIENT, "ERROR:Send buf failed,iRes:%d\r\n", iRes);return;}SaveLog(MODULE_TCPCLIENT, "INFO:Send buf:%s\r\nLen:%d\r\n", szBuf, iRes);sleep(1);while(1);
}INT ConnectServer()
{INT sockfd = 0;struct sockaddr_in cli_addr;memset(&cli_addr, 0, sizeof(cli_addr));cli_addr.sin_family = AF_INET;cli_addr.sin_port   = htons(SERVER_PORT);cli_addr.sin_addr.s_addr = inet_addr(SERVER_IP);sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){SaveLog(MODULE_TCPCLIENT, "ERROR:Create socket fail\r\n");return -1;}if(0 > connect(sockfd, (struct sockaddr *)&cli_addr, sizeof(cli_addr))){SaveLog(MODULE_TCPCLIENT, "ERROR:connect server failed\r\n");return -1;}SaveLog(MODULE_TCPCLIENT, "INFO:connect server success\r\n");return sockfd;
}VOID PackMsgHead(TCP_MSG_TYPE_E eMsgType, ULONG ulMsgLen, CHAR *pcOutBuf)
{if(NULL == pcOutBuf){SaveLog(MODULE_TCPCLIENT, "Buf is null");return;}TCP_MSG_HEAD_S stTcpMsgHead;memset(&stTcpMsgHead, 0, sizeof(stTcpMsgHead));stTcpMsgHead.eMsgType = eMsgType;stTcpMsgHead.ulMsgLen = ulMsgLen;memcpy(pcOutBuf, (CHAR *)&stTcpMsgHead, sizeof(stTcpMsgHead));
}

相关结构体、宏定义等

/* tcp_client.h */
#ifndef _TCP_CLIENT_H_
#define _TCP_CLIENT_H_#include "../../common/common.h"#define SERVER_PORT     14000           //TCP服务器使用的端口号
#define SERVER_IP       "192.168.1.80"  //服务器IP地址INT ConnectServer();
VOID Client_Process(INT sockfd);#endif/* tcp_server.h */
#ifndef _TCP_SERVER_H_
#define _TCP_SERVER_H_#include "../../common/common.h"#define SERVER_PORT     14000
#define SERVER_MAX_CON  100     //最大连接数
#define SERVER_IP       "192.168.0.80"
#define MAX_LOG_SIZE    4096INT  Socket_Init();
VOID Socket_Process(INT sockfd);
VOID Server_Process(VOID *void_sockfd);#endif/* tcp.h */
#ifndef _TCP_H_
#define _TCP_H_
#include "../common/common.h"#define TCP_MSG_HEAD_LEN sizeof(TCP_MSG_HEAD_S)typedef enum
{MSG_PRINT = 0xFF00, //0xFF00MSG_ERROR           //0xFF01
}TCP_MSG_TYPE_E;typedef struct tagTcpMsgHead
{TCP_MSG_TYPE_E eMsgType;ULONG ulMsgLen;
}TCP_MSG_HEAD_S;#endif/* common.h */
#ifndef _COMMON_H_
#define _COMMON_H_#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <stddef.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <pthread.h>
#include <time.h>
#include <uinstd.h>#define INT         int
#define CHAR        char
#define FLOAT       float
#define DOUBLE      double
#define SHORT       short
#define LONG        long
#define UINT        unsigned int
#define ULONG       unsigned long
#define UCHAR       unsigned char
#define USHORT      unsigned short
#define VOID        void
#define STATIC      static
#define CONST       constextern INT Exec_Shell(CONST CHAR* pcCmd, CHAR *pcOutBuf, INT iLen);#endif

日志功能

/* log.c */
#include "log.h"VOID LogWright(CHAR *pcModule, CHAR *pcFile, CHAR *pcFunc, INT iLine, CONST CHAR *pcFormat, ...)
{va_list argList = {0};CHAR szLogTemp[MAX_LOG_SIZE] = {0};CHAR szLogHeader[256] = {0};CHAR szFileName[64] = {0};FILE *fpLogFile;CHAR szTime[64] = {0};INT iRes = 0;struct tm *stNowTime;time_t nowTime;time(&nowTime);stNowTime = localtime(&nowTime);sprintf(szFileName, "../log/%04d%02d%02d%s.log", stNowTime->tm_year + 1900, stNowTime->tm_mon + 1, stNowTime->tm_mday, pcModule);sprintf(szTime, "%d-%d-%d %d:%d:%d", stNowTime->tm_year + 1900, stNowTime->tm_mon + 1, stNowTime->tm_mday, stNowTime->tm_hour, stNowTime->tm_min, stNowTime->tm_sec);va_start(argList, pcFormat);    vsnprintf(szLogTemp, MAX_LOG_SIZE, pcFormat, argList);va_end(argList);snprintf(szLogHeader, sizeof(szLogHeader), "[%s]  [FILE: %s]  [FUNC: %s]  [LINE: %d] ------ ", szTime, pcFile, pcFunc, iLine);fpLogFile = fopen(szFileName, "a+");if(NULL == fpLogFile){printf("FILE:tcp_client_log.c LINE:%d fopen %s error!\r\n", __LINE__, szFileName);return;}fwrite(szLogHeader, 1, strlen(szLogHeader), fpLogFile);fwrite(szLogTemp, 1, strlen(szLogTemp), fpLogFile);fclose(fpLogFile);
}
/* log.h */
#ifndef _LOG_H_
#define _LOG_H_#include "../../common/common.h"#define MAX_LOG_SIZE    4096#define MODULE_TCPSERVER    "tcp_server"
#define MODULE_TCPCLIENT    "tcp_client"#define SaveLog(module, format, ...)\
{\LogWright((CHAR *)module, (CHAR *)__FILE__, (CHAR *)__FUNCTION__, (INT)__LINE__, format, ##__VA_ARGS__);\
}extern VOID LogWright(CHAR *pcModule, CHAR *pcFile, CHAR *pcFunc, INT iLine, CONST CHAR *pcFormat, ...);#endif

TCP协议服务端与客户端相关推荐

  1. C#创建TCP/IP服务端和客户端,含测试demo及源码

    网上的TCP/IP创建服务端和客户端的方法鱼龙混杂,自己把在使用项目中的TCP服务端和客户端的代码抽了出来,做了demo,以供大家使用参考. 为了方便调用,我把一些基础方法都整合封装到了Reader. ...

  2. windows Socket编程之TCP服务端与客户端

    在前面的文章中有一篇讲到了命名管道通信,它是创建一根管道来进行进程之间或网络之间通信的.但是它有些缺陷,比如说效率较低等.而从这篇文章开始将介绍socket编程.socket是通过TCP,UDP,IP ...

  3. 易语言tcp多线程服务端客户端_从TCP协议到TCP通信的各种异常现象和分析

    很多人总觉得学习TCP/IP协议没什么用,觉得日常编程开发只需要知道socket接口怎么用就可以了.如果大家定位过线上问题就会知道,实际上并非如此.如果应用在局域网内,且设备一切正常的情况下可能确实如 ...

  4. C#Winform窗体实现服务端和客户端通信例子(TCP/IP)

    Winform窗体实现服务端和客户端通信的例子,是参考这个地址 http://www.cnblogs.com/longwu/archive/2011/08/25/2153636.html 进行了一些异 ...

  5. TCP/IP网络编程之基于TCP的服务端/客户端(二)

    回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...

  6. OSI七层、TCP/IP五层、UDP、TCP的socket编程(服务端及客户端)、字节序转换、多进程以及多线程服务端的实现

    1.网络以覆盖范围划分:局域网/城域网/广域网   互联网/因特网   以太网/令牌环网--组网方式 2.在网络中必须能够为一表示每一台主机,才能实现点到点的精确通信            IP地址: ...

  7. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...

  8. java BIO tcp服务端向客户端消息群发代码教程实战

    前言 项目需要和第三方厂商的服务需要用TCP协议通讯,考虑到彼此双方可能都会有断网重连.宕机重启的情况,需要保证 发生上述情况后,服务之间能够自动实现重新通信.研究测试之后整理如下代码实现.因为发现客 ...

  9. 2-3 建立简易TCP服务端、客户端【socket server/client】【socket、bind、listen、accept、send、closesocket】【conect、recv】

    2-3 建立简易TCP服务端.客户端 文章目录 2-3 建立简易TCP服务端.客户端 0-前言 1-服务端简易功能 2-客户端简易功能 3-代码逻辑 4-服务端 4-1 建立socket 4-2 绑定 ...

最新文章

  1. 面试题整理18 根据上排给出十个数,在其下排填出对应的十个数
  2. 3_9 VisitorMode 访问者模式
  3. android资源收藏贴[持续更新]
  4. 杂志html转换服务平台,Verypdf HTML Converter(网页转换器)
  5. excel 统计字数公式
  6. 【UDP通过多线程改进,在一个窗口中同时接收又发送】
  7. 提供高速信号接口认证测试 GRL上海实验室成立
  8. JAVA学习-----容器和数据结构
  9. 10.7 a.m.小结
  10. 【竞赛02-a】【题解】第十二届电工杯全国大学生数学建模大赛B题
  11. 南方cass简码识别大全_CASS分类简码
  12. 中国AI专利数稳居第一!世界各国AI专利深度盘点
  13. vue项目文件命名规范推荐
  14. Shell脚本三种循环
  15. ggplot2画histogram(坐标轴刻度值字体大小,坐标轴标题字体大小,柱形宽度,大标题字体大小、居中)...
  16. 不是吧阿sir,你不会还不知道Github可以当做Maven仓库吧
  17. 程序员小心,中了这4条,别人就很难和你沟通
  18. 使用schrodinger建立深度学习QSAR模型
  19. 研究生常用的几种风险评估方法-专家调查法、蒙特卡洛模拟法、计划评审技术、敏感性分析法、决策树法、影响图法、模糊综合评价方法
  20. LyScript 插件实现UPX脱壳

热门文章

  1. orcad与pads的转换
  2. 【第一篇】Spring Cloud简介
  3. linux远程ipv6端口,SSH端口转发笔记(ipv6 与 端口映射)
  4. 应用:Application
  5. 亚马逊关键词首页排名技巧 让客户找到你
  6. Nagios bacula
  7. 编码的奥秘: ASCII码和字符映射
  8. [转载] 为研究网络游戏成瘾 我陪儿子玩王者荣耀
  9. html5+css3案例——仿瓢城旅行网
  10. 新手入门 | 跑分子动力学必须要知道的5个点