TCP 套接字编程

  • 实验目的

通过 TCP 套接字编程实验,掌握网络套接字的构成,掌握 TCP 的通信特点,理解面向连接的通信方式;实现基于 TCP 的服务器/客户端通信过程。

  • 实验平台与实验设备(或实验环境)

能够连接网络的计算机一台;高级程序设计软件如 Microsoft Visual C++ 6.0

或 Code::Blocks。

  • 实验内容与实验步骤(如:任务、基本原理、软硬件设计、网络拓扑图、流程图、代码等)
  1. TCP 传输层协议的特点是什么?

连接只能有两个端点(endpoint),每一条连接只能是点对点的(一对一)。 是面向连接的运输层协议。提供全双工通信。提供可靠交付的服务;面向字节流

(1)提供协议端口来保证进程通信;

(2)提供面向连接的全双工数据传输;

(3)提供高可靠的按序传送数据的服务。

(2) 基于 TCP 的服务器/客户端通信,服务器与客户端的程序流程应该是怎么样的?请画出流程图。

服务器:

创建socket对象

使用bind()绑定主机号host和端口号port

使用listen()监听

使用accept()被动连接

使用send()/rece() 发送/接收数据

客户端:

创建socket对象

使用connect连接到服务器端

使用send()/recv() 发送/接收数据

关闭socket对象

  1. 基 于 Socket 的 TCP 通 信 中 , 用 到 的 系 统 函 数 有 socket(), bind(),listen(),connect(),accept(),send(), recv()。请给出每个函数的形式参数,并解释参数的含义。

socket()

socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor)当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。

bind(),

bind()函数把一个地址族中的特定地址赋给socket。

listen()

第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

connect(),

客户端通过调用connect函数来建立与TCP服务器的连接

accept(),

accept()是在一个套接口接受的一个连接。从s的等待连接队列中抽取第一个连接,创建一个与s同类的新的套接口并返回句柄

send(),

功能是向一个已经连接的socket发送数据,如果无错误,返回值为所发送数据的总数,否则返回SOCKET_ERROR

recv()

使用该函数可以方便地接收网络数据,从而实现各种网络应用程序

  • 实验数据和结果分析与实验思考题的回答(最好有实验截图)

通过编写 TCP 套接字程序,采用 C 程序设计语言,实现 TCP 的服务器/客户端相互之间数据的传送与接受。

(1)、编写程序时,在创建套接字 socket 时,采用socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)函数,函数的三个参数分别表示:Socket 的地址域,分为 unix(AF_UNIX,表示 unix 系统的两个进程可以通信)和 Internet(AF_INET,表示 Internet 上的任意两台主机可以通信);Socket 的类型,UDP 是数据报形式,DGRAM,而 TCP是流形式 SOCK_STREAM;协议类型,通常由系统自行设定是 TCP 还是 UDP;

结构体类型 sockaddr_in,是包括 Internet 地址的、含有四个域的类型;

函数 bind()将一个 Socket 绑定到特定的 IP 地址和端口上;

函数 listen( )表示特点的端口上进行侦听;

函数 connect()表示想特定的套接字发起连接;

函数 accept()表示有连接请求并且接受连接;

函数 send()表示向特定的 socket 地址发送数据;

函数 recv()表示从特定的 socket 地址接受数据。

(2)测试代码。打开 VC6.0,新建工程,分别启动服务端、客户端应用程序,在本地机器上实现 TCP 数据的发送与接收;

(3)测试两台主机,实现 TCP 数据的发送与接受;

  1. 实现字符数组的翻转与加解密操作。客户端从用户输入得到一个字符串,对字符串加密(采用凯撒加密),即将字符的 ASCII 码循环加一个整数,将加密结果发生给服务器端;服务器端收到字符后,进行解密,并在控制台显示解密后结果;对字符进行翻转后,采用凯撒加密,将加密结果发送给客户端;客户端收到该消息,进行解密,将解密结果在控制台显示。

(相关的关键代码需写在实验报告上)。

  1. 实现基于 TCP 的聊天软件设计。客户端与服务端相互之间能够发送聊天内容(文字),约定结束标识为“ByeBye”,即当某一方输入该“ByeBye”字符串后,程序结束通信,并且退出。

(相关的关键代码需写在实验报告纸上

服务器:

#include <winsock2.h>

#include <stdio.h>

#include <string.h>

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

typedef struct sockaddr_in sockaddr_in;

typedef struct WSAData WSAData;

typedef struct sockaddr sockaddr;

int main(int argc, char* argv[])

{//初始化WSA

    WORD sockVersion = MAKEWORD(2,2);

    WSADATA wsaData;

    if(WSAStartup(sockVersion, &wsaData)!=0)

        {            return 0;

        }

//创建套接字

    SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(slisten == INVALID_SOCKET)

        {            printf("socket error !");

            return 0;

        }

 //绑定IP和端口

    sockaddr_in sin;

    sin.sin_family = AF_INET;

    sin.sin_port = htons(8080);

    sin.sin_addr.S_un.S_addr = INADDR_ANY;

    if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)

//if(bind(slisten, (sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)

        {            printf("bind error !");

        }

//开始监听

    if(listen(slisten, 5) == SOCKET_ERROR)

        {            printf("listen error !");

            return 0;

        }

    SOCKET sClient;

    sockaddr_in remoteAddr;

    int nAddrlen = sizeof(remoteAddr);

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

    sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);

    if(sClient == INVALID_SOCKET)

        {

            printf("accept error !");

            closesocket(sClient);

            closesocket(slisten);

            WSACleanup();

            return 0;

        }

    printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));

    int i=0,j,tmp;

    char buf[255]={0};

    char revData[255];

    printf("\n实现字符数组的翻转与加解密操作\n");

    while(1)

    {   printf("\n等待客户端发送数据\n");

        int ret = recv(sClient,revData,255,0);

        if(ret>0)

        {   revData[ret] = 0x00;

            printf("\n接收到的数据:  ");

            printf(revData);

            printf("\n");

            if(strcmp(revData,"next")==0)

            {

                break;

            }

            while(revData[i] !='\0')

            {

                i++;

            }

            tmp = i;

            for(j=0;j<tmp;j++,i--)

            {

                buf[j]=revData[i-1];

            }

            printf("\n反转后的数据:  ");

            printf(buf);

            printf("\n");

            for(i=0;i<strlen(revData);i++)

            {if(revData[i]>='A'&&revData[i]<='Z')

                {

                    revData[i]=((revData[i]-'A')+26-4)%26+'A';

                }

                else if(revData[i]>='a'&&revData[i]<='z')

                {

                    revData[i]=((revData[i]-'a')+26-4)%26+'a';

                }

            }

            printf("\n解密后的数据:  ");

            printf(revData);

            printf("\n");

    }

     for(i=0;i<strlen(buf);i++)

            {  if(buf[i]>='A'&&buf[i]<='Z')

                {

                    buf[i]=((buf[i]-'A')+26-4)%26+'A';

                }

                else if(buf[i]>='a'&&buf[i]<='z')

                {

                    buf[i]=((buf[i]-'a')+26-4)%26+'a';

                }

            }

            printf("\n反转后的的加密数据:  ");

            printf(revData);

            printf("\n");

            send(sClient,buf,strlen(buf),0);

    }

    printf("\n**********聊天**********\n");

    while(1)

    {char str1[50];

        char str2[50];

        int ret = recv(sClient, str2, 255, 0);

        if(strcmp(str2,"byebye")==0)//判断

            {

                break;

            }

        if(ret > 0)

            {

                str2[ret] = 0x00;

                printf("\n来自client:  ");

                printf(str2);

                printf("\n");

            }

        printf("sever:  ");

        if(scanf("%s", str1))

            {

                printf("\n");

                send(sClient, str1, strlen(str1), 0);

                if(strcmp(str1,"byebye")==0)//判断

                    {

                        break;

                    }

            }

    }

        printf("\n开始退出\n");

        closesocket(slisten);

        closesocket(sClient);

        WSACleanup();

        return 0;

    }

客户端:

#include <winsock2.h>

#include <stdio.h>

#include <string.h>

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

typedef struct sockaddr_in sockaddr_in;

typedef struct WSAData WSAData;

typedef struct sockaddr sockaddr;

int main(int argc, char* argv[])

{

    WORD sockVersion = MAKEWORD(2,2);

    WSADATA data;

    if(WSAStartup(sockVersion, &data) != 0)

        {

            return 0;

        }

    SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if(sclient == INVALID_SOCKET)

        {

            printf("invalid socket !");

            return 0;

        }

    sockaddr_in serAddr;

    serAddr.sin_family = AF_INET;

    serAddr.sin_port = htons(8080);

    serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) ==

    SOCKET_ERROR)

        {

            printf("connect error !");

            closesocket(sclient);

            return 0;

        }

    int i;

    int len = sizeof(serAddr);

    char sentData[255];

    char recData[255];

    printf("\n实现字符数组的翻转与加解密操作\n");

    while(1)//实现字符数组的翻转与加解密操作

    {

        printf("\n发送数据\n");

        gets(sentData);

        if(strcmp(sentData,"next")!=0)

            {

                for(i=0; i<strlen(sentData); i++)//加密

                    {

                        if(sentData[i] >= 'A' && sentData[i] <= 'Z')

                            {

                                sentData[i] = ((sentData[i]-'A')+4)%26+'A';

                            }

                        else if(sentData[i] >= 'a' && sentData[i] <= 'z')

                            {

                                sentData[i] = ((sentData[i]-'a')+4)%26+'a';

                            }

                    }

                printf("\n加密后:  ");

                printf(sentData);

                printf("\n");

                send(sclient, sentData, strlen(sentData), 0);

                int ret = recv(sclient, recData, 255, 0);

                if(ret > 0)

                {

                    recData[ret] = 0x00;

                    for(i=0; i<strlen(recData); i++)//原字符解密

                    {

                        if(recData[i] >= 'A' && recData[i] <= 'Z')

                            {

                                recData[i] = ((recData[i]-'A')+26-4)%26+'A';

                            }

                        else if(recData[i] >= 'a' && recData[i] <= 'z')

                            {

                                recData[i] = ((recData[i]-'a')+26-4)%26+'a';

                            }

                    }

                    printf("\n解密翻转后的数据:  ");

                    printf(recData);

                    printf("\n");

                }

            }

            else

            {

                strcpy(sentData,"next");

                sendto(sclient, sentData, strlen(sentData), 0, (sockaddr *)&serAddr, len);

                break;

            }

    }

    printf("\n**********聊天**********\n");

    while(1)//实现基于 TCP 的聊天软件设计

    {

        char str1[50];

        char str2[50];

        printf("\nclient:  ");

        if(scanf("%s",str1))

        {

            send(sclient,str1,strlen(str1),0);

        }

        if(strcmp(str1,"byebye")==0)

        {

            break;

        }

        int ret = recv(sclient,str2,255,0);

        if(ret>0)

        {

            str2[ret]=0x00;

            printf("来自sever:  ");

            printf(str2);

            printf("\n");

            if(strcmp(str2,"byebye")==0)

            {                break;

            }

        }

    }

    closesocket(sclient);

    WSACleanup();

    return 0;

}

(1)、标识 TCP 套接字的四元组是什么?TCP 通信能否实现一个套接字对多个套接字的通信方式?为什么?

答:源IP,源端口,目标IP,目标端口

     TCP通信是一对一的点对点通信方式,一个套接字只能与一个套接字进行通信。要实现一个套接字与多个套接字的通信,可以使用并发编程技术,创建多个线程或进程来处理多个连接。

(2)、列举 3 种以上采用 TCP 传输层协议的网络应用程序。

答:TELNET,FTP,客户机/服务器模式,基于tcp的socket编程。

(3)、TCP 的拥塞控制有什么优点和缺点?

    优点:若对网络中某一个资源的需求超过了该资源所能提供的可用部分,网络性能就要降低。轻度拥塞状态: 网络的吞吐量明显小于理想的吞吐量时进入拥塞状态: 若出现拥塞而不控制,整个网络的吞吐量将随输入负荷的增大而下降 缺点:死锁: 吞吐量为0时,网络将无法工作

  • 实验总结与体会

在进行TCP套接字编程实验后,我得出以下总结:TCP套接字编程可用于实现客户端和服务器之间的通信。服务器端通过绑定地址、监听连接请求、接受连接和与客户端通信来提供服务,而客户端通过创建套接字对象、连接服务器、发送和接收数据来与服务器进行通信。还有就是代码的输入中英文区分,和大小之间的关系是32位在大小写直接进行转换以及反转大小写。TCP套接字编程能够提供可靠的数据传输和连接管理,适用于需要确保数据完整性和可靠性的应用程序,如网页浏览、文件传输和实时通信等。

实验过程可以帮助我们加深对实验的理解,发现实验的优点和不足,并提出改进和进一步研究的建议。同时,也可以总结自己在实验中的收获和成长。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是阿宇呢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值