参考文章:https://blog.youkuaiyun.com/only_write_bug/article/details/148590269?spm=1001.2014.3001.5502
https://blog.youkuaiyun.com/only_write_bug/article/details/148590269?spm=1001.2014.3001.5502
inet_addr()和inet_aton()函数详解-优快云博客https://blog.youkuaiyun.com/only_write_bug/article/details/148604927?spm=1001.2014.3001.5502 地址位与数据位、大端和小端字节序、字节序转换-优快云博客
https://blog.youkuaiyun.com/only_write_bug/article/details/148603350?spm=1001.2014.3001.5502
一、环境配置
我们使用Visual Studio 2008版本。
1、导入头文件winsock2.h
2、链接ws2_32.lib库
打开项目的属性页,选中“配置属性”-->"输入"—>“附加依赖项”。也可Alt+F7打开“属性页”。
在右边空白处填入ws2_32.lib。之后我们只需要在源文件中添加头文件winsock2.h即可。
二、Winsock的初始化
进行Winsock编程时,首先调用WSAStartup函数,设置程序中要使用的Winsock版本。并初始化库。
#include <winsock2.h>
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSData);
//成功返回0,失败返回非0的错误码
//wVersionRequested:选中的Winsock版本信息
//lpWSData:WSADATA结构体变量的地址值
/* wVersionRequested:若传递0x0201,表示主版本号1,副版本号2. 高八位为副版本号,低八位为主版本号。
也可借助MAKEWORD宏函数构建版本信息
MAKEWORD(1,2):主版本号1,副版本号2,返回0x0201.
MAKEWORD(2,2):主版本号2,副版本号2,返回0x0202.
*/
//使用示例
int main()
{
WSADATA wsaData;//定义WSADATA 结构体
//调用初始化函数
if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
{
printf("WSAStartup error!\n");
return 0;
}
return 0;
}
三、基于Windows的套接字相关函数
#include <winsock2.h>
SOCKET socket(int af, int type, int protocol);
/* 成功返回套接字句柄, 失败返回INVALID_SOCKET*/
int bind(SOCKET s, const struct sockaddr*name, int namelen);
/* 与Linux系统的bind函数相同,实现IP和端口分配。 成功返回0, 失败返回SOCKET_ERROR*/
int listen(SOCKET s, int backlog);
/* 与Linux系统的Listen函数相同,实现接收客户端连接功能, 成功返回0,失败返回SOCKET_ERROR*/
SOCKET accept(SOCKET s, struct sockaddr*addr, int *addrLen);
/* 与Linux系统的accept函数相同,实现受理客户端连接请求,成功返回套接字句柄,失败返回INVALID_SOCKET*/
int connect(SOCKET s, const struct sockaddr*addr, int addrLen);
/* 与Linux系统的connect函数相同, 实现从客户端发送连接请求的功能,成功返回0,失败返回SOCKET_ERROR */
int closesocket(SOCKET s);
/*和Linux系统的close函数相同,实现关闭套接字连接,成功返回0,失败返回SOCKET_ERROR */
int send(SOCKET s, const char*buf, int len, int flags);
/*成功返回0,失败返回SOCKET_ERROR */
/*
s:传输数据的SOCKET套接字句柄
buf:要传输的数据的地址值
len:要传输的数据大小,字节数
flags:传输数据时用到的多种选项,一般为 0 即可
*/
int recv(SOCKET s, const char*buf, int len, int flags);
/*成功返回0,失败返回SOCKET_ERROR */
/*
s:接收数据的SOCKET套接字句柄
buf:要保存数据的地址值
len:要接收的最大数据大小,字节数
flags:接收数据时用到的多种选项,一般为 0 即可
*/
四、服务端与客户端创建示例
一、Windows网络通信客户端创建示例
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
int main()
{
char buff[100] = "hello, I am Client";
char recvbuff[1024] = {0};//接收信息
//1、初始化WSADATA结构体对象
WSADATA wsaData;
//2、初始化WSAStartup
if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
{
printf("WSAStartup init error!\n");
return 0;
}
//3、初始化套接字 TCP套接字SOCK_STREAM,PF_INET:IPV4互联网协议族
SOCKET cli_socket;
cli_socket = socket(PF_INET, SOCK_STREAM, 0);
if(cli_socket == INVALID_SOCKET)
{
printf("cli_socket init error!\n");
return 0;
}
//4、初始化结构体
SOCKADDR_IN ser_addr;//服务器IP和端口等信息
memset(ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_addr.s_addr = inet_addr("192.168.1.110");//指定服务器IP
ser_addr.sin_port = htons(8888);//指定服务器端口
int ser_addrLen = sizeof(ser_addr);
//5、与服务器建立连接
if(connect(cli_socket, (SOCKADDR*)&ser_addr, ser_addrLen) == SOCKET_ERROR)
{
printf("connect init error!\n");
return 0;
}
//6、接收发送信息
int recvlen = 0; //接收字节长度
//可添加循环退出标志 bool flag = true;
while(1)
{
//可添加循环判断
/*
if(!flag)
{break;}
*/
memset(recvbuff, 0, sizeof(recvbuff));//先清空
recvlen = recv(cli_socket, recvbuff, sizeof(recvbuff)-1, 0);
if(recvlen > 0)
{
printf("recvbuff is %s\n",recvbuff);
//发送数据到服务器
if(send(cli_socket, buff, sizeof(buff), 0) == SOCKET_ERROR)
{
printf("socket send information error!\n");
return 0;
}
//flag = false;
}
}
closesocket(cli_socket);//关闭套接字
WSACleanup();//清除WSA
return 0;
}
二、Windows网络通信服务端创建示例
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
int main()
{
char buff[100] = "hello, I am Server";
char recvbuff[1024] = {0};//接收信息
//1、初始化WSADATA结构体对象
WSADATA wsaData;
//2、初始化WSAStartup
if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
{
printf("WSAStartup init error!\n");
return 0;
}
//3、初始化套接字 TCP套接字SOCK_STREAM,PF_INET:IPV4互联网协议族
SOCKET ser_socket;//服务器监听套接字
ser_socket = socket(PF_INET, SOCK_STREAM, 0);
if(ser_socket == INVALID_SOCKET)
{
printf("ser_socket init error!\n");
return 0;
}
//4、初始化结构体
SOCKADDR_IN ser_addr;//服务器IP和端口等信息
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_addr.s_addr = INADDR_ANY;//指定服务器IP
ser_addr.sin_port = htons(8888);//指定服务器端口
int ser_addrLen = sizeof(ser_addr);
//5、绑定自身端口和IP信息
if(bind(ser_socket, (SOCKADDR*)&ser_addr, ser_addrLen) == SOCKET_ERROR)
{
printf("bind init error!\n");
return 0;
}
//6、监听连接对象
if(listen(ser_socket, 5) == SOCKET_ERROR)
{
printf("Listen init error!\n");
return 0;
}
//7、接收连接请求建立连接
SOCKET cli_socket;//客户端通信套接字
SOCKADDR_IN cli_addr;//客户端IP和端口等信息
int cli_addrLen = sizeof(cli_addr);
cli_socket = accept(ser_socket, (SOCKADDR*)&cli_addr, &cli_addrLen);
if(cli_socket == INVALID_SOCKET)
{
printf("accept client error!\n");
return 0;
}
//8、发送信息,接收信息
send(cli_socket, buff, sizeof(buff), 0);
int recvNum = 0;
while(1)
{
memset(recvbuff, 0, sizeof(recvbuff));
recvNum = recv(cli_socket, recvbuff, sizeof(recvbuff)-1, 0);
if(recvNum > 0)
{
printf("recvbuff is %s\n",recvbuff);
// 添加退出条件示例
if(strcmp(recvbuff, "exit") == 0) break;
}
}
closesocket(cli_socket);//关闭套接字
closesocket(ser_socket);//关闭套接字
WSACleanup();//清除WSA
return 0;
}
三、服务器部分添加多线程实现
// 使用多线程或select()处理多个客户端
while(1) {
SOCKET new_client = accept(...);
CreateThread(NULL, 0, ClientHandler, (LPVOID)new_client, 0, NULL);
}