1.了解基本的socket API函数
WSAStartup()和WSACleanup()函数的关系
WSAStartup()函数
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
#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;
}
我的文件只有三句话,所以没有考虑客户端输出的字符串不在文件内的情况,读者自行设计,加入判断语句
跟工程文件放在一起
建立两个win32控制台应用程序,分别命名server和client,代码输入完成之后先执行服务端,再执行客户端
运行效果图