计算机网络课程设计——基于Windows socket编程的简易聊天机器人

1.了解基本的socket API函数
WSAStartup()和WSACleanup()函数的关系
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第1张图片
WSAStartup()函数
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第2张图片
WSACleanup()函数
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第3张图片
socket()函数
功能是创建套接字,客户端和服务端都要调用的函数,产生各自的套接字,两端之间传输数据都要用到套接字,可以认为是两个识别码,用来区别。
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第4张图片
socket()函数第二个参数的选择方式,本例采用tcp,所以选SOCK_STREAM
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第5张图片
Closesocket()函数
关闭套接字
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第6张图片
bind()函数
功能是把套接字和IP地址绑定起来,在服务端调用
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第7张图片
listen()函数
使服务端处于监听状态,在服务端调用
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第8张图片
connect()函数
功能是连接服务端和客户端,在客户端调用
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第9张图片
accpet()函数
建立第一次连接,之后开始正常通话,在服务端调用
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第10张图片
send()函数,本例是TCP模式,采用send()函数,UDP模式采用sendto()函数
功能是发送数据
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第11张图片
recv()函数,本例是TCP模式,采用recv()函数,UDP模式采用recvfrom()函数
功能是接收数据
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第12张图片

//server.cpp

#include "stdafx.h"
#include   
#include  
#include
#pragma comment(lib, "WS2_32")
//文件链表,文件里都是一个个字符串。一个字符串是问题,下一个字符串是回答,依次排列
typedef struct node {
    char data[100];
    struct node *next;
}*LinkList, *pNode;
node *createlink()
{
    node *head = (node*)malloc(sizeof(node));
    char t[100];
    node *p;
    node *q;
    p  = head;
    FILE * r = fopen("input.txt", "r");//问题和回答都当做字符串保存在名为input的txt文件
    if (r == NULL)
    {
        printf("打开文件失败!");
        return NULL;
    }

    while (fscanf(r, "%s", &t) != EOF)
    {
        q = (node*)malloc(sizeof(node));
        strcpy(q->data, t);
        p->next = q;
        p = q;
    }
    p->next = NULL;
    return head;
}

int main()
{
    node *head;
    head = createlink();//建立链表
    head = head->next;//因为第一个是空结点,直接指向头结点的下一个结点

    WORD w;
    WSADATA wsdata;
    int err;
    w = MAKEWORD(1, 1);//请求1.1的winsock版本
    err = WSAStartup(w, &wsdata);//判断请求的winsock版本是否与本机相符合
    if (err!=0)//如果不相符,err==0,直接退出
    {
        return -1;
    }
    if (LOBYTE(wsdata.wVersion)!=1||HIBYTE(wsdata.wVersion)!=1)
    //再判断高低位是否都是1,确定是不是1.1的版本
    {
        WSACleanup();
        return -1;
    }
    /*
    SOCKADDR结构体和SOCKADDR_IN结构体的区别
    struct SOCKADDR
    {
    unsigned short sa_family;
    addressfamily,AF_xxx
    char sa_data[14];
    14bytesofprotocoladdress
    }
    struct SOCKADDR_IN

    {

    short sin_family;//Address family一般来说AF_INET(地址族)PF_INET(协议族)

    unsigned short sin_port;//Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)

    struct in_addr sin_addr;//IP address in network byte order(Internet address)

    unsigned char sin_zero[8];//Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐

    };
    typedef struct in_addr
    {
    union{
    struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b;
    struct { unsigned short s_w1,s_w2; } S_un_w;
    unsigned long S_addr;
    }S_un;
    }IN_ADDR;
    */
    SOCKET socket1 = socket(AF_INET, SOCK_STREAM, 0);//创建套接字,三个参数都是TCP模式相关的
    SOCKADDR_IN address;//定义结构体
    address.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    /*sin_addr是一个联合体,用联合体就可以使用多种方式表示IP地址.一般我们使用其中的最后一种方式,即用无符号长整型数据来表示IP地址.此时,用的是s_nu数据成员,s_un是一个结构体,其中只有一个数据成员,即s_addr.
    使用方式:
        变量.sin_family = AF_INET;
        变量.sin_addr.S_un.S.addr = htonl("IP地址");*/
    address.sin_family = AF_INET;
    address.sin_port = htons(1000);
    //htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为:高位字节存放在内存的低地址处。
    bind(socket1, (SOCKADDR*)&address, sizeof(SOCKADDR));
    listen(socket1, 10);//处于监听状态
    SOCKADDR_IN addrClient;//定义另一个结构体
    int len = sizeof(SOCKADDR);
    while (true)
    {
        SOCKET sockConn = accept(socket1, (sockaddr*)&addrClient, &len);
        char sendBuf[100]="欢迎来到聊天机器人\n";
        send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);//发送第一个字符串给客户端

        char recvBuf[100];
        recv(sockConn, recvBuf, 100, 0);
        printf("%s", recvBuf);
        while (true)//这里就是服务端和客户端的连接过程,一直进行
        {
            char recvBuf[100];
            recv(sockConn, recvBuf, 100, 0);//接收客户端发送的字符串
            printf("客户端说: %s\n",recvBuf);//打印客户端发送的字符串

            char talk[100];
            printf("你想说什么:");
            while (strcmp(recvBuf, head->data)!=0)
            //把客户端说的话当作字符串,在链表里找,并把它的下一句当作回答输出
            {
                head=head->next;//跳出时head->data就是客户端说的字符串后面的那个字符串
            }
            strcpy(talk, head->next->data);//拷贝给talk数组
            send(sockConn, talk, strlen(talk) + 1, 0);//发送给客户端
            printf("\n");
        }
        closesocket(sockConn);
    }
    return 0;
}
//client.cpp
#include "stdafx.h"
#include
#pragma comment(lib,"WS2_32")

int main()
{
    WORD w;
    WSADATA wsadata;
    int err;
    w = MAKEWORD(1,1);
    err = WSAStartup(w, &wsadata);
    if (err!=0) {
        return -1;    
    }
    if (LOBYTE(wsadata.wVersion)!=1||HIBYTE(wsadata.wVersion)!=1)
    {
        WSACleanup();
        return -1;
    }
    SOCKET sockclient = socket(AF_INET, SOCK_STREAM, 0);
    SOCKADDR_IN ClientAddr;
    ClientAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    ClientAddr.sin_family = AF_INET;
    ClientAddr.sin_port = htons(1000);
    connect(sockclient, (SOCKADDR*)&ClientAddr, sizeof(SOCKADDR));

    char recvBuf[100];
    recv(sockclient, recvBuf, 100, 0);
    printf("%s\n", recvBuf);
    send(sockclient, recvBuf, 100, 0);
    printf("开始聊天!\n");
    while (true)
    {
        char talk[100];
        printf("请说:");
        gets_s(talk);//获得输入的字符串并赋给talk数组
        send(sockclient, talk, strlen(talk) + 1, 0);//把talk数组发送给服务端

        char recvBuf[100];
        recv(sockclient, recvBuf, 100, 0);//接收服务端发出的消息
        printf("服务器说:%s\n", recvBuf);//打印服务端发送的消息
    }
    closesocket(sockclient);
    WSACleanup();
    return 0;
}

我的文件只有三句话,所以没有考虑客户端输出的字符串不在文件内的情况,读者自行设计,加入判断语句
跟工程文件放在一起
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第13张图片
建立两个win32控制台应用程序,分别命名server和client,代码输入完成之后先执行服务端,再执行客户端
运行效果图
计算机网络课程设计——基于Windows socket编程的简易聊天机器人_第14张图片

你可能感兴趣的:(数据结构,计算机网络,socket,tcp)