Linux C之Socket通信(TCP協議)

引言:Linux 中的一切都是文件,進程創建或打開文件,內核都會返回一個整數類型的文件描述符;socket 也是一個文件,所以創建socket,也會返回文件描述符。socket用於IPC通信。

在這裏插入圖片描述

一 . TCP實現基本流程(C/S架構)

server端
  • socket()
  • bind()
  • listen()
  • accept()
  • read() wirte()
  • close()

client端

  • socket()
  • connet()
  • read() wirte()
  • close()
---->>>>上述函數可以用Linux的man手冊進行查詢函數原型和所需頭文件,man手冊分爲九個板塊 。如socket函數,其屬於系統調用函數,命令查詢格式爲:man 2 socket 。

在這裏插入圖片描述

socket函數查詢結果:

在這裏插入圖片描述

二 . TCP建立連接過程

在這裏插入圖片描述

三 . server端分步編程:

1 .函數頭文件:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
2 . 宏定義和變量聲明:
#define PORT 8888      //綁定端口爲8888
int oldfd,newfd,len;   //套接字和數據長度 
struct sockaddr_in server,client;   //server和client的結構體
3 . socket初始化:
void socket_init()
{
   server.sin_family = AF_INET;
   server.sin_port = htons(PORT);  //主機字節序轉網絡字節序(轉化數據爲短整型)
   server.sin_addr.s_addr = htonl(0);   ////主機字節序轉網絡字節序(轉化數據爲長整型)
    /********   AF_INET表示IPv4 地址,SOCK_STREAM表示流式套接字,protocol 的值設爲 0 *****/
   oldfd = socket(AF_INET,SOCK_STREAM,0);  //創建套接字(用於監聽客戶端連接)
   len = sizeof(client);
   /************* 調用函數的返回值小於0,一般是調用函數失敗 *************/
   if(oldfd < 0)  
   { 
      perror("socket:");  //打印錯誤函數
      exit(1);   //結束進程
   }
   if(bind(oldfd,(struct sockaddr *)&server,sizeof(server)) < 0)
   {
      perror("bind:");
      exit(1);
   }
   if(listen(oldfd,128) < 0)   //監聽端口(接受客戶端的請求)
   {
     perror("listen:");
     exit(1);
   }
   /************  未有客戶端請求,該函數的狀態爲阻塞 , 
 -              完成TCP三次握手連接,該函數創建新的套接字,
 -              用於與客戶端通信。
   ***************/
   newfd = accept(oldfd,(struct sockaddr *)&client,&len);   
   if(newfd < 0)
   {
      perror("accept:");
      exit(1);
   }
}
4 . server main函數:
int main()
{
   char recbuf[1024],sendbuf[1024];  //發送和接收數據的數組
   printf("this is server\n");
   socket_init();   //socket函數初始化
   while(1)
   {
     if(read(newfd,recbuf,sizeof(recbuf))<0)   //讀取客戶端發來的數據
     {
         perror("read:");
         exit(1); 
     }
     printf("%s\n",recbuf);
     strcpy(sendbuf,recbuf);    //接收什麼數據就返回什麼數據
     if(write(newfd,sendbuf,sizeof(sendbuf))<0)   //向客戶端發送數據
     {
         perror("write:");
         exit(1);
     }
   }
   //關閉套接字
   close(oldfd);
   close(newfd);
   return 0;
}

四 . client端分步編程:

1 . 函數頭文件:
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
2 . 宏定義和變量聲明:
#define PORT 8888    //目標服務器端口
int socket_fd;       //與服務器通信的套接字
struct sockaddr_in server;    //server結構體
3 . socket初始化:
void socket_init()
{
  server.sin_family = AF_INET;  //AF_INET表示IPv4 地址
  server.sin_port = htons(PORT);  //主機字節序轉網絡字節序
  /**************  兩個進程由於在本機進行通信,即服務器目標地址爲迴環地址  *******/
  server.sin_addr.s_addr = inet_addr("127.0.0.1");  
  /********   AF_INET表示IPv4 地址,SOCK_STREAM表示流式套接字,protocol 的值設爲 0 *****/
  socket_fd = socket(AF_INET,SOCK_STREAM,0);  
  if(socket_fd < 0)
  {
     perror("socket:");
     exit(1);
  }
  /********   connet函數用來連接服務端(客戶端的端口隨機分配,也能用bind函數來指定端口)****/
  if(connect(socket_fd,(struct sockaddr *)&server,sizeof(server))<0)  //返回值爲0,連接失敗
  {
     perror("connect:");
     exit(1);
  }
}
4 . client端 main函數:
int main()
{
  char sendbuf[1024],recbuf[1024];
  printf("this is client\n");
  socket_init();
  while(1)
  {
    scanf("%s",sendbuf);
    if(write(socket_fd,sendbuf,sizeof(sendbuf))<0)
    {
       perror("write:");
       exit(1);
    }
    if(read(socket_fd,recbuf,sizeof(recbuf))<0)
    {
         perror("read:");
         exit(1);
    }
    printf("%s\n",recbuf);
  }
  close(socket_fd);  //關閉套接字
  return 0;
}
  • 最後使用gcc對源文件進行編譯。
程序最終運行圖:

在這裏插入圖片描述

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