1. 程式人生 > >計算機網路課程設計——基於Windows socket程式設計的簡易聊天機器人

計算機網路課程設計——基於Windows socket程式設計的簡易聊天機器人

1.瞭解基本的socket API函式
WSAStartup()和WSACleanup()函式的關係
WSAStartup()和WSACleanup()函式的關係
WSAStartup()函式
WSAStartup()函式
WSACleanup()函式
WSACleanup()
socket()函式
功能是建立套接字,客戶端和服務端都要呼叫的函式,產生各自的套接字,兩端之間傳輸資料都要用到套接字,可以認為是兩個識別碼,用來區別。
這裡寫圖片描述
socket()函式第二個引數的選擇方式,本例採用tcp,所以選SOCK_STREAM
這裡寫圖片描述
Closesocket()函式
關閉套接字
這裡寫圖片描述
bind()函式
功能是把套接字和IP地址繫結起來,在服務端呼叫
這裡寫圖片描述
listen()函式
使服務端處於監聽狀態,在服務端呼叫
這裡寫圖片描述


connect()函式
功能是連線服務端和客戶端,在客戶端呼叫
這裡寫圖片描述
accpet()函式
建立第一次連線,之後開始正常通話,在服務端呼叫
這裡寫圖片描述
send()函式,本例是TCP模式,採用send()函式,UDP模式採用sendto()函式
功能是傳送資料
這裡寫圖片描述
recv()函式,本例是TCP模式,採用recv()函式,UDP模式採用recvfrom()函式
功能是接收資料
這裡寫圖片描述

//server.cpp

#include "stdafx.h"
#include <stdio.h>  
#include <Winsock2.h> 
#include<stdlib.h>
#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<WinSock2.h>
#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;
}

我的檔案只有三句話,所以沒有考慮客戶端輸出的字串不在檔案內的情況,讀者自行設計,加入判斷語句
跟工程檔案放在一起
這裡寫圖片描述
建立兩個win32控制檯應用程式,分別命名server和client,程式碼輸入完成之後先執行服務端,再執行客戶端
執行效果圖
這裡寫圖片描述