当前位置:   article > 正文

Socket编程——TCP通信与UDP通信_socket programming with tcp

socket programming with tcp


前言

TCP(Transmission Control Protocol,传输控制协议)和UDP(User Data Protocol ,用户数据报协议)都属于TCP/IP协议簇。其中,TCP是面向连接的协议,在数据传输之前必须通过"三次握手"与对方建立可靠的连接,当数据传输完毕后通过"四次挥手"与服务端断开连接,这样可以保证数据传输的可靠性。也正是其的可靠性,TCP通信经常用于需要保证网络通信内容完整性的情况,比如网页HTTP、QQ文件传输等过程。而,UDP是一个面向无连接的协议,在数据传输之前,发送端和接收端不建立连接,发送端尽可能快的将数据扔到网络上,接收端从消息队列中读取消息段。如果接收方接收报文后校验失败,则直接将报文丢弃,不做任何处理。也正是因为这种特性,UDP通信一般用于要求网络通讯速度尽量的快(对网络通讯质量要求不高)的情况下,如QQ语音、QQ视频等。


一、TCP通信

TCP通信流程示意图

1、客户端源码

1.1、PreInclude.h

#ifndef PREINCLUDE_H
#define PREINCLUDE_H

#define WIN32_LEAN_AND_MEAN
//#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include <windows.h>
#include <WinSock2.h>
#include <stdio.h>
#include <WS2tcpip.h>
#include <thread>

#pragma comment(lib, "ws2_32.lib")//windows�µ��ö�̬��

#pragma pack(1)

struct DataPackage {
  int dealMode;
  char str[32];
};

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 65151

#endif //PREINCLUDE_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

1.2、Client.h

#ifndef CLIENT_H
#define CLIENT_H

#include "../PreInclude.h"

void ClientAPI();

#endif //CLIENT_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1.3、Client.cpp

#include "../Client/Client.h"

void ClientAPI()
{
  //加载Windows环境
  WORD ver = MAKEWORD(2, 2);
  WSADATA dat;
  WSAStartup(ver, &dat);

  //创建socket
  SOCKET Client_sock = socket(AF_INET, SOCK_STREAM, 0);
  if (INVALID_SOCKET == Client_sock) {
    printf("Build client's socket failed!!!\n");
  }
  else {
    printf("Build client's socket successful...\n");
  }
  //
  sockaddr_in Server_sin = {};
  Server_sin.sin_family = AF_INET;
  Server_sin.sin_port = htons(SERVER_PORT);
  //Server_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //INADDR_ANY为默认IP
  //将十进制SERVER_IP转化为二进制保存至Server_sin.sin_addr中
  inet_pton(AF_INET, SERVER_IP, &Server_sin.sin_addr);

  //调用connect函数连接服务端
  int connectRet = connect(Client_sock, (sockaddr*)&Server_sin, sizeof(sockaddr_in));
  if (INVALID_SOCKET == connectRet) {
    printf("Connect failed!!!\n");
  }
  else {
    printf("Connect successfully...\n");
  }

  //缓冲区
  while (true) {
    //输入发送信息
    DataPackage dp;
    scanf_s("%d",&dp.dealMode);
    //输入-1,表示结束
    if (dp.dealMode==-1) {
      break;
    }
    else {
      scanf_s("%s", dp.str,32);
      dp.str[strlen(dp.str)] = '\0';
      //调用send函数,将缓冲区内的数据发送至服务端
      send(Client_sock, (const char*)&dp, sizeof(DataPackage), 0);
    }
    //初始化接收缓冲区,调用recv函数进行数据接收
    char recvBuf[1024] = {};
    int nlen = recv(Client_sock, recvBuf, 127, 0);
    if (nlen > 0) {
        printf("Recv message successfully...\n");
        printf("Message:%s\n", recvBuf);
    }
    memset(recvBuf, 0, sizeof(recvBuf));
  }

  //关闭客户端连接
  closesocket(Client_sock);
  //
  WSACleanup();
  printf("Client closed...");
  getchar();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

2、服务端源码

2.1、Server.h

#ifndef SERVER_H
#define SERVER_H

#include "../PreInclude.h"

//服务端处理信息,将字符换变为大写
void StrToUpper(char* str,int len);

//服务端处理信息,将字符换变为小写
void StrToLower(char* str,int len);

void ServerAPI();

#endif //SERVER_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.2、Server.cpp

#include "../Server/Server.h"

//服务端处理信息,将字符换变为大写
void StrToUpper(char* str,int len)
{
  for(int i=0;i<len ;i++)
  {
    str[i]=toupper(str[i]);
  }
}
//服务端处理信息,将字符换变为小写
void StrToLower(char* str,int len)
{
  for(int i=0;i<len ;i++)
  {
    str[i]=tolower(str[i]);
  }
}

void ServerAPI()
{
  //加载Windows环境
  WORD ver = MAKEWORD(2, 2);
  WSADATA dat;
  WSAStartup(ver, &dat);
  
  //创建socket
  SOCKET Server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  sockaddr_in Server_sin = {};
  Server_sin.sin_family = AF_INET;
  Server_sin.sin_port = htons(SERVER_PORT);
  //Server_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //INADDR_ANY为默认地址
  //将十进制SERVER_IP转化为二进制保存至Server_sin.sin_addr中
  inet_pton(AF_INET, SERVER_IP, &Server_sin.sin_addr);

  //调用bind,将服务端本地地址到socket上
  if (SOCKET_ERROR == bind(Server_sock, (sockaddr*)&Server_sin, sizeof(Server_sin)))
  {
    printf("Bind failed!!!\n");
  }
  else {
    printf("Bind successful...\n");
  }

  //调用listen函数进行监听
  if (SOCKET_ERROR == listen(Server_sock, 5))
  {
    printf("Listen failed!!!\n");
  }
  else {
    printf("Listen successful...\n");
  }
  //调用accept接收客户端连接申请,并将客户端信息保存至Client_sin中
  sockaddr_in Client_sin = {};
  char IPdotdec[20];
  int nAddrLen = sizeof(sockaddr_in);

  SOCKET Client_sock = INVALID_SOCKET;
  Client_sock = accept(Server_sock, (sockaddr*)&Client_sin, &nAddrLen);
  if (Client_sock == INVALID_SOCKET)
  {
    printf("Accept Client_sin failed!!!\n");
  }
  else
  {
    printf("Accept clirntAddr successful... Sock = %d,IP = %s \n", (int)Client_sock, inet_ntop(AF_INET, &Client_sin.sin_addr, IPdotdec, 16));
  }

  char sendError[127]="The message is error!!!";
  char _recvBuf[128] = {};
  while (true)
  {
    //调用recv函数接收客户端发送的信息
    int nLen = recv(Client_sock, _recvBuf, 128, 0);
    if (nLen <= 0) {
      printf("Recv client's message failed!!!\n");
      break;
    }
    printf("Recv successful...");
    
    //根据接收到的信息,进行处理,并调用send返回对应的处理信息
    DataPackage* dp= (DataPackage*)_recvBuf;
    if(dp->dealMode==1){
      StrToUpper(dp->str,strlen(dp->str));
      send(Client_sock, (const char*)dp->str, strlen(dp->str), 0);
    }
    else if(dp->dealMode==2){
      StrToLower(dp->str,strlen(dp->str));
      send(Client_sock, (const char*)dp->str, strlen(dp->str), 0);
    }
    else{
      send(Client_sock, (const char*)sendError, strlen(sendError), 0);
    }
  }

  //当客户端断开连接后,会关闭客户端
  closesocket(Server_sock);
  //
  WSACleanup();
  printf("Sever closed ...");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101

3、运行示意图

运行示意图

二、UDP通信

UDP通信流程示意图

1、客户端源码

1.1、UDP_Client.h

#ifndef UDP_CLIENT_H
#define UDP_CLIENT_H

#include "../PreInclude.h"

void UDP_ClientAPI();

#endif //CLIENT_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1.2、UDP_Client.cpp

#include "../UDP_Client/UDP_Client.h"

void UDP_ClientAPI()
{
  const size_t BufSize=1024;
  char sendBuf[BufSize];
  char recvBuf[BufSize];

  //加载Windows环境
  WORD ver = MAKEWORD(2, 2);
  WSADATA dat;
  WSAStartup(ver, &dat);

  //创建socket
  SOCKET Client_sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (-1 == Client_sock) {
    printf("Build UDP_client's socket failed!!!\n");
  }
  else {
    printf("Build UDP_client's socket successful...\n");
  }
  //
  sockaddr_in Server_sin = {};
  Server_sin.sin_family = AF_INET;
  Server_sin.sin_port = htons(SERVER_PORT);
  //Server_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //INADDR_ANY为默认IP
  //将十进制SERVER_IP转化为二进制保存至Server_sin.sin_addr中
  inet_pton(AF_INET, SERVER_IP, &Server_sin.sin_addr);
  sockaddr_in Server_sock;
  socklen_t len=sizeof(Server_sock);

  //缓冲区
  while (true) {
    //输入发送信息
    printf("Please input:\n");
    scanf_s("%s", sendBuf, BufSize);
    sendBuf[strlen(sendBuf)] = '\0';

    //调用sendto函数,将缓冲区内的数据发送至服务端
    sendto(Client_sock, sendBuf, strlen(sendBuf), 0, (SOCKADDR*)&Server_sin, sizeof(SOCKADDR));

    int nlen = recvfrom(Client_sock, recvBuf, sizeof(recvBuf), 0, (SOCKADDR*)&Server_sock, &len);
    printf("%d", nlen);

    if (nlen > 0)
    {
      recvBuf[nlen] = '\0';      //给字符数组加一个'\0',表示结束了。不然输出有乱码
      if (strcmp(recvBuf, "bye") == 0)
      {
        printf("Server close!!!");//当服务器发来bye时,关闭socket
        closesocket(Client_sock);
        break;
      }
      else
      {
        printf("Recv message sucessful... Message:%s\n", recvBuf);
      }
    }
    memset(sendBuf, 0, sizeof(sendBuf));
    memset(recvBuf, 0, sizeof(recvBuf));
  }
  //关闭客户端连接
  closesocket(Client_sock);
  //
  WSACleanup();
  printf("Client closed...");
  getchar();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

2、服务端源码

2.1、UDP_Server.h

#ifndef UDP_SERVER_H
#define UDP_SERVER_H

#include "../PreInclude.h"

//服务端处理信息,将字符换变为大写
void StrToUpper(char* str,int len);

//服务端处理信息,将字符换变为小写
void StrToLower(char* str,int len);

void UDP_ServerAPI();

#endif //SERVER_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.2、UDP_Server.cpp

#include "../UDP_Server/UDP_Server.h"

//服务端处理信息,将字符换变为大写
void StrToUpper(char* str,int len)
{
  for(int i=0;i<len ;i++)
  {
    str[i]=toupper(str[i]);
  }
}
//服务端处理信息,将字符换变为小写
void StrToLower(char* str,int len)
{
  for(int i=0;i<len ;i++)
  {
    str[i]=tolower(str[i]);
  }
}

void UDP_ServerAPI()
{
  //加载Windows环境
  WORD ver = MAKEWORD(2, 2);
  WSADATA dat;
  WSAStartup(ver, &dat);
  
  //创建socket
  SOCKET Server_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  sockaddr_in Server_sin = {};
  Server_sin.sin_family = AF_INET;
  Server_sin.sin_port = htons(SERVER_PORT);
  //Server_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //INADDR_ANY为默认地址
  //将十进制SERVER_IP转化为二进制保存至Server_sin.sin_addr中
  inet_pton(AF_INET, SERVER_IP, &Server_sin.sin_addr);

  //调用bind,将服务端本地地址到socket上
  if (SOCKET_ERROR == bind(Server_sock, (sockaddr*)&Server_sin, sizeof(Server_sin)))
  {
    printf("Bind failed!!!\n");
  }
  else {
    printf("Bind successful...\n");
  }

  //调用accept接收客户端连接申请,并将客户端信息保存至Client_sin中
  sockaddr_in Client_sin = {};
  char IPdotdec[20];
  int nAddrLen = sizeof(sockaddr_in);

  char recvBuf[1024] = {};
  while (true)
  {
    //调用recv函数接收客户端发送的信息
    int nLen = recvfrom(Server_sock, recvBuf, 1024, 0,(SOCKADDR*) &Client_sin, &nAddrLen);
    if (nLen <= 0) {
      printf("Recv client's message failed!!!\n");
      break;
    }
    printf("Recv successful...");
    recvBuf[nLen]='\0';
    if(strcmp(recvBuf,"bye")==0)
    {
      printf("Client close!!!");      
      closesocket(Server_sock);  
      return ;
    }
    else
    {
      printf("Recv message sucessful from %s ...\n Message:%s\n", inet_ntoa(Client_sin.sin_addr), recvBuf);
    }
    StrToLower(recvBuf,strlen(recvBuf));

    sendto(Server_sock,recvBuf, strlen(recvBuf), 0, (SOCKADDR*)&Client_sin, sizeof(SOCKADDR)); 

    memset(recvBuf, 0, sizeof(recvBuf));
  }

  //当客户端断开连接后,会关闭客户端
  closesocket(Server_sock);
  //
  WSACleanup();
  printf("Sever closed ...");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83

3、运行示意图

UDP通信运行结果


源码地址:https://gitee.com/wang_jun_quan_admin/crawler
如需要,请自取。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/585085
推荐阅读
相关标签
  

闽ICP备14008679号