客户端:
记得在编译的时候加上这个 -lwsock32 -lws2_32
。
不然会报错 undefined reference to `__imp_WSAStartup‘
。
注意:如果端口在此之前被占了,则不会发生预期的结果
服务端,得到连接后创建线程,执行处理函数。
client.cpp
//client.cpp
#include <cstdio>
#include <winsock2.h>
#include <iostream>
#include <cstring>
#include <windows.h>
//#pragma comment(lib, "ws2_32.lib")
#define RECV_BUFFER_SIZE 8192
int main(int argc, char **argv)
{
//变量定义
SOCKADDR_IN clientService; //地址
SOCKET ConnectSocket; //Socket
WSADATA wsaData; //库
// LPVOID recvbuf; //接收缓存
char *recvbuf ;
// char recvbuf[64];
int bytesSent;
int bytesRecv = 0;
//默认的发送数据
// char sendbuf[32] = "get information";
char sendbuf[32] = "download file";
//初始化 Socket 库,保证 ws2_32.dll 已经加载
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(iResult != NO_ERROR){
printf("Error at WSAStartup()\n");
}
//创建 Socket
ConnectSocket = socket(AF_INET, //Ipv4
SOCK_STREAM, //顺序的、可靠的、基于链接的、双向数据流通信
IPPROTO_TCP //使用 TCP 协议
);
if(ConnectSocket == INVALID_SOCKET)
{
printf("Error at socket(): %ld\n",
WSAGetLastError());
WSACleanup();
return 3;
}
//设置服务端的通信协议、ip地址、端口
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
clientService.sin_port = htons(12321);
//连接到服务端
if( connect(
ConnectSocket, //socket
(SOCKADDR*) &clientService, //地址
sizeof(clientService) //地址的大小
) == SOCKET_ERROR
){
printf("Failed to connect(%d)\n",WSAGetLastError());
WSACleanup();
return 2;
}else{
printf("连上了~!\n");
}
//准备发送数据
// 如果输入的参数是 "-d" ,那么发生的数据事 "download file"
// 否则发送的数据是 "get information"
if(argc == 2 && (!lstrcmp(argv[1],"-d"))){
lstrcpyn(sendbuf, "download file", 32);
}
//向服务端发送数据
bytesSent = send(ConnectSocket, //socket
sendbuf, //发送的数据
lstrlen(sendbuf)+1, //数据长度
0); //无标志
if(bytesSent == SOCKET_ERROR){
printf("send error(%d)\n",
WSAGetLastError());
closesocket(ConnectSocket);
return 1;
}
printf("Bytes Sent: %ld\n",bytesSent);
//准备接收数据
// recvbuf = HeapAlloc(GetProcessHeap(), 0, RECV_BUFFER_SIZE);
recvbuf = (char*)malloc(sizeof(char)*RECV_BUFFER_SIZE);
//循环接收
while(bytesRecv != SOCKET_ERROR)
{
bytesRecv = recv(ConnectSocket, //socket
recvbuf, //接收数据缓存
RECV_BUFFER_SIZE, //缓存大小
0); //无标志
if(bytesRecv == 0){
printf("Connection Closed.\n");
break;
}
//TODO,处理接收到的数据,这里只简单地将收到的数据大小显示
printf("Bytes Recv: %ld\n", bytesRecv);
printf("data:%s\n",recvbuf);
}
// HeapFree(GetProcessHeap(), 0, recvbuf);
free(recvbuf);
WSACleanup();
return 0;
}
server.cpp
// server.cpp
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <cstring>
#include <cstdio>
//常量
#define DEFAULT_PORT "12321" //端口
#define MAX_REQUEST 1024 //接收数据的缓存大小
#define BUF_SIZE 4096 //发送数据的缓存大小
/***************************
用于接收和发送数据的线程
为每一个连接的客户端创建一个接收发送数据的线程,
可以使用多个客户端同时连接到服务端
**************************/
DWORD WINAPI CommunicationThread(LPVOID lpParameter)
{
DWORD dwTid = GetCurrentThreadId();
//获得参数 socket
SOCKET socket = (SOCKET)lpParameter;
//为接收数据分配空间
// LPSTR szRequest = HeapAlloc(GetProcessHeap(), 0, MAX_REQUEST);
char *szRequest = (char *)malloc(sizeof(char)*MAX_REQUEST);
int iResult;
//用于保存send 的返回值,既实际发送的数据的大小
int bytesSent;
//接收数据
iResult = recv(socket, //socket
szRequest, //接收缓存
MAX_REQUEST, //缓存大小
0); //标志
if(iResult == 0){ //接收数据失败,连接已关闭
printf("COnnection closing...\n");
// HeapFree(GetProcessHeap(), 0, szRequest);
free(szRequest);
closesocket(socket);
return 1;
}else if(iResult == SOCKET_ERROR){//接收数据失败,Socket 错误
printf("recv failed: %d\n",WSAGetLastError());
// HeapFree(GetProcessHeap(), 0, szRequest);
free(szRequest);
closesocket(socket);
return 1;
}else if(iResult > 0){ //接收数据成功
//显示接收到的数据
printf("\tCommunicationThread(%d)\tBytes received: %d\n",dwTid, iResult);
printf("\tCommunicationThread(%d)\trequest string is (%s)\n",dwTid, szRequest);
//如果接收到的数据是 "download file"
if(lstrcmpi(szRequest, "download file") == 0){
//读取文件 download.txt 将发送
HANDLE hFile;
// LPVOID lpReadBuf; //发生缓存
char *lpReadBuf;
DWORD dwBytesRead;
DWORD dwFileSize;
DWORD dwSendFile = 0;
hFile = CreateFile("download.txt",
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hFile == INVALID_HANDLE_VALUE){
printf("\tCommunicationThread\tCould not open file (error %d)\n",
GetLastError());
send(socket, "error", 6, 0);
closesocket(socket);
return 1;
}
//分配发送数据的缓存
// lpReadBuf = HeapAlloc(GetProcessHeap(), 0, BUF_SIZE);
lpReadBuf = (char*) malloc(sizeof(char) * BUF_SIZE);
//获取文件的大小
dwFileSize = GetFileSize(hFile, NULL);
//循环发送
while(1){
//读文件到缓存
if(!ReadFile(hFile, lpReadBuf, BUF_SIZE, &dwBytesRead, NULL)){
printf("\tCommunicationThread\tCould not read from file (error %d)\n",
GetLastError());
closesocket(socket);
CloseHandle(hFile);
return 1;
}
//发送读取的文件数据
bytesSent = send(socket, lpReadBuf, dwBytesRead, 0);
if(bytesSent == SOCKET_ERROR){
printf("\tCommunicationThread\tsend error %d\n",WSAGetLastError());
closesocket(socket);
CloseHandle(hFile);
return 1;
}
//显示发送数据的大小
printf("\tCommunicationThread(%d)\tsend %d bytes\n", dwTid, bytesSent);
//累加,已经发送的大小
dwSendFile += dwBytesRead;
//如果所以文件数据都已经发送
if(dwSendFile == dwFileSize){
printf("\tCommunicationThread\tFile download ok\n");
break;
}
}
//释放内存、关闭连接、关闭文件
// HeapFree(GetProcessHeap(), 0, lpReadBuf);
free(lpReadBuf);
CloseHandle(hFile);
closesocket(socket);
}
//如果接收到的数据 是 "get information"
else if(lstrcmpi(szRequest, "get information") == 0){
//发送数据
bytesSent = send(socket, //socket
"this is information", //数据
lstrlen("this is information")+1,//数据长度
0); //标志
//判断是否成功
if(bytesSent == SOCKET_ERROR){
printf("\tCommunicationThread\tsend error %d\n",
WSAGetLastError());
closesocket(socket);
return 1;
}
printf("\tCommunicationThread(%d)\tsend %d bytes\n",dwTid, bytesSent);
} else{
//收到未知数据
printf("unreferenced request\n");
}
}
//释放接收数据缓存 ,关闭Socket
// HeapFree(GetProcessHeap(), 0, szRequest);
free(szRequest);
closesocket(socket);
return 0;
}
/***************************
socket 服务端主程序
************************/
int __cdecl main(void)
{
WSADATA wsaData;
//监听 Socket
SOCKET ListenSocket = INVALID_SOCKET;
//连接 Socket
SOCKET ClientSocket = INVALID_SOCKET;
struct addrinfo *result = NULL, hints;
int iResult; //保存返回的结果
//初始化 WinSock,保证 Ws2_32.dll 已经加载
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(iResult != 0){
printf("WSAStartup failed: %d\n",iResult);
return 1;
}
//地址
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
//获取主机地址,保证网络协议可用等
iResult = getaddrinfo(NULL, //本机
DEFAULT_PORT, //端口
&hints, //使用的网络协议、连接类型等
&result); //结果
if(iResult != 0){
printf("getaddrinfo failed: %d\n",iResult);
WSACleanup();
return 1;
}
//创建 Socket,用于监听
ListenSocket = socket(
result->ai_family, //网络协议,AF_INET,Ipv4
result->ai_socktype, //类型,SOCK_STREAM
result->ai_protocol //通讯协议,TCP
);
if(ListenSocket == INVALID_SOCKET){
printf("socket failed: %ld\n",
WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
//绑定到端口
iResult = bind(ListenSocket,
result->ai_addr,
(int)result->ai_addrlen)
;
if(iResult == SOCKET_ERROR){
printf("bind failed: %d\n",WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
printf("bind\n");
freeaddrinfo(result); //reuslt 不再使用
//开始监听
iResult = listen(ListenSocket, SOMAXCONN);
printf("start listen......\n");
if(iResult == SOCKET_ERROR)
{
printf("listen failed: %d\n",WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
while(1){
//接收客户端连接,accept函数会等待,直到连接建立
printf("ready to accept\n");
ClientSocket = accept(ListenSocket, NULL, NULL);
//accept 函数返回,说明已经有客户端连接
//返回连接 Socket
printf("accept a connetion\n");
if(ClientSocket == INVALID_SOCKET){
printf("accept failed: %d\n",WSAGetLastError());
closesocket(ListenSocket);
break;//等待连接错误,退出循环
}
//为每一个连接创建一个数据发送的接收线程
//使服务端又可以立即接收到其他客户端的连接
if(!CreateThread(
NULL,
0,
CommunicationThread, //线程函数
(LPVOID)ClientSocket, //将Socket 作为传入参数
0,
NULL))
{
printf("Create Thread error (%d)",
GetLastError());
break;
}
}
//循环退出,释放DLL
WSACleanup();
return 0;
}
download.txt
Hello,This is download.txt
你好,这里是 download.txt 文件
hello, 这里是 download.txt file.
运行结果
先运行 server 再运行 client。