客户端创建线程负责收发数据,其余操作封装在函数中,方便其他应用程序调用。服务端创建了两个线程,一个用于收发数据,对于接收连接accept部分开一个线程,这样主窗口就不会因阻塞而挂掉。( 参考资料:《Windows API开发详解——函数、接口、编程实例》第十四章)
客户端client.c
/* 头文件 */
#include <stdio.h>
#include "winsock2.h"
#include <conio.h>
/* 常量 */
#define RECV_BUFFER_SIZE 8192
// 变量定义
SOCKADDR_IN clientService;// 地址
SOCKET ConnectSocket;// socket
WSADATA wsaData;// 库
HANDLE hThread;
char sendbuf[32] = "get information";// 默认发送的数据
void SendStr(LPSTR sendbuf,SOCKET socket1);
void InitSocket();
void CreateMyThread();
DWORD WINAPI CommunicationThread(
LPVOID lpParameter
);
/*************************************
* CommunicationThread
* 功能 用于接收和发送数据的线程
* 为每一个连接的客户端创建一个接收发送数据的线程,
* 可以使用多个客户端同时连接到服务端
* 参数 lpParameter,SOKCET
**************************************/
DWORD WINAPI CommunicationThread(
LPVOID lpParameter
)
{
// 获得参数sokcet
SOCKET socket = (SOCKET)lpParameter;
// 为接收数据分配空间
LPSTR szRequest = HeapAlloc(GetProcessHeap(),0,RECV_BUFFER_SIZE);
int iResult = 0;
int bytesSent;// 用于保存send的返回值,实际发送的数据的大小
while(1)
{
// 接收数据
iResult = recv(socket, // socket
szRequest, // 接收缓存
RECV_BUFFER_SIZE, // 缓存大小
0);// 标志
if (iResult == 0)// 接收数据失败,连接已经关闭
{
printf("Connection closing...\n");
HeapFree(GetProcessHeap(), 0 ,szRequest);
closesocket(socket);
return 1;
}
else if (iResult == SOCKET_ERROR)// 接收数据失败,socket错误
{
printf("recv failed: %d\n", WSAGetLastError());
HeapFree(GetProcessHeap(), 0 ,szRequest);
closesocket(socket);
return 1;
}
else if (iResult > 0) // 接收数据成功
{
// 显示接收到的数据
printf("Bytes received: %d\tContent: %s\n",iResult,szRequest);
// 如果接收到的数据是"download file"
if (lstrcmpi(szRequest, "start listen") == 0)
{
SendStr("COMING",socket);
}
// 如果接收到的数据是"get information"
else if (lstrcmpi(szRequest, "YEAH") == 0)
{
SendStr("123",socket);
}
else// 收到未知数据
{
printf ("unreferenced request\n");
}
}
}
// 释放接收数据缓存,关闭socket
HeapFree(GetProcessHeap(), 0 ,szRequest);
closesocket(socket);
return 0;
}
/*************************************
* main
* 功能 socket通信客户端
**************************************/
void main(int argc, char* argv[])
{
InitSocket();
CreateMyThread();
SendStr("request listen",ConnectSocket);
getch();
WaitForSingleObject(
hThread, // handle to mutex
INFINITE); // no time-out interval
WSACleanup();
return;
}
void InitSocket()
{
// 初始化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;
}
// 设置服务端的通信协议、IP地址、端口
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr( "222.31.66.209" );
clientService.sin_port = htons( 10000 );
// 连接到服务端
if ( connect(
ConnectSocket, // socket
(SOCKADDR*) &clientService, // 地址
sizeof(clientService) // 地址的大小
) == SOCKET_ERROR)
{
printf( "Failed to connect(%d)\n",WSAGetLastError() );
WSACleanup();
return;
}
}
void SendStr(LPSTR sendbuf,SOCKET socket1)
{
int bytesSent;
// 准备发送数据
// 如果输入参数是-d,那么发送的数据是“download file”否则是"get information"
// 向服务端发送数据
bytesSent = send( socket1, // socket
sendbuf,// 发送的数据
lstrlen(sendbuf)+1,// 数据长度
0 );// 无标志
if(bytesSent == SOCKET_ERROR)
{
printf( "send error (%d)\n", WSAGetLastError());
closesocket(socket1);
return;
}
printf( "Bytes Sent: %ld\t Content: %s\n", bytesSent,sendbuf);
}
void CreateMyThread()
{
// 为每一个连接创建一个数据发送的接收线程,
// 使服务端又可以立即接收其他客户端的连接
hThread = CreateThread(
NULL,
0,
CommunicationThread, // 线程函数
(LPVOID)ConnectSocket, // 将socket作为参数
0,
NULL);
if(!hThread)
{
printf("Create Thread error (%d)", GetLastError());
}
}
服务端 server.c
/* 头文件 */ #include <winsock2.h> #include <ws2tcpip.h> #include <stdio.h> /* 常量 */ #define DEFAULT_PORT "10000" // 端口 #define MAX_REQUEST 1024 // 接收数据的缓存大小 #define BUF_SIZE 4096 // 发送数据的缓存大小 WSADATA wsaData; SOCKET ListenSocket = INVALID_SOCKET;// 监听socket SOCKET ClientSocket = INVALID_SOCKET;// 连接socket struct addrinfo *result = NULL, hints; int iResult;// 保存返回结果 void InitSocket(); DWORD WINAPI AcceptThead(); void SendStr(LPSTR sendbuf,SOCKET socket); /************************************* * CommunicationThread * 功能 用于接收和发送数据的线程 * 为每一个连接的客户端创建一个接收发送数据的线程, * 可以使用多个客户端同时连接到服务端 * 参数 lpParameter,SOKCET **************************************/ DWORD WINAPI CommunicationThread( LPVOID lpParameter ) { // 获得参数sokcet SOCKET socket = (SOCKET)lpParameter; // 为接收数据分配空间 LPSTR szRequest = HeapAlloc(GetProcessHeap(),0, MAX_REQUEST); int iResult; int bytesSent;// 用于保存send的返回值,实际发送的数据的大小 while(1) { // 接收数据 iResult = recv(socket, // socket szRequest, // 接收缓存 MAX_REQUEST, // 缓存大小 0);// 标志 if (iResult == 0)// 接收数据失败,连接已经关闭 { printf("Connection closing...\n"); HeapFree(GetProcessHeap(), 0 ,szRequest); closesocket(socket); return 1; } else if (iResult == SOCKET_ERROR)// 接收数据失败,socket错误 { printf("recv failed: %d\n", WSAGetLastError()); HeapFree(GetProcessHeap(), 0 ,szRequest); closesocket(socket); return 1; } else if (iResult > 0) // 接收数据成功 { // 显示接收到的数据 printf("Bytes received: %d\tContent: %s\n",iResult, szRequest); // 如果接收到的数据是"download file" if (lstrcmpi(szRequest, "request listen") == 0) { SendStr("start listen",socket); } // 如果接收到的数据是"get information" else if (lstrcmpi(szRequest, "COMING") == 0) { // 发送数据 SendStr("YEAH",socket); } else if (lstrcmpi(szRequest, "123") == 0) { SendStr("333",socket); } else if (lstrcmpi(szRequest, "close") == 0) { break; } else// 收到未知数据 { printf ("unreferenced request\n"); } } } // 释放接收数据缓存,关闭socket HeapFree(GetProcessHeap(), 0 ,szRequest); closesocket(socket); return 0; } /************************************* * int __cdecl main(void) * 功能 socket服务端 **************************************/ int __cdecl main(void) { InitSocket(); AcceptThead(); // 循环退出,释放DLL。 WSACleanup(); return 0; } void InitSocket() { // 初始化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; } if(!CreateThread( NULL, 0, AcceptThead, // 线程函数 (LPVOID)ClientSocket, // 将socket作为参数 0, NULL)) { printf("Create Thread error (%d)", GetLastError()); } } DWORD WINAPI AcceptThead() { 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; } } } void SendStr(LPSTR sendbuf,SOCKET socket) { int bytesSent; // 准备发送数据 // 如果输入参数是-d,那么发送的数据是“download file”否则是"get information" // 向服务端发送数据 bytesSent = send( socket, // socket sendbuf,// 发送的数据 lstrlen(sendbuf)+1,// 数据长度 0 );// 无标志 if(bytesSent == SOCKET_ERROR) { printf( "send error (%d)\n", WSAGetLastError()); closesocket(socket); return; } printf( "Bytes Sent: %ld\tContent: %s\n", bytesSent,sendbuf ); }