歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

Linux下TCP通信簡單實例

基於TCP(面向連接)的socket編程,分為服務器端和客戶端

  服務器端的流程如下:

  (1)創建套接字(socket)

  (2)將套接字綁定到一個本地地址和端口上(bind)

  (3)將套接字設為監聽模式,准備接收客戶端請求(listen)

  (4)等待客戶請求到來;當請求到來後,接受連接請求,返回一個新的對應於此次連接的套接字(accept)

  (5)用返回的套接字和客戶端進行通信(send/recv)

  (6)返回,等待另一個客戶請求。

  (7)關閉套接字。

  客戶端的流程如下:

  (1)創建套接字(socket)

  (2)向服務器發出連接請求(connect)

  (3)和服務器端進行通信(send/recv)

  (4)關閉套接字

  下面通過一個具體例子講解一下具體的過程和相關的函數,環境是suse linux.

  #include <stdio.h>

  #include <stdlib.h>

  #include <strings.h>

  #include <sys/types.h>

  #include <sys/socket.h>

  #include <memory.h>

  #include <unistd.h>

  //#include <linux/in.h>

  #include <netinet/in.h>

  //#include <linux/inet_diag.h>

  #include <arpa/inet.h>

  #include <signal.h>

  /**

  關於 sockaddr  sockaddr_in  socketaddr_un說明

  http://maomaozaoyue.blog.sohu.com/197538359.html

  */

  #define PORT    11910   //定義通信端口

  #define BACKLOG 5       //定義偵聽隊列長度

  #define buflen  1024

  void process_conn_server(int s);

  void sig_pipe(int signo);

  int ss,sc;  //ss為服務器socket描述符,sc為某一客戶端通信socket描述符

  int main(int argc,char *argv[])

  {

  struct sockaddr_in server_addr; //存儲服務器端socket地址結構

  struct sockaddr_in client_addr; //存儲客戶端 socket地址結構

  int err;    //返回值

  pid_t pid;  //分叉進行的ID

  /*****************socket()***************/

  ss = socket(AF_INET,SOCK_STREAM,0); //建立一個序列化的,可靠的,雙向連接的的字節流

  if(ss<0)

  {

  printf("server : server socket create error\n");

  return -1;

  }

  //注冊信號

  sighandler_t ret;

  ret = signal(SIGTSTP,sig_pipe);

  if(SIG_ERR == ret)

  {

  printf("信號掛接失敗\n");

  return -1;

  }

  else

  printf("信號掛接成功\n");

  /******************bind()****************/

  //初始化地址結構

  memset(&server_addr,0,sizeof(server_addr));

  server_addr.sin_family = AF_INET;           //協議族

  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);   //本地地址

  server_addr.sin_port = htons(PORT);

  err = bind(ss,(struct sockaddr *)&server_addr,sizeof(sockaddr));

  if(err<0)

  {

  printf("server : bind error\n");

  return -1;

  }


 /*****************listen()***************/

  err = listen(ss,BACKLOG);   //設置監聽的隊列大小

  if(err < 0)

  {

  printf("server : listen error\n");

  return -1;

  }

  /****************accept()***************/

  /**

  為類方便處理,我們使用兩個進程分別管理兩個處理:

  1,服務器監聽新的連接請求;2,以建立連接的C/S實現通信

  這兩個任務分別放在兩個進程中處理,為了防止失誤操作

  在一個進程中關閉 偵聽套接字描述符 另一進程中關閉

  客戶端連接套接字描述符。注只有當所有套接字全都關閉時

  當前連接才能關閉,fork調用的時候父進程與子進程有相同的

  套接字,總共兩套,兩套都關閉掉才能關閉這個套接字

  */

  for(;;)

  {

  socklen_t addrlen = sizeof(client_addr);

  //accept返回客戶端套接字描述符

  sc = accept(ss,(struct sockaddr *)&client_addr,&addrlen);  //注,此處為了獲取返回值使用 指針做參數

  if(sc < 0)  //出錯

  {

  continue;   //結束此次循環

  }

  else

  {

  printf("server : connected\n");

  }

  //創建一個子線程,用於與客戶端通信

  pid = fork();

  //fork 調用說明:子進程返回 0 ;父進程返回子進程 ID

  if(pid == 0)        //子進程,與客戶端通信

  {

  close(ss);

  process_conn_server(sc);

  }

  else

  {

  close(sc);

  }

  }

  }

  /**

  服務器對客戶端連接處理過程;先讀取從客戶端發送來的數據,

  然後將接收到的數據的字節的個數發送到客戶端

  */

  //通過套接字 s 與客戶端進行通信

  void process_conn_server(int s)

  {

  ssize_t size = 0;

  char buffer[buflen];  //定義數據緩沖區

  for(;;)

  {

  //等待讀

  for(size = 0;size == 0 ;size = read(s,buffer,buflen));

  //輸出從客戶端接收到的數據

  printf("%s",buffer);

  //結束處理

  if(strcmp(buffer,"quit") == 0)

  {

  close(s);   //成功返回0,失敗返回-1

  return ;

  }

  sprintf(buffer,"%d bytes altogether\n",size);

  write(s,buffer,strlen(buffer)+1);

  }

  }

  void sig_pipe(int signo)

  {

  printf("catch a signal\n");

  if(signo == SIGTSTP)

  {

  printf("接收到 SIGTSTP 信號\n");

  int ret1 = close(ss);

  int ret2 = close(sc);

  int ret = ret1>ret2?ret1:ret2;

  if(ret == 0)

  printf("成功 : 關閉套接字\n");

  else if(ret ==-1 )

  printf("失敗 : 未關閉套接字\n");

  exit(1);

  }

  }

  客戶端代碼:

  #include <stdio.h>

  #include <strings.h>

  #include <unistd.h>

  #include <sys/types.h>

  #include <sys/socket.h>

  //#include <linux/in.h>

  #include <stdlib.h>

  #include <memory.h>

  #include <arpa/inet.h>

  #include <netinet/in.h>

  #include <signal.h> //添加信號處理  防止向已斷開的連接通信

  /**

  信號處理順序說明:在Linux操作系統中某些狀況發生時,系統會向相關進程發送信號,

  信號處理方式是:1,系統首先調用用戶在進程中注冊的函數,2,然後調用系統的默認

  響應方式,此處我們可以注冊自己的信號處理函數,在連接斷開時執行

  */

  #define PORT    11910

  #define Buflen  1024

  void process_conn_client(int s);

  void sig_pipe(int signo);    //用戶注冊的信號函數,接收的是信號值

  int s;  //全局變量 , 存儲套接字描述符

  int main(int argc,char *argv[])

  {

  sockaddr_in server_addr;

Copyright © Linux教程網 All Rights Reserved