1.什么是socket
socket(套接字),簡單來說是IP地址與埠(port)的組合,可以與遠程主機的應用程式進行通信,通過IP地址可以確定一臺主機,而通過埠則可以確定某一個應用程式,IP+埠則可以完全確定某臺主機的某個應用,socket起源于UNIX,類似一種特殊檔案,可以進行打開,關閉,讀寫操作,總而言之,有了socket就可以與網路上的主機進行通信,
2.TCP/UDP 協議
要進行網路通信,就要進行一定規則約束,TCP/UDP就是這樣的協議,規定了通信的規則,
TCP是可靠的,面向連接的雙向資料傳輸協議,可靠是指資料不會重復,也不會丟失,每當發送方發送一個資料給接收方時,如果接收方接收到了該資料,則會發送確認資訊給發送方表示”我已經收到該資料了,你可以發送下一條資料了“,收到確認資訊后,發送方才會發送下一條資料,這樣就可以確定資訊的無誤,雙向傳輸指雙方都可以作為發送方或接收方,
UDP是不可靠的,無連接的雙向傳輸協議,UDP協議只管發送資料,不會確認你有沒有收到,只負責發,不負責確認,所以是不可靠的,UDP適用于傳輸視頻之類的,視頻就算丟失一兩幀也不會有太大影響,
socket既可以是基于TCP,也可以是基于UDP的,根據需求選擇即可,
3.一個簡單的通信程式
用一個簡單的例子來說明socket的用法,用socket寫的程式一般分為,兩部分,一個是服務器端,一個是客戶端.
下面說明服務器端創建程序
1).首先要有套接字才能進行通信,創建套接字的函式是
1 int socket(int af, int type, int protocol);
af:表示地址族,常用的有AF_INET表示使用IPV4地址,AF_INET6表示使用IPV6地址
type:傳輸型別常用有SOCK_STREAM ,SOCK_DGRAM,流式傳輸,報文傳輸
protocol:要使用的協議常用有 IPPROTO_TCP 和 IPPTOTO_UDP,分別表示TCP,UDP協議
回傳一個套接字描述符,也就是一個整型,
2).用bind()函式確定socket各種屬性
1 int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
sock:要系結的套接字
addr:SOCKADDR地址結構體,里面包含使用的協議,IP地址,埠等,要自己設定
addrlen:SOCKADDR的大小,可以用sizeof()獲取
下面的代碼展示創建一個套接字與系結的程序
1 //使用IPV4地址,TCP協議 2 serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 3 SOCKADDR_IN addr; 4 addr.sin_addr.S_un.S_addr = htonl(ADDR_ANY);//表示任何的ip過來連接都接受 5 addr.sin_family = AF_INET;//使用IPV4地址 6 addr.sin_port = htons(6666);//使用6666號埠 7 bind(serverSocket, &addr, sizeof(SOCKADDR));//將套接字與埠6666,設定接收的ip系結
3).listen函式監聽
設定屬性后,服務器端就可以開始監聽了,監控是否有客戶端請求連接,
函式原型
1 int listen(int sock, int backlog);
sock:套接字
backlog:允許多少個客服端連接
4).accept函式等待連接
accept是一個阻塞函式,如果沒有客戶端清求連接會一直等待在這里
1 int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
sock:套接字,
addr:SOCKADDR 結構體
addrlen:addr的長度,可以用sizeof求到
要注意該函式的回傳值,它會回傳一個新的套接字,這個新的套接字是用來與客戶端通信的套接字,之前那個套接字是監聽的套接字,要分清楚,
5).send/recv發送/接收資訊
與客戶端連接成功后就可以進行通信了,可以通信的函式有write/read,send/recv等,這里介紹send/recv
1 int send(int sockfd, const char *buf, size_t len, int flags); 2 3 int recv(int sockfd, char*buf, size_t len, int flags);
sockfd:套接字
buf:發送資料的緩沖區
len:發送資料的長度
flags:標志,一般為零
6).closesocket函式關閉套接字
closesocket()關閉套接字
下面是一個完整的服務器端的代碼
1 #include<stdio.h> 2 #include<WinSock2.h> 3 #pragma comment (lib,"ws2_32.lib") 4 int main() 5 { 6 SOCKET serverSocket;//監視的套接字 7 SOCKET newSocket;//用來通信的套接字 8 SOCKADDR_IN newAddr;//保存客戶端的socket地址資訊 9 SOCKADDR_IN addr;//地址結構體,包括ip port(埠) 10 11 WSADATA data; 12 WORD version;//socket版本 13 int info; 14 char buf[32];//資料緩沖區 15 /* 16 在使用socket之前要進行版本的設定和初始化 17 看不懂不用管 18 */ 19 version = MAKEWORD(2, 2);//設定版本 20 info = WSAStartup(version, &data); 21 /*應用程式或DLL只能在一次成功的WSAStartup()呼叫之后 22 才能呼叫進一步的Windows Sockets API函式, 23 根據版本初始化 windows socket,回傳0表示成功 24 */ 25 26 if (info != 0) 27 { 28 printf("初始化失敗\n"); 29 return -1; 30 } 31 if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion) != 2) 32 { 33 printf("加載失敗\n"); 34 WSACleanup(); 35 return 0; 36 } 37 //創建套接字,使用TCP協議 38 serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 39 addr.sin_addr.S_un.S_addr = htonl(ADDR_ANY);//表示任何的ip過來連接都接受 40 addr.sin_family = AF_INET;//使用ipv4的地址 41 addr.sin_port = htons(6666);//設定應用占用的埠 42 bind(serverSocket, &addr, sizeof(SOCKADDR));//將套接字與埠6666,接收的ip系結 43 listen(serverSocket, 3);//開始監聽,是否有客服端請求連接 44 printf("開始監聽,等待連接..........\n"); 45 int len = sizeof(SOCKADDR); 46 newSocket=accept(serverSocket, (SOCKADDR*)&newAddr,&len); 47 sprintf(buf, "歡迎:%s 的用戶連接", inet_ntoa(newAddr.sin_addr)); 48 send(newSocket, buf, 32, 0);//發送資訊 49 printf("連接成功,開始發送資訊..........\n"); 50 recv(newSocket, buf, 32, 0);//接收資訊 51 printf("接收到的資訊為:%s\n", buf); 52 closesocket(newSocket);//關閉套接字 53 }
運行結果

客戶端例子
客戶端與服務器端不同,服務器端是等待連接的,而客戶端是主動連接的,所以客戶端沒有listen函式監聽,也沒有accept函式等待連接,
客戶端有一個connect函式用于主動連接服務器端,其余差不多
1 int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);
sock:套接字
serv_addr:SOCKADDR結構體
addrlen:serv_addr長度,可以用sizeof得到
客戶端代碼
1 #include<stdio.h> 2 #include<WinSock2.h> 3 #pragma comment(lib,"Ws2_32.lib") 4 5 int main() 6 { 7 SOCKET clientSocket; 8 SOCKADDR_IN addr; 9 int len; 10 char buf[32]; 11 int info; 12 WSADATA data; 13 WORD version; 14 //設定版本,與初始化 15 version = MAKEWORD(2, 2); 16 info = WSAStartup(version, &data); 17 if (info != 0) 18 { 19 printf("初始化失敗\n"); 20 return -1; 21 } 22 if (LOBYTE(data.wVersion) != 2 || HIBYTE(data.wVersion) != 2) 23 { 24 printf("加載失敗\n"); 25 WSACleanup(); 26 return 0; 27 } 28 29 clientSocket = socket(AF_INET, SOCK_STREAM, 0);//創建套接字 30 //要連接的服務器的ip,因為現在服務器端就是本機,所以寫本機ip 31 //127.0.0.1一個特殊的IP地址,表示是本機的IP地址 32 addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); 33 //埠要與服務器相同,不然找不到 34 addr.sin_port = htons(6666); 35 //用IPV4地址 36 addr.sin_family = AF_INET; 37 //主動連接服務器 38 connect(clientSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR)); 39 //接收服務發送的資料 40 recv(clientSocket, buf, 32, 0);//接收資料 41 printf("服務器發送的資訊是:%s\n", buf); 42 sprintf(buf, "%s","你好,服務器"); 43 //發送資料 44 send(clientSocket, buf, 32, 0); 45 //關閉套接字 46 closesocket(clientSocket); 47 return 0; 48 49 }
先啟動服務器,再啟動客戶端,一次簡單的通信就完成了

把這個簡單的例子做出來,對于socket應該會有初步的認識,最起碼應該學會怎么用,
下次利用socket寫個簡單的聊天程式,進一步加深對socket的認識,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/36267.html
標籤:C
上一篇:不借助多余引數也可交換兩個引數(c++,swap函式)
下一篇:通訊錄管理系統(大一上)
