客戶端創建線程負責收發數據,其餘操作封裝在函數中,方便其他應用程序調用。服務端創建了兩個線程,一個用於收發數據,對於接收連接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 ); }