[C++] Linux TCP Socket 實例- 阻塞


Linux平台 TCP Socket通信實例,發現用代碼注釋記筆記也不錯

TCP server 阻塞

  1 // 兩個進程通過socket進行通信,client需要知道server的,server卻不需要實現知道client的。
  2 // socket需要知道type和address domain;
  3 // address domain:unix domain用於同主機進程間,通過文件系統實現;Internet domain用於兩台機器間,需要IP和Port信息。一般2000以下的port保留。
  4 // socket type:stream(意味着持續讀取,TCP是一種協議)、datagram(一次需要讀完整個信息,UDP是一種協議)。
  5 
  6 // port復用機制的理解
  7 
  8 /**
  9 stream server
 10 1.socket
 11 2.bind
 12 3.listen
 13 4.accept
 14 */
 15 #include <cstdlib>              /* exit() */
 16 #include <cstdio>               /* perror(): 打印信息+發生錯誤的原因,可用於定位。 */
 17 #include <cstring>              /* memset memcpy*/
 18 #include <iostream>             /* cin cout */
 19 #include <sys/types.h>          /* 為了滿足一些 BSD系統添加頭文件*/
 20 #include <sys/socket.h>         /* socket(); listen(); baccept(); socklen_t */
 21 #include <netinet/in.h>         /* struct sockaddr_in: 保存socket信息; ntohl(), ntohs(), htonl() and htons()*/
 22 #include <unistd.h>             /* read() write(), 不是C語言范疇,所以沒有cxxxx的實現 */
 23     // http://man7.org/linux/man-pages/man2/read.2.html
 24     // http://man7.org/linux/man-pages/man2/write.2.html
 25 
 26 /**
 27 \brief  錯誤處理函數
 28 */
 29 void tzError(const char *msg)
 30 {
 31     perror(msg);
 32     exit(1);    // 一般不同原因不同的exit code更為規范
 33 }
 34 
 35 /**
 36 \brief  主函數
 37 */
 38 int main(int argc, char *argv[]){
 39     // 參數check
 40     if (argc < 2) {
 41         fprintf(stderr,"ERROR, no [port] arg provided\n");
 42         exit(1);
 43     }
 44     // 生成socke + check
 45     int sockfd;
 46     sockfd = socket(AF_INET, SOCK_STREAM, 0);
 47         // int socket(int domain, int type, int protocol);
 48         // http://man7.org/linux/man-pages/man2/socket.2.html
 49         // domain:
 50         //      AF_UNIX     用於本地通信
 51         //      AF_INET     IPv4
 52         //      AF_INET6    IPv6
 53         // type:
 54         //      SOCK_STREAM     面向連接通信
 55         //      SOCK_DGRAM      面向無連接通信
 56         // protocol:
 57         //      SOCK_NONBLOCK   非阻塞
 58         //      SOCK_CLOEXEC    子類不繼承父類的socket fd
 59         // Return:
 60         //      建立的socket:對於server用於監聽,對於client用於向指定位置發送
 61     if (sockfd < 0){
 62         tzError("socket()");
 63     }
 64     // bind socket + check
 65     int port_no = atoi(argv[1]);
 66     struct sockaddr_in serv_addr;
 67     serv_addr.sin_family = AF_INET;             // IPv4
 68     serv_addr.sin_addr.s_addr = INADDR_ANY;     // 相當於inet_addr("0.0.0.0"),所有網卡。
 69     serv_addr.sin_port = htons(port_no);         // 需要網絡序轉換
 70     int bind_ret = bind(sockfd, 
 71                     (struct sockaddr*)&serv_addr, 
 72                     sizeof(serv_addr));
 73         // int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
 74         // http://man7.org/linux/man-pages/man2/bind.2.html
 75         // socket()產生sockdf,有了name space(address family)但是沒有具體地址,bind完成此任務
 76     if( bind_ret<0 ){
 77         tzError("bind()");
 78     }
 79     // listen
 80     listen(sockfd,5);
 81         // int listen(int sockfd, int backlog);
 82         // http://man7.org/linux/man-pages/man2/listen.2.html
 83         // listen讓sockfd對應的socket成為被動socket,用於accetp
 84         // backlog: 最大可以入隊等待處理的連接數量。如果大於128(somaxconn)可能被內核截斷,cat /proc/sys/net/core/somaxconn
 85         // 如果嘗試connect的鏈接超過backlog,client收到ECONNREFUSED錯誤,如果有重傳機制可能忽略此錯誤。
 86     // accept + check
 87     struct sockaddr_in client_addr;
 88     socklen_t clilen_len = sizeof(client_addr);
 89     int new_sockfd = accept(sockfd, 
 90                  (struct sockaddr *) &client_addr, 
 91                  &clilen_len);
 92         // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
 93         // http://man7.org/linux/man-pages/man2/accept.2.html
 94         // 用於面向連接socket(SOCK_STREAM, SOCK_SEQPACKET),提取隊列中第一個請求,創建新的connected socket
 95         // 返回新創建的socket的fd,原始的被動sockfd不受影響。
 96         // addr: socketaddr指針,記錄peer的信息,信息格式取決於socket的定義。
 97             // 如果等待隊列為空,且socket沒有設置nonblocking-->阻塞等待;
 98             // 如果等待隊列為空,且socket設置nonblocking,返回錯誤:EAGAIN or EWOULDBLOCK
 99         
100             // 為了處理對socket的新的connect,可使用:select(2), poll(2), or epoll(7)-->有嘗試連接的請求會出觸發"readable event"
101             // 也可以通過socket()設置發送SIGIO中斷。
102         // addrlen:傳入傳輸參數,實際的大小會重新保存到addrlen中
103     if (new_sockfd < 0) 
104           tzError("accept()");
105     // 接收-發送信息
106 #define RECV_BUF_SIZE   256
107     char buffer[RECV_BUF_SIZE];
108     std::memset(buffer, 0, RECV_BUF_SIZE);  // 用0進行set
109     int valread = read( new_sockfd , buffer, RECV_BUF_SIZE);
110     std::cout << "read:" << valread << "char, content: " << buffer << std::endl;
111     write(new_sockfd , "get msg", 7);
112     // close sockets
113     close(new_sockfd); 
114     close(sockfd);
115 
116     return 0;
117 }
118 
119 // ref:
120 //      https://www.cnblogs.com/fuchongjundream/p/3914696.html
121 //      http://www.linuxhowtos.org/C_C++/socket.htm
122 //      

TCP Client - 單次連接發送接收測試

  1 // 兩個進程通過socket進行通信,client需要知道server的,server卻不需要實現知道client的。
  2 // socket需要知道type和address domain;
  3 // address domain:unix domain用於同主機進程間,通過文件系統實現;Internet domain用於兩台機器間,需要IP和Port信息。一般2000以下的port保留。
  4 // socket type:stream(意味着持續讀取,TCP是一種協議)、datagram(一次需要讀完整個信息,UDP是一種協議)。
  5 
  6 // port復用機制的理解
  7 
  8 /**
  9 stream client
 10 1.socket
 11 2.connect
 12 */
 13 #include <cstdlib>              /* exit() */
 14 #include <cstdio>               /* perror(): 打印信息+發生錯誤的原因,可用於定位。 */
 15 #include <cstring>              /* memset memcpy strcpy string.c_str()*/
 16 #include <iostream>             /* cin cout */
 17 #include <sys/types.h>          /* 為了滿足一些 BSD系統添加頭文件*/
 18 #include <sys/socket.h>         /* socket(); listen(); baccept(); socklen_t */
 19 // #include <netinet/in.h>         /* struct sockaddr_in: 保存socket信息; ntohl(), ntohs(), htonl() and htons()*/
 20 #include <netdb.h>              /* hostent,IP地址 */
 21 
 22 #include <unistd.h>             /* read() write(), 不是C語言范疇,所以沒有cxxxx的實現 */
 23     // http://man7.org/linux/man-pages/man2/read.2.html
 24     // http://man7.org/linux/man-pages/man2/write.2.html
 25 
 26 /**
 27 \brief  錯誤處理函數
 28 */
 29 void tzError(const char *msg)
 30 {
 31     perror(msg);
 32     exit(1);    // 一般不同原因不同的exit code更為規范
 33 }
 34 
 35 /**
 36 \brief  主函數
 37 */
 38 int main(int argc, char *argv[]){
 39     // 參數check
 40     if (argc < 3) {
 41         tzError("ERROR, no [IP] [port] arg provided");
 42     }
 43     // 生成socke + check
 44     int sockfd;
 45     sockfd = socket(AF_INET, SOCK_STREAM, 0);
 46         // int socket(int domain, int type, int protocol);
 47         // http://man7.org/linux/man-pages/man2/socket.2.html
 48         // domain:
 49         //      AF_UNIX     用於本地通信
 50         //      AF_INET     IPv4
 51         //      AF_INET6    IPv6
 52         // type:
 53         //      SOCK_STREAM     面向連接通信
 54         //      SOCK_DGRAM      面向無連接通信
 55         // protocol:
 56         //      SOCK_NONBLOCK   非阻塞
 57         //      SOCK_CLOEXEC    子類不繼承父類的socket fd
 58         // Return:
 59         //      建立的socket:對於server用於監聽,對於client用於向指定位置發送
 60     if (sockfd < 0){
 61         tzError("socket()");
 62     }
 63     // connect + check
 64     struct hostent *server;
 65         // struct hostent {
 66         //     char  *h_name;            /* official name of host */
 67         //     char **h_aliases;         /* alias list */
 68         //     int    h_addrtype;        /* host address type */
 69         //     int    h_length;          /* length of address */
 70         //     char **h_addr_list;       /* list of addresses */
 71         // }
 72         // #define h_addr h_addr_list[0] /* for backward compatibility,第一個地址 */
 73     server = gethostbyname(argv[1]);
 74     if (server == NULL) {
 75         tzError("[IP] error");
 76     }
 77     int port_no = atoi(argv[2]);
 78     struct sockaddr_in serv_addr;
 79     memset(&serv_addr, 0, sizeof(serv_addr));
 80     serv_addr.sin_family = AF_INET;     // set type
 81     memcpy((char *)server->h_addr,      // set addr
 82         (char *)&serv_addr.sin_addr.s_addr,
 83         server->h_length);
 84     serv_addr.sin_port = htons(port_no); // set port
 85     int conntect_ret = connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr));
 86     if(conntect_ret<0){
 87         tzError("connect()");
 88     }
 89 
 90     // write and read
 91     std::cout << "msg>";
 92     std::string msg;
 93     std::cin >> msg;
 94 #define BUF_SIEE    128
 95     char buf[BUF_SIEE];
 96     memset(buf, 0, BUF_SIEE);
 97     strcpy(buf, msg.c_str());
 98     int write_n = write(sockfd, buf, msg.size());
 99     std::cout << write_n << " chars send." << std::endl;
100     memset(buf, 0, BUF_SIEE);   // 用於輸出,依賴\0
101     int read_n = read(sockfd, buf, BUF_SIEE);
102     std::cout << buf << std::endl;
103     std::cout << read_n << " chars read." << std::endl;
104     
105     // close sockets
106     close(sockfd);
107 
108     return 0;
109 }
110 
111 // ref:
112 //      http://www.linuxhowtos.org/C_C++/socket.htm
113  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM