本文簡要介紹並實現一個客戶端和服務器之間進行通信的程序,主要目的是爲了瞭解建立客戶端和服務器連接的過程,熟悉相應的API。
主要用到的API有如下幾個,socket()、connect()、bind()、listen()、accept()。
下面附一個簡單的代碼實現:
首先,是客戶端程序:
#include <sys/socket.h>
#include <netinet/in.h>
#include <memory.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
using namespace std;
int RunClient(int ServerPort, const char *strServerIP)
{
//根據指定的協議和套接字類型來獲取客戶端套接字
int ClientSocket = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == ClientSocket) //-1表示獲取失敗,返回並輸出錯誤原因
{
cout << "get clientsocket error" << endl;
return -1;
}
//創建鏈接服務器所需要的信息
sockaddr_in ServerAddress; //定義一個套接口的地址對象,其本身是一個結構體,存貯服務器的端口、協議、地址長度、ip等信息
memset(&ServerAddress, 0, sizeof(sockaddr_in)); //將該地址對象所佔用的內存空間清零
ServerAddress.sin_family = AF_INET; //對套接口地址對象中的字段賦值,協議與socket中一致
ServerAddress.sin_port = htons(ServerPort); //對端口字段進行賦值
int writeip = inet_pton(AF_INET, strServerIP, &ServerAddress.sin_addr); // 將服務器的ip地址存儲到地址字段中
if(writeip != 1) //不等於一則說明存貯失敗,打印錯誤信息,關閉客戶端套接字並返回
{
cout << "inet_pton store ip error" << endl;
close(ClientSocket);
return -1;
}
cout << "prepare to connect server....." << endl;
//創建本地客戶端套接字與網絡服務器的鏈接
int conn = connect(ClientSocket, (sockaddr*)&ServerAddress, sizeof(ServerAddress));
if(conn == -1) //返回-1表示鏈接失敗,關閉客戶端套接字並返回錯誤信息
{
cout << "connect error" << endl;
close(ClientSocket);
return -1;
}
else
{
cout << "connect server success! " << endl;
}
//鏈接成功後,則讀取從服務器返回的信息,並存入緩衝區中
char buf[13];
read(ClientSocket, buf, 11);
cout << "the info from server is:"<< endl;
cout << buf << endl; //打印服務器返回的信息
//完成信息傳輸後,關閉客戶端套接字
close(ClientSocket);
cout << "client closed" << endl;
return 0;
}
int main()
{
RunClient(4000, "127.0.0.1");
return 0;
}
接着是服務器端程序:
#include <netinet/in.h>
#include <arpa/inet.h>
#include <memory.h>
#include <unistd.h>
#include <iostream>
using namespace std;
int RunServer(int Port, int LengthOfQueueOfListen = 100, const char *strBoundIP = NULL)
{
//創建一個用來監聽的套接口
int ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == ListenSocket) //失敗返回-1
{
cout << " creat listensocket error" << endl;
return -1;
}
//設置監聽套接口的地址配置信息
sockaddr_in ServerAddress; //定義一個套接口的地址對象
memset(&ServerAddress, 0, sizeof(sockaddr_in)); //該對象所佔用內存區域清零
ServerAddress.sin_family = AF_INET; //協議字段賦值
ServerAddress.sin_port = htons(Port); //端口
if(NULL == strBoundIP) //如果沒有指定服務器綁定的ip,則監聽本機的所有ip (多網卡時可能有多個ip)
{
ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{ //否則就使用指定的ip,將其寫入到套接口的ip字段
int boundip = inet_pton(AF_INET, strBoundIP, &ServerAddress.sin_addr);
if(boundip != 1) //寫入失敗則返回錯誤,並關閉監聽套接口
{
cout << "inet_pton error" << endl;
close(ListenSocket);
return -1;
}
}
//將監聽套接口與其套接口地址信息綁定
int bindd = bind(ListenSocket, (sockaddr *)&ServerAddress, sizeof(sockaddr_in));
if(bindd == -1) //綁定失敗返回錯誤信息,關閉監聽套接口
{
cout << "bind error" << endl;
close(ListenSocket);
return -1;
}
//listen將監聽套接口從主動轉爲被動狀態,指示內核應該接收指向該套接口的鏈接請求,第二個參數表示最大連接個數
int lis = listen(ListenSocket, LengthOfQueueOfListen);
if(lis == -1) //監聽失敗返回錯誤信息,關閉監聽套接字
{
cout << "listen error" << endl;
close(ListenSocket);
return -1;
}
//從連接的隊列中,取出一個已完成的連接,並保存這個連接對應的客戶端信息(ip、端口、協議等)
sockaddr_in ClientAddress; //創建套接口對象,主要用於存貯客戶端的套接字信息
socklen_t LengthOfClientAddress = sizeof(sockaddr_in);
cout << "prepare to connect client....." << endl;
//進行連接,並存貯連接到的客戶端套接口信息,若連接成功則會返回一個表示已經連接的套接口,用來進行信息傳輸
int ConnectedSocket = accept(ListenSocket, (sockaddr *)&ClientAddress, &LengthOfClientAddress);
if(-1 == ConnectedSocket) //若連接失敗,則返回失敗信息,關閉監聽套接字
{
cout << "accept error" << endl;
close(ListenSocket);
return -1;
}
else
{
cout << "connect client success!" << endl;
}
//根據表示已經連接的套接口,向客戶端發送信息
write(ConnectedSocket, "Hello World", 11);
cout << "the info return to client is:\r\nHello World"<< endl;
//關閉表示已經連接的套接口以及監聽套接口
close(ConnectedSocket);
close(ListenSocket);
cout << "server closed \r\n";
return 0;
}
int main()
{
RunServer(4000);
return 0;
}
編譯運行之後,得到的結果如下所示: