socket服務器其實網上有很多的代碼,但是大部分是基於Linux的,或者是Windows下的c++語言完成的設計,而且大部分是一對一的連接。今天發表一個C語言完成的socket服務器,可以實現一對多,多個客戶端可以用IP和發送的數據來區分不同。另外,我的多客戶端不是選擇進程和多線程的辦法,但是對於有需要的,我會添加線程和進程的使用。
注意:這是vs2017的環境,但是對於Windows大部分的編譯器來說,是沒有問題的,如果不行,對於報錯的信息進行修改就可以了。如果是Linux的系統下,需要改的稍微多一點,但是萬變不離其宗,基本上就是頭文件名,參數和函數名的修改,整體的結構是我實驗了很多次的。
1.socket服務器的代碼 (一對一)
#include"pch.h"
#include <stdio.h>
#include <winsock2.h>
//#include<iostream>
//#define MaxSize 10000
#pragma comment(lib,"ws2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS//解決:inet_ntoa函數和無法使用inet_ntop的問題
int main(int argc, char* argv[])
{
//初始化WSA
WORD sockVersion = MAKEWORD(2, 2);//操作系統根據請求的Socket版本來搜索相應的Socket庫,
//然後綁定找到的Socket庫到該應用程序中。
//以後應用程序就可以調用所請求的Socket庫中的其它Socket函數
WSADATA wsaData;//WSADATA是一種數據結構,存儲WSAStartup函數返回的數據
if (WSAStartup(sockVersion, &wsaData) != 0)//第一個參數是版本號,第二個參數是保存start的返回值
{
return 0;
}
//創建套接字
SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//創建面向網絡的套接字,而且是有連接的,協議自然爲TCP協議
if (slisten == INVALID_SOCKET)//判斷套接字是否創建成功
{
printf("socket error !");
return 0;
}
//綁定IP和端口
sockaddr_in sin;//處理網絡通信地址的結構體,聲明變量
sin.sin_family = AF_INET;// TCP/IP協議族
sin.sin_port = htons(80);//端口可以自己賦值
sin.sin_addr.S_un.S_addr = INADDR_ANY;//設置的是服務器的IP地址,inet_addr("0.0.0.0"); 本地的IP地址是
//服務器端計算機上的所有網卡的IP地址都可以作爲服務器IP地址
if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)//綁定並進行判斷,套接字指向sockaddr_in結構體的指針,參數長度
{
printf("bind error !");
}
for (int i=0; i<10;i++)
{//開始監聽
if (listen(slisten, 10) == SOCKET_ERROR)//監聽一個主動連接的套接字,一個隊列內核上限(slisten 變爲監聽套接字)
{
printf("listen error !");
return 0;
}
//循環接收數據
SOCKET sClient;
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
char revData[255];
printf("等待連接...\n");
sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);//返回值:連接套接字;參數:監聽套接字,ADDR結構體,ADDR結構體的大小
if (sClient == INVALID_SOCKET)//爲空,繼續
{
printf("accept error !");
//continue;
}
printf("接受到一個連接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
while (true)
{
//接收數據
int ret = recv(sClient, revData, 1024, 0);//指定套接字,接收的數據,buf,一般爲0
//printf("%s", &revData);
if (ret > 0)
{
revData[ret] = 0x00;
printf(revData);//顯示接收的數據
printf("\n");
}
//發送數據
const char * sendData = revData;//對於客戶端發送過來的數據,再原樣反饋回去。
send(sClient, sendData, strlen(sendData), 0);//參數說明:套接字,發送數據,長度,一般爲0
if (sClient == INVALID_SOCKET)//爲空,繼續
{
printf("listen error !");
break;
}
// closesocket(sClient);
}
closesocket(sClient);
}
closesocket(slisten);//關閉
WSACleanup();//清除
return 0;
}
這是一對一連接成功的結果圖,有問題的可以聯繫我。
代碼編譯的控制檯 一個仿真軟件做客戶端,也可以自己寫
2.進程的使用方法
/*如果想使用多線程,可以在原有的基礎上進行新的函數定義,然後用下面的辦法創建,其中會有一些聲明和變量的定義,可以通過編譯器的報錯來修改,基本上沒有什麼大問題了。當然,你要使用互斥鎖可能難度更大一些*/
HANDLE hThread01 = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)send, (LPVOID)sClient, 0, 0);//發送,send就是你所編輯的函數,可以替換,sclient就是你的創建的接收的數據套接字,也可修改
if (hThread1 != NULL)
{
CloseHandle(hThread1);
}
HANDLE hThread02 = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)recv, (LPVOID)sClient, 0, 0);//接收,類比第一個線程
if (hThread2 != NULL)
{
CloseHandle(hThread2);
}
Sleep(1000); //must
3.一對多服務器,可以打開多個客戶端連接
在一對一的基礎上,可以使用線程的辦法,實現一個服務器多個客戶端,但是線程挺難的,所以說,我沒有理解透徹,就換了一個辦法,在Windows的環境下可以,在Linux的環境下,稍微修改一下頭文件和函數名也可以實現。我這裏給出Windows下一對多的服務器代碼。
服務器的代碼網址:https://download.csdn.net/download/jinfu5225/12342650
客戶端是用的sscom仿真軟件,這樣的話,比較方便,有需要的下載:https://download.csdn.net/download/jinfu5225/12342561