服务端和客户端通信-TCP(含完整源代码)

2023-05-16

简单TCP通信实验

目录

简单TCP通信实验

分析

1、套接字类型

2、socket编程步骤

3、socket编程实现具体思路

实验结果截图

程序代码


实验设备:   

目标系统:windows

软件工具:vs2022/VC6/dev

实验要求:

  1. 完成TCP服务端和客户端的程序编写;
  2. 实现简单字符串的收发功能。
  3. 需附上代码及运行结果截图。

实验内容:

分析

1套接字类型

  ① 流式套接字(SOCK_STREAM): 提供面向连接的、可靠的字节流服务,用于TCP。

  ② 数据报套接字(SOCK_DGRAM): 提供无连接的,不可靠的数据报服务,用于UDP。

  ③ 原始套接字(SOCK_RAW): 允许对较低层的协议,如IP、ICMP直接访问。

复习TCP三次握手过程,理解TCP socket编程。

2、socket编程步骤

服务器端:

创建socket----socket()

绑定的socket和端口号------bind()

监听该端口号----listen()

接收来自客户端的连接请求---accept()

从socket中读取字符----recv()

关闭socket---close()

客户端:

创建socket-----socket()

连接指定计算机的端口-----connect()

向socket中写入信息-----send()

关闭socket-----close()

3、socket编程实现具体思路

服务器端:

其过程是首先服务器方要先启动,并根据请求提供相应服务

    1)打开一通信通道并告知本地主机,它愿意在某一公认地址上的某端口接收客户请求;

    2)等待客户请求到达该端口;

    3)接收到客户端的服务请求时,处理该请求并发送应答信号。接收到并发服务请求,要激活一新进程来处理这个客户请求。新进程处理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止。

    4)返回第(2)步,等待另一客户请求。

    5)关闭服务器

客户端:

  1. 打开一通信通道,并连接到服务器所在主机的特定端口;
  2. 向服务器发服务请求报文,等待并接收应答;继续提出请求…
  3. 请求结束后关闭通信通道并终止。
  1. 代码实现过程分析

1)、创建套接字----socket()

应用程序在使用套接字前,首先必须拥有一个套接字,系统调用socket()向应用程序提供创建套接字的手段,其调用格式如下:

SOCKET PASCAL FAR socket(int af, int type, int protocol)

该调用要接收三个参数:af、type、protocol。

Af--------指定通信发生的区域:AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中仅支持AF_INET,它是网际网区域。因此,地址族与协议族相同。

Type----- 描述要建立的套接字的类型。这里分三种:

一、是TCP流式套接字(SOCK_STREAM)提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复地发送,且按发送顺序接收。内设流量控制,避免数据流超限;数据被看作是字节流,无长度限制。文件传送协议(FTP)即使用流式套接字。

二、是数据报式套接字(SOCK_DGRAM)提供了一个无连接服务。数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。网络文件系统(NFS)使用数据报式套接字。

三、是原始式套接字(SOCK_RAW)该接口允许对较低层协议,如IP、ICMP直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。

Protocol-----说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。

2)指定本地地址---bind()

当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关。其调用格式如下:

int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR * name, int namelen); 

s----是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。
name-----是赋给套接字s的本地地址(名字),其长度可变,结构随通信域的不同而不同。
namelen-----表明了name的长度。如果没有错误发生,bind()返回0。否则返回SOCKET_ERROR。

3)建立套接字连接---connect()与accept()

这两个系统调用用于完成一个完整相关的建立,其中connect()用于建立连接。accept()用于使服务器等待来自某客户进程的实际连接。connect()的调用格式如下:

int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR * name, int namelen);

参数s是欲建立连接的本地套接字描述符。
参数name指出说明对方套接字地址结构的指针。
对方套接字地址长度由namelen说明。
如果没有错误发生,connect()返回0。否则返回值SOCKET_ERROR。在面向连接的协议中,该调用导致本地系统和外部系统之间连接实际建立。、

accept()的调用格式如下:

SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen); 

参数s为本地套接字描述符,在用做accept()调用的参数前应该先调用过listen()。

addr指向客户方套接字地址结构的指针,用来接收连接实体的地址。addr的确切格式由套接字创建时建立的地址族决定。

addrlen为客户方套接字地址的长度(字节数)。

如果没有错误发生,accept()返回一个SOCKET类型的值,表示接收到的套接字的描述符。否则返回值INVALID_SOCKET。

4)监听连接---listen()

此调用用于面向连接服务器,表明它愿意接收连接。listen()需在accept()之前调用,其调用格式如下:

int PASCAL FAR listen(SOCKET s, int backlog);

参数s标识一个本地已建立、尚未连接的套接字号,服务器愿意从它上面接收请求。

backlog表示请求连接队列的最大长度,用于限制排队请求的个数,目前允许的最大值为5。

如果没有错误发生,listen()返回0。否则它返回SOCKET_ERROR。

listen()在执行调用过程中可为没有调用过bind()的套接字s完成所必须的连接,并建立长度为backlog的请求连接队列。

5)数据传输---send()与recv()

当一个连接建立以后,就可以传输数据了。常用的系统调用有send()和recv()。
send()调用用于s指定的已连接的数据报或流套接字上发送输出数据,格式如下:

int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags);

参数s为已连接的本地套接字描述符。
buf 指向存有发送数据的缓冲区的指针,其长度由len 指定。
flags指定传输控制方式,如是否发送带外数据等。如果没有错误发生,
send()返回总共发送的字节数。否则它返回SOCKET_ERROR。

recv()调用用于s指定的已连接的数据报或流套接字上接收输入数据,格式如下:

int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags);

参数s 为已连接的套接字描述符。
buf指向接收输入数据缓冲区的指针,其长度由len 指定。
flags指定传输控制方式,如是否接收带外数据等。
如果没有错误发生,recv()返回总共接收的字节数。如果连接被关闭,返回0。否则它返回SOCKET_ERROR。

6)关闭套接字---closesocket()

closesocket()关闭套接字s,并释放分配给该套接字的资源;如果s涉及一个打开的TCP连接,则该连接被释放。closesocket()的调用格式如下:

BOOL PASCAL FAR closesocket(SOCKET s);

参数s待关闭的套接字描述符。
如果没有错误发生,closesocket()返回0。否则返回值SOCKET_ERROR。


注意:client_in.sin_addr.S_un.S_addr = inet_addr()  

inet_addr()地址要查询本机的ip。或者直接用回环地址127.0.0.1

实验结果截图

 

 

程序代码:

客户端:

#include<stdio.h>

#include <Winsock2.h>

#pragma comment(lib, "Ws2_32.lib")

#include<windows.h>

int main()

{

  char sendBuf[1024];

  char receiveBuf[1024];

  while (1)

  {

        WSADATA wsadata;

        if (0 == WSAStartup(MAKEWORD(2, 2), &wsadata))

        {

              printf("等待连接....\n");

             

        }

        else

        {

                    printf("连接失败!\n");

 

        }

        SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);

        SOCKADDR_IN client_in;

        client_in.sin_addr.S_un.S_addr = inet_addr("172.29.18.16");//将网络地址字符串转换成二进制形式

        client_in.sin_family = AF_INET;

        client_in.sin_port = htons(6000);

        connect(clientSocket, (SOCKADDR*)&client_in, sizeof(SOCKADDR));

        recv(clientSocket, receiveBuf, 1024, 0);

        printf("收到来自服务器:  %s\n", receiveBuf);

      printf("向服务器发出: ");

        gets(sendBuf);

        send(clientSocket, sendBuf, 1024, 0);

        closesocket(clientSocket);

        WSACleanup();

  }

  return 0;

}

服务端:

#include<stdio.h>

#include <Winsock2.h>

#pragma comment(lib, "Ws2_32.lib")

#include<windows.h>

int main()

{

  char sendBuf[1024];

  char receiveBuf[1024];

  while (1)

  {

        //创建套接字,socket前的一些检查工作.

  //服务的启动

        WSADATA wsadata;//wsa 即windows socket async 异步套接字

        if (0 != WSAStartup(MAKEWORD(2, 2), &wsadata))

        {

              printf("未连接\n");

              return 0;

        }

        else

        {

              printf("连接成功!\n");

        }

       SOCKET serSocket = socket(AF_INET, SOCK_STREAM, 0);//创建可识别的套接字//parm1: af 地址协议族 ipv4 ipv6

                                                           //parm2:type 传输协议类型 流式套接字(SOCK_STREAM),数据包套接字(SOCK_DGRAM)

                                                           //parm3:ptotoc1 使用具体的某个传输协议

       

        SOCKADDR_IN addr;                                  //需要绑定的参数,主要是本地的socket的一些信息。

        addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);     //ip地址,htonl即host本机 to:to  n:net l:unsigned long 大端存储,低字节在高位

        addr.sin_family = AF_INET;

        addr.sin_port = htons(6000);                       //端口 htons将无符号短整型转化为网络字节序

        bind(serSocket, (SOCKADDR*)&addr, sizeof(SOCKADDR));

        listen(serSocket, 5);                             

        SOCKADDR_IN clientsocket;

        int len = sizeof(SOCKADDR);

        SOCKET serConn = accept(serSocket, (SOCKADDR*)&clientsocket, &len);

        printf("向客户端发出: \n");

        gets(sendBuf);

        send(serConn, sendBuf, 1024, 0);

        recv(serConn, receiveBuf,1024, 0);

 

        printf("收到来自客户端: %s\n", receiveBuf);

        closesocket(serConn);//关闭

        WSACleanup();//释放资源

  }

  return 0;

}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

服务端和客户端通信-TCP(含完整源代码) 的相关文章

  • A*寻路算法

    目录 1 动画演示2 游戏中的自动寻路A 算法3 A 寻路算法原理4 调试代码分析代码5 代码 1 动画演示 2 游戏中的自动寻路A 算法 随着3D游戏的日趋流行 在复杂的3D游戏环境中如何能使非玩家控制角色准确实现自动寻路功能成为了3D游
  • 2022数学建模国赛B题和C题高质量论文代码数据

    目录 B题论文 5 1 问题一的建模与求解 5 1 1 使用极坐标求解具体位置 C题论文 1 1 研究背景 1 2 问题的提 5 1 问题一的建模与求解 5 1 1 数据的预处理 B题论文 5 1 问题一的建模与求解 5 1 1 使用极坐标
  • stm32小白学习之寄存器名称

    IDR输入只读寄存器 xff0c ODR输出可读可写寄存器 BSRR xff08 置位寄存器 xff09 与BRR xff08 复位寄存器 xff09 CRL xff08 端口配置低位寄存器 xff09 与CRH xff08 端口配置高位寄
  • 使用Vite创建Vue3+TS项目并整合Element Plus框架等一条龙服务

    记录一下使用Vite创建Vue3 43 TS项目以及整合Element Plus框架 xff0c 还有Less Pinia Vue router monaco editor等插件或组件 一 使用Vite创建Vue3 43 TS项目 第一步
  • Qt学习 第37节:QString

    在阅读QString文档时 xff0c 出了一个词 implicit sharing copy on write xff0c 不是很懂 xff0c 下面链接解释的表明白 QT的隐式共享 Implicit Sharing 道路与梦想 CSDN
  • 下载Postman并且汉化使用

    下载Postman并且汉化使用 一 下载postman postman有不同的版本 xff0c 如果要汉化就要下载的版本与汉化包一致 下载地址 xff1a postman官网下载地址 xff1a https www postman com
  • 【Vue】postman汉化教程 保姆级教程 包教会

    下载链接 xff1a Win64 Win32 历史版本下载 请把下面链接的 34 版本号 34 替换为指定的版本号 xff0c 例如 xff1a 8 8 0 版本链接Windows32位https dl pstmn io download
  • 操作系统实验——进程与线程

    目录 1 使用GCC xff08 1 xff09 参数 xff08 2 xff09 自定义头文件 xff08 3 xff09 makefile脚本 xff08 4 xff09 gdb调试 2 进程 xff08 1 xff09 新建进程 xf
  • 串口应用(USART)

    串行口应用 1 USART介绍 通用同步异步收发器 USART 提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的 外部设备之间进行全双工数据交换 USART利用分数波特率发生器提供宽范围的波特率选择 它支持同步单向通信和半双工单线
  • cpp-httplib 避免阻塞主线程, c++封装httplib,httplib面向对象开发

    目录 说明 前言原生的httplib会阻塞你的主线程解决httplib阻塞主线程的问题BashController 面向对象风格使用httplib自定义controller MyController h文件自定义controller Tes
  • 数据结构——结构体的5种定义方式及对比

    以下仅为定义结构体的方式 xff0c 具体使用在后续的文章中介绍 span class token macro property span class token directive hash span span class token d
  • 【C++】STL应用(详解)

    一 泛型程序与STL1 泛型程序设计的基本概念2 STL简介 二 迭代器1 输入流迭代器2 输出流迭代器 三 STL应用1 撰写自己的算法和函数 xff0c 结合容器和迭代器解决序列变换 xff08 如取反 平方 立方 xff09 xff0
  • 模块学习(二)——MPU6050

    去年电赛备赛期间 xff0c 学的STM32标准库 xff0c 那一整个繁琐直接给我劝退了 xff0c 当时学习MPU6050时就非常痛苦 xff0c 代码也看不懂 xff0c 无非抄来抄去 xff0c 然后就是编译 xff0c 改错 xf
  • 【嵌入式面试题】常见的面试题梳理一

    注 xff1a 看面试题时 xff0c 主要应该以学习为主 xff0c 面试题有些基本上是我们编程时会遇上的问题 xff0c 通过学习面试题会提升我们的编程意识和解决一些日常我们编程所遇到的问题 xff0c 看完这篇面试题后 xff0c 希
  • 使用Python+openpyxl实现导出自定义样式的Excel文件

    之前项目中的导出Excel文件操作都是在前端完成的 xff0c 项目是由vue 43 vite构建的 xff0c 效果还不错的 xff0c 所需依赖包如下所示 npm i xlsx 64 0 18 5 npm i xlsx style vi
  • 【嵌入式面试题】常见面试题梳理二

    注 xff1a 看面试题时 xff0c 主要应该以学习为主 xff0c 面试题有些基本上是我们编程时会遇上的问题 xff0c 通过学习面试题会提升我们的编程意识和解决一些日常我们编程所遇到的问题 xff0c 看完这篇面试题后 xff0c 希
  • 【嵌入式面试题】常见面试题梳理三

    注 xff1a 看面试题时 xff0c 主要应该以学习为主 xff0c 面试题有些基本上是我们编程时会遇上的问题 xff0c 通过学习面试题会提升我们的编程意识和解决一些日常我们编程所遇到的问题 xff0c 看完这篇面试题后 xff0c 希
  • 【嵌入式面试题】常见面试题梳理四

    注 xff1a 看面试题时 xff0c 主要应该以学习为主 xff0c 面试题有些基本上是我们编程时会遇上的问题 xff0c 通过学习面试题会提升我们的编程意识和解决一些日常我们编程所遇到的问题 xff0c 看完这篇面试题后 xff0c 希
  • 【嵌入式面试题】常见面试题梳理五

    注 xff1a 看面试题时 xff0c 主要应该以学习为主 xff0c 面试题有些基本上是我们编程时会遇上的问题 xff0c 通过学习面试题会提升我们的编程意识和解决一些日常我们编程所遇到的问题 xff0c 看完这篇面试题后 xff0c 希
  • 【嵌入式面试题】常见面试题梳理六

    注 xff1a 看面试题时 xff0c 主要应该以学习为主 xff0c 面试题有些基本上是我们编程时会遇上的问题 xff0c 通过学习面试题会提升我们的编程意识和解决一些日常我们编程所遇到的问题 xff0c 看完这篇面试题后 xff0c 希

随机推荐