文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-09-22 06:49

关注

简单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

#include

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

#include

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

#include

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

#include

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;

}

来源地址:https://blog.csdn.net/qq_51782199/article/details/129528327

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-服务器
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯