【網絡編程01】socket的基礎知識-簡單網絡通信程序


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的認識。

 


免責聲明!

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



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