您好,登錄后才能下訂單哦!
TCP/IP協議(Transmission Control Protocol/Internet Protocol)叫做傳輸控制/網際協議,又叫網絡通信協議。實際上,它包含上百個功能的協議,如ICMP(互聯網控制信息協議)、FTP(文件傳輸協議)、UDP(用戶數據包協議)、ARP(地址解析協議)等。TCP負責發現傳輸的問題,一旦有問題就會發出重傳信號,直到所有數據安全正確的傳輸到目的地。
套接字(socket):在網絡中用來描述計算機中不同程序與其他計算機程序的通信方式。socket其實是一種特殊的IO借口,也是一種文件描述符。
套接字分為三類:
流式socket(SOCK_STREAM):流式套接字提供可靠、面向連接的通信流;它使用TCP協議,從而保證了數據傳輸的正確性和順序性。
數據報socket(SOCK_DGRAM):數據報套接字定義了一種無連接的服務,數據通過相互獨立的保溫進行傳輸,是無序的,并且不保證是可靠、無差錯的。它使用的數據報協議是UDP。
原始socket:原始套接字允許對底層協議如IP或ICMP進行直接訪問,它功能強大但使用復雜,主要用于一些協議的開發。
套接字由三個參數構成:IP地址,端口號,傳輸層協議。
這三個參數用以區分不同應用程序進程間的網絡通信與連接。
套接字的數據結構:C語言進行套接字編程時,常會使用到sockaddr數據類型和sockaddr_in數據類型,用于保存套接字信息。
兩種結構體分別表示如下:
struct sockaddr { //地址族,2字節 unsigned short sa_family; //存放地址和端口,14字節 char sa_data[14]; } struct sockaddr_in { //地址族 short int sin_family; //端口號(使用網絡字節序) unsigned short int sin_port; //地址 struct in_addr sin_addr; //8字節數組,全為0,該字節數組的作用只是為了讓兩種數據結構大小相同而保留的空字節 unsigned char sin_zero[8] }
對于sockaddr,大部分的情況下只是用于bind,connect,recvfrom,sendto等函數的參數,指明地址信息,在一般編程中,并不對此結構體直接操作。而是用sockaddr_in來代替。
兩種數據結構中,地址族都占2個字節,常見的地址族有:AF_INET,AF_INET6,AF_LOCAL。
這里要注意字節序的問題,最好使用以下函數來對端口和地址進行處理:
uint16_t htons(uint16_t host16bit) uint32_t htonl(uint32_t host32bit) uint16_t ntohs(uint16_t net16bit) uint32_t ntohs(uint32_t net32bit)
將主機字節序改成網絡字節序。
使用socket進行TCP通信時,經常使用的函數有:
下面是TCP通信的demo:
/*socket tcp服務器端*/ #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define SERVER_PORT 5555 /* 監聽后,一直處于accept阻塞狀態, 直到有客戶端連接, 當客戶端如數quit后,斷開與客戶端的連接 */ int main() { //調用socket函數返回的文件描述符 int serverSocket; //聲明兩個套接字sockaddr_in結構體變量,分別表示客戶端和服務器 struct sockaddr_in server_addr; struct sockaddr_in clientAddr; int addr_len = sizeof(clientAddr); int client; char buffer[200]; int iDataNum; //socket函數,失敗返回-1 //int socket(int domain, int type, int protocol); //第一個參數表示使用的地址類型,一般都是ipv4,AF_INET //第二個參數表示套接字類型:tcp:面向連接的穩定數據傳輸SOCK_STREAM //第三個參數設置為0 if((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return 1; } bzero(&server_addr, sizeof(server_addr)); //初始化服務器端的套接字,并用htons和htonl將端口和地址轉成網絡字節序 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); //ip可是是本服務器的ip,也可以用宏INADDR_ANY代替,代表0.0.0.0,表明所有地址 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //對于bind,accept之類的函數,里面套接字參數都是需要強制轉換成(struct sockaddr *) //bind三個參數:服務器端的套接字的文件描述符, if(bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("connect"); return 1; } //設置服務器上的socket為監聽狀態 if(listen(serverSocket, 5) < 0) { perror("listen"); return 1; } while(1) { printf("Listening on port: %d\n", SERVER_PORT); //調用accept函數后,會進入阻塞狀態 //accept返回一個套接字的文件描述符,這樣服務器端便有兩個套接字的文件描述符, //serverSocket和client。 //serverSocket仍然繼續在監聽狀態,client則負責接收和發送數據 //clientAddr是一個傳出參數,accept返回時,傳出客戶端的地址和端口號 //addr_len是一個傳入-傳出參數,傳入的是調用者提供的緩沖區的clientAddr的長度,以避免緩沖區溢出。 //傳出的是客戶端地址結構體的實際長度。 //出錯返回-1 client = accept(serverSocket, (struct sockaddr*)&clientAddr, (socklen_t*)&addr_len); if(client < 0) { perror("accept"); continue; } printf("\nrecv client data...n"); //inet_ntoa ip地址轉換函數,將網絡字節序IP轉換為點分十進制IP //表達式:char *inet_ntoa (struct in_addr); printf("IP is %s\n", inet_ntoa(clientAddr.sin_addr)); printf("Port is %d\n", htons(clientAddr.sin_port)); while(1) { iDataNum = recv(client, buffer, 1024, 0); if(iDataNum < 0) { perror("recv"); continue; } buffer[iDataNum] = '\0'; if(strcmp(buffer, "quit") == 0) break; printf("%drecv data is %s\n", iDataNum, buffer); send(client, buffer, iDataNum, 0); } } return 0; }
/*socket tcp客戶端*/ #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define SERVER_PORT 5555 /* 連接到服務器后,會不停循環,等待輸入, 輸入quit后,斷開與服務器的連接 */ int main() { //客戶端只需要一個套接字文件描述符,用于和服務器通信 int clientSocket; //描述服務器的socket struct sockaddr_in serverAddr; char sendbuf[200]; char recvbuf[200]; int iDataNum; if((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return 1; } serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(SERVER_PORT); //指定服務器端的ip,本地測試:127.0.0.1 //inet_addr()函數,將點分十進制IP轉換成網絡字節序IP serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); if(connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { perror("connect"); return 1; } printf("connect with destination host...\n"); while(1) { printf("Input your world:>"); scanf("%s", sendbuf); printf("\n"); send(clientSocket, sendbuf, strlen(sendbuf), 0); if(strcmp(sendbuf, "quit") == 0) break; iDataNum = recv(clientSocket, recvbuf, 200, 0); recvbuf[iDataNum] = '\0'; printf("recv data of my world is: %s\n", recvbuf); } close(clientSocket); return 0; }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。