基本的 TCP Socket 編程(c++、linux環境下)

        本文簡要介紹並實現一個客戶端和服務器之間進行通信的程序,主要目的是爲了瞭解建立客戶端和服務器連接的過程,熟悉相應的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;
}

 編譯運行之後,得到的結果如下所示:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章