qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請訪問 http://qaseven.github.io/

          Linux下TCP通信簡單實例

           基于TCP(面向連接)的socket編程,分為服務(wù)器端和客戶端
            服務(wù)器端的流程如下:
            (1)創(chuàng)建套接字(socket)
            (2)將套接字綁定到一個本地地址和端口上(bind)
            (3)將套接字設(shè)為監(jiān)聽模式,準(zhǔn)備接收客戶端請求(listen)
            (4)等待客戶請求到來;當(dāng)請求到來后,接受連接請求,返回一個新的對應(yīng)于此次連接的套接字(accept)
            (5)用返回的套接字和客戶端進(jìn)行通信(send/recv)
            (6)返回,等待另一個客戶請求。
            (7)關(guān)閉套接字。
            客戶端的流程如下:
            (1)創(chuàng)建套接字(socket)
            (2)向服務(wù)器發(fā)出連接請求(connect)
            (3)和服務(wù)器端進(jìn)行通信(send/recv)
            (4)關(guān)閉套接字
            下面通過一個具體例子講解一下具體的過程和相關(guān)的函數(shù),環(huán)境是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>
          /**
          關(guān)于 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為服務(wù)器socket描述符,sc為某一客戶端通信socket描述符
          int main(int argc,char *argv[])
          {
          struct sockaddr_in server_addr; //存儲服務(wù)器端socket地址結(jié)構(gòu)
          struct sockaddr_in client_addr; //存儲客戶端 socket地址結(jié)構(gòu)
          int err;    //返回值
          pid_t pid;  //分叉進(jìn)行的ID
          /*****************socket()***************/
          ss = socket(AF_INET,SOCK_STREAM,0); //建立一個序列化的,可靠的,雙向連接的的字節(jié)流
          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()****************/
          //初始化地址結(jié)構(gòu)
          memset(&server_addr,0,sizeof(server_addr));
          server_addr.sin_family = AF_INET;           //協(xié)議族
          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);   //設(shè)置監(jiān)聽的隊列大小
          if(err < 0)
          {
          printf("server : listen error\n");
          return -1;
          }
          /****************accept()***************/
          /**
            為類方便處理,我們使用兩個進(jìn)程分別管理兩個處理:
            1,服務(wù)器監(jiān)聽新的連接請求;2,以建立連接的C/S實現(xiàn)通信
            這兩個任務(wù)分別放在兩個進(jìn)程中處理,為了防止失誤操作
            在一個進(jìn)程中關(guān)閉 偵聽套接字描述符 另一進(jìn)程中關(guān)閉
            客戶端連接套接字描述符。注只有當(dāng)所有套接字全都關(guān)閉時
            當(dāng)前連接才能關(guān)閉,fork調(diào)用的時候父進(jìn)程與子進(jìn)程有相同的
            套接字,總共兩套,兩套都關(guān)閉掉才能關(guān)閉這個套接字
          */
          for(;;)
          {
          socklen_t addrlen = sizeof(client_addr);
          //accept返回客戶端套接字描述符
          sc = accept(ss,(struct sockaddr *)&client_addr,&addrlen);  //注,此處為了獲取返回值使用 指針做參數(shù)
          if(sc < 0)  //出錯
          {
          continue;   //結(jié)束此次循環(huán)
          }
          else
          {
          printf("server : connected\n");
          }
          //創(chuàng)建一個子線程,用于與客戶端通信
          pid = fork();
          //fork 調(diào)用說明:子進(jìn)程返回 0 ;父進(jìn)程返回子進(jìn)程 ID
          if(pid == 0)        //子進(jìn)程,與客戶端通信
          {
          close(ss);
          process_conn_server(sc);
          }
          else
          {
          close(sc);
          }
          }
          }
          /**
          服務(wù)器對客戶端連接處理過程;先讀取從客戶端發(fā)送來的數(shù)據(jù),
          然后將接收到的數(shù)據(jù)的字節(jié)的個數(shù)發(fā)送到客戶端
          */
          //通過套接字 s 與客戶端進(jìn)行通信
          void process_conn_server(int s)
          {
          ssize_t size = 0;
          char buffer[buflen];  //定義數(shù)據(jù)緩沖區(qū)
          for(;;)
          {
          //等待讀
          for(size = 0;size == 0 ;size = read(s,buffer,buflen));
          //輸出從客戶端接收到的數(shù)據(jù)
          printf("%s",buffer);
          //結(jié)束處理
          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("成功 : 關(guān)閉套接字\n");
          else if(ret ==-1 )
          printf("失敗 : 未關(guān)閉套接字\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操作系統(tǒng)中某些狀況發(fā)生時,系統(tǒng)會向相關(guān)進(jìn)程發(fā)送信號,
          信號處理方式是:1,系統(tǒng)首先調(diào)用用戶在進(jìn)程中注冊的函數(shù),2,然后調(diào)用系統(tǒng)的默認(rèn)
          響應(yīng)方式,此處我們可以注冊自己的信號處理函數(shù),在連接斷開時執(zhí)行
          */
          #define PORT    11910
          #define Buflen  1024
          void process_conn_client(int s);
          void sig_pipe(int signo);    //用戶注冊的信號函數(shù),接收的是信號值
          int s;  //全局變量 , 存儲套接字描述符
          int main(int argc,char *argv[])
          {
          sockaddr_in server_addr;
          int err;
          sighandler_t ret;
          char server_ip[50] = "";
          int port = 0;
          strcpy(server_ip, argv[1]);
          port = atoi(argv[2]);
          /********************socket()*********************/
          s= socket(AF_INET,SOCK_STREAM,0);
          if(s<0)
          {
          printf("client : create socket error\n");
          return 1;
          }
          printf("client : socket fd = %d\n", s);
          //信號處理函數(shù)  SIGINT 是當(dāng)用戶按一個 Ctrl-C 建時發(fā)送的信號
          ret = signal(SIGTSTP,sig_pipe);
          if(SIG_ERR == ret)
          {
          printf("信號掛接失敗\n");
          return -1;
          }
          else
          printf("信號掛接成功\n") ;
          /*******************connect()*********************/
          //設(shè)置服務(wù)器地址結(jié)構(gòu),準(zhǔn)備連接到服務(wù)器
          memset(&server_addr,0,sizeof(server_addr));
          server_addr.sin_family = AF_INET;
          server_addr.sin_port = htons(PORT);
          server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
          /*將用戶數(shù)入對額字符串類型的IP格式轉(zhuǎn)化為整型數(shù)據(jù)*/
          //inet_pton(AF_INET,argv[1],&server_addr.sin_addr.s_addr);
          printf("please input server ip address : \n");
          read(0,server_ip,50);
          //err = inet_pton(AF_INET,server_ip,&server_addr.sin_addr.s_addr);
          server_addr.sin_addr.s_addr = inet_addr(server_ip);
          err = connect(s,(struct sockaddr *)&server_addr,sizeof(sockaddr));
          if(err == 0)
          {
          printf("client : connect to server\n");
          }
          else
          {
          printf("client : connect error\n");
          return -1;
          }
          //與服務(wù)器端進(jìn)行通信
          process_conn_client(s);
          close(s);
          }
          void process_conn_client(int s)
          {
          ssize_t size = 0;
          char buffer[Buflen];
          for(;;)
          {
          memset(buffer,'\0',Buflen);
          /*從標(biāo)準(zhǔn)輸入中讀取數(shù)據(jù)放到緩沖區(qū)buffer中*/
          size = read(0,buffer,Buflen);   // 0,被默認(rèn)的分配到標(biāo)準(zhǔn)輸入  1,標(biāo)準(zhǔn)輸出  2,error
          if(size >  0)
          {
          //當(dāng)向服務(wù)器發(fā)送 “quit” 命令時,服務(wù)器首先斷開連接
          write(s,buffer,strlen(buffer)+1);   //向服務(wù)器端寫
          //等待讀取到數(shù)據(jù)
          for(size = 0 ; size == 0 ; size = read(s,buffer,Buflen) );
          write(1,buffer,strlen(buffer)+1);   //向標(biāo)準(zhǔn)輸出寫
          }
          }
          }
          void sig_pipe(int signo)    //傳入套接字描述符
          {
          printf("Catch a signal\n");
          if(signo == SIGTSTP)
          {
          printf("接收到 SIGTSTP 信號\n");
          int ret = close(s);
          if(ret == 0)
          printf("成功 : 關(guān)閉套接字\n");
          else if(ret ==-1 )
          printf("失敗 : 未關(guān)閉套接字\n");
          exit(1);
          }
          }

          posted on 2014-02-20 10:57 順其自然EVO 閱讀(458) 評論(0)  編輯  收藏 所屬分類: linux

          <2014年2月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          2324252627281
          2345678

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 永丰县| 兴业县| 法库县| 中山市| 雅安市| 廊坊市| 汉源县| 林西县| 永兴县| 汤阴县| 金秀| 鞍山市| 绥阳县| 旬阳县| 南岸区| 商城县| 台安县| 时尚| 武山县| 临沧市| 都江堰市| 犍为县| 肥西县| 乌兰察布市| 托克逊县| 和政县| 北碚区| 二连浩特市| 三原县| 泰兴市| 兰州市| 盘锦市| 太白县| 临漳县| 沂源县| 开阳县| 横山县| 贡嘎县| 丹凤县| 永胜县| 四川省|