用C語言進行最基本的socket編程


什么是socket
  你經常聽到人們談論着 “socket”,或許你還不知道它的確切含義。現在讓我告訴你:它是使用 標准Unix 文件描述符 (file descriptor) 和其它程序通訊的方式。什么?你也許聽到一些Unix高手(hacker)這樣說過:“呀,Unix中的一切就是文件!”那個家伙也許正在說到一個事實:Unix 程序在執行任何形式的 I/O 的時候,程序是在讀或者寫一個文件描述符。一個文件描述符只是一個和打開的文件相關聯的整數。但是(注意后面的話),這個文件可能是一個網絡連接,FIFO,管道,終端,磁盤上的文件或者什么其它的東西。Unix 中所有的東西就是文件!所以,你想和Internet上別的程序通訊的時候,你將要使用到文件描述符。你必須理解剛才的話。現在你腦海中或許冒出這樣的念頭:“那么我從哪里得到網絡通訊的文件描述符呢?”,這個問題無論如何我都要回答:你利用系統調用 socket(),它返回套接字描述符 (socket descriptor),然后你再通過它來進行send() 和 recv()調用。“但是...”,你可能有很大的疑惑,“如果它是個文件描述符,那么為什 么不用一般調用read()和write()來進行套接字通訊?”簡單的答案是:“你可以使用!”。詳細的答案是:“你可以,但是使用send()和recv()讓你更好的控制數據傳輸。”存在這樣一個情況:在我們的世界上,有很多種套接字。有DARPA Internet 地址 (Internet 套接字),本地節點的路徑名 (Unix套接字),CCITT X.25地址 (你可以將X.25 套接字完全忽略)。也許在你的Unix 機器上還有其它的。我們在這里只講第一種:Internet 套接字。
Internet 套接字的兩種類型
  什么意思?有兩種類型的Internet 套接字?是的。不,我在撒謊。其實還有很多,但是我可不想嚇着你。我們這里只講兩種。除了這些, 我打算另外介紹的 "Raw Sockets" 也是非常強大的,很值得查閱。
那么這兩種類型是什么呢?一種是"Stream Sockets"(流格式),另外一種是"Datagram Sockets"(數據包格式)。我們以后談到它們的時候也會用到"SOCK_STREAM" 和 "SOCK_DGRAM"。數據報套接字有時也叫“無連接套接字”(如果你確實要連接的時候可以用connect()。) 流式套接字是可靠的雙向通訊的數據流。如果你向套接字按順序輸出“1,2”,那么它們將按順序“1,2”到達另一邊。它們是無錯誤的傳遞的,有自己的錯誤控制,在此不討論。
    有什么在使用流式套接字?你可能聽說過 telnet,不是嗎?它就使用流式套接字。你需要你所輸入的字符按順序到達,不是嗎?同樣,WWW瀏覽器使用的 HTTP 協議也使用它們來下載頁面。實際上,當你通過端口80 telnet 到一個 WWW 站點,然后輸入 “GET pagename” 的時候,你也可以得到 HTML 的內容。為什么流式套接字可以達到高質量的數據傳輸?這是因為它使用了“傳輸控制協議 (The Transmission Control Protocol)”,也叫 “TCP” (請參考 RFC-793 獲得詳細資料。)TCP 控制你的數據按順序到達並且沒有錯
誤。你也許聽到 “TCP” 是因為聽到過 “TCP/IP”。這里的 IP 是指“Internet 協議”(請參考 RFC-791。) IP 只是處理Internet 路由而已。
    那么數據報套接字呢?為什么它叫無連接呢?為什么它是不可靠的呢?有這樣的一些事實:如果你發送一個數據報,它可能會到達,它可能次序顛倒了。如果它到達,那么在這個包的內部是無錯誤的。數據報也使用 IP 作路由,但是它不使用 TCP。它使用“用戶數據報協議 (User Datagram Protocol)”,也叫 “UDP” (請參考 RFC-768。)
    為什么它們是無連接的呢?主要是因為它並不象流式套接字那樣維持一個連接。你只要建立一個包,構造一個有目標信息的IP 頭,然后發出去。無需連接。它們通常使用於傳輸包-包信息。簡單的應用程序有:tftp, bootp等等。
    你也許會想:“假如數據丟失了這些程序如何正常工作?”我的朋友,每個程序在 UDP 上有自己的協議。例如,tftp 協議每發出的一個被接受到包,收到者必須發回一個包來說“我收到了!” (一個“命令正確應答”也叫“ACK” 包)。如果在一定時間內(例如5秒),發送方沒有收到應答,它將重新發送,直到得到 ACK。這一ACK過程在實現SOCK_DGRAM 應用程序的時候非常重要。

簡單的發送和接收實現

服務器端接收代碼:

 

 1 #include <Winsock2.h>
 2 #pragma comment(lib,"Ws2_32.lib")
 3 #include <stdio.h>
 4 #include <memory.h>
 5  
 6 void main()
 7 {
 8 WSAData wsd;
 9 WSAStartup(MAKEWORD(2,0),&wsd);
10  
11 SOCKET s =NULL;
12 s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
13 struct sockaddr_in ch;
14 memset(&ch,0,sizeof(ch));
15 ch.sin_family=AF_INET;
16 ch.sin_addr.s_addr=INADDR_ANY;
17 ch.sin_port=htons(1041);
18 int b=bind(s,(struct sockaddr *) &ch,sizeof(ch));
19 #define QUEUE_SIZE 5
20 int l=listen(s,QUEUE_SIZE);
21 printf("正在監聽本機的1041端口!\n");
22 SOCKET sc=accept(s,0,0);
23 printf("客戶端已經連接到本機的1041端口!\n");
24 #define BUF_SIZE 4096
25 int receByt=0;
26 while(1)
27 {
28 char buf[BUF_SIZE];
29 receByt=recv(sc,buf,BUF_SIZE,0);
30 buf[receByt]='\0';
31 if(receByt>0)
32 {
33 printf("接收的消息是:%s\n",buf);
34 }
35 else
36 {
37 printf("接收消息結束!");
38 break;
39 }
40  
41 }
42 int ic=closesocket(sc);
43 int is=closesocket(s);
44  
45 }

 

 

 

 

客戶端發送的代碼:

 

 1 #include <Winsock2.h>
 2 #pragma comment(lib,"Ws2_32.lib")
 3 #include <stdio.h>
 4 #include <memory.h>
 5 #include <string.h>
 6  
 7 void main()
 8 {
 9 WSAData wsd;
10 WSAStartup(MAKEWORD(2,0),&wsd);
11  
12 SOCKET s =NULL;
13 s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
14 struct sockaddr_in ch;
15 memset(&ch,0,sizeof(ch));
16 ch.sin_family=AF_INET;
17 ch.sin_addr.s_addr=inet_addr("127.0.0.1");
18 ch.sin_port=htons(1041);
19  
20 int c=connect(s,(struct sockaddr *) &ch,sizeof(ch));
21 printf("已經連接到服務器的1041端口!現在可以向服務器發送消息了!\n");
22 #define BUF_SIZE 4096
23 char info[1024],buf[BUF_SIZE];
24  
25 while(1)
26 {
27 gets(info);
28 if(info[0]=='\0')
29 break;
30 strcpy(buf,info);
31 int nsend=send(s,buf,strlen(buf),0);
32  
33 }
34 int ic=closesocket(s);
35 }

 

程序代碼經過了優化,並且整合多線程,把接收和發送放到同一個文件中,使用參數模式調用發送和接收模塊。增加了創建SOCKET的創建的時候s句柄(或對象)判斷返回值是否為INVALID_SOCKET,以及socket的bind操作的返回值是否為SOCKET_ERROR,其他socket的操作應該也判斷SOCKET_ERROR,以保證程序的穩定性,這里只是測試代碼就不去寫這么多了,剩下的就由你個人發揮。

 

  1 #include <Winsock2.h>
  2 #pragma comment(lib,"Ws2_32.lib")
  3 #include <stdio.h>
  4 #include <memory.h>
  5 #include <string.h>
  6 #include <pthread.h>
  7  
  8  
  9 void Receive();
 10 void Send();
 11 void creatThread();
 12  
 13 SOCKET s =NULL;
 14 pthread_t t[1000];
 15 int threadCount=0;
 16  
 17 void main(int argc,char* argv[])
 18 {
 19   printf("本程序制作人學號:713901040041\n");
 20   printf("程序說明:服務器端和客戶端為同一個程序,請使用不同的參數運行.\n");
 21   printf("接收程序請使用 r參數;發送程序請使用 s參數。\n");
 22   //printf("len : %d\n", argc);
 23   //printf("count %d\n",argc);
 24   //printf("value: %s\n",argv[1]);
 25   //printf("%d",argv[1][0]=='r');
 26  
 27   if(argc<=1)
 28   {
 29     printf("please input program arguments ...\n");
 30     exit(0);
 31   }
 32   if(argc>1 && argv[1][0]=='r')
 33   {
 34     printf("run receive ...\n");
 35     Receive();
 36   }
 37   if(argc>1 && argv[1][0]=='s')
 38   {
 39     printf("run send ...\n");
 40     Send();
 41   }
 42 }
 43  
 44  
 45 void* receiveWork(void * args)
 46 {
 47   SOCKET sc=accept(s,0,0);
 48   if(sc==INVALID_SOCKET)
 49   {
 50     printf("sc Error");
 51   }
 52   creatThread();
 53  
 54   printf("----------客戶端已經連接到本機的%d線程連接!\n",threadCount-2);
 55 #define BUF_SIZE 4096
 56   int receByt=0;
 57   while(1)
 58   {
 59     char buf[BUF_SIZE];
 60     receByt=recv(sc,buf,BUF_SIZE,0);
 61     buf[receByt]='\0';
 62     if(receByt>0)
 63     {
 64       printf("線程接收的消息是:%s\n",buf);
 65     }
 66     else
 67     {
 68       printf("客戶端已退出,");
 69       break;
 70     }
 71        
 72   }
 73   int ic=closesocket(sc);
 74   printf("服務器結束連接!\n");
 75   return NULL;
 76 }
 77  
 78 void creatThread()
 79 {
 80   pthread_create(&t[threadCount++],NULL,receiveWork,NULL);
 81 }
 82  
 83  
 84 void Receive()
 85 {
 86   WSAData wsd;
 87   WSAStartup(MAKEWORD(2,0),&wsd);  
 88   s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 89   if(s==INVALID_SOCKET)
 90   {
 91     printf("socket created Error");
 92   }
 93   struct sockaddr_in ch;
 94   memset(&ch,0,sizeof(ch));
 95   ch.sin_family=AF_INET;
 96   ch.sin_addr.s_addr=INADDR_ANY;
 97   ch.sin_port=htons(1041);
 98   int b=bind(s,(struct sockaddr *) &ch,sizeof(ch));
 99   if(b==SOCKET_ERROR)
100   {
101     printf("bind 失敗,出錯代碼是:%d\n",WSAGetLastError());
102     exit(0);
103   }
104 #define QUEUE_SIZE 5
105   int l=listen(s,QUEUE_SIZE);
106   printf("正在監聽本機的1041端口!\n");
107    
108   creatThread();
109  
110   for(int i=0;i<1000;i++)
111   {
112     pthread_join(t[i],NULL);
113   }
114  
115 int is=closesocket(s);
116 }
117  
118  
119  
120 void Send()
121 {
122   WSAData wsd;
123   WSAStartup(MAKEWORD(2,0),&wsd);
124  
125   SOCKET s =NULL;
126   s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
127   if(s==INVALID_SOCKET)
128   {
129     printf("socket created Error");
130   }
131   struct sockaddr_in ch;
132   memset(&ch,0,sizeof(ch));
133   ch.sin_family=AF_INET;
134   ch.sin_addr.s_addr=inet_addr("127.0.0.1");
135   ch.sin_port=htons(1041);
136  
137   int c=connect(s,(struct sockaddr *) &ch,sizeof(ch));
138   printf("已經連接到服務器的1041端口!現在可以向服務器發送消息了!\n");
139 #define BUF_SIZE 4096
140   char info[1024],buf[BUF_SIZE];
141  
142   while(1)
143   {
144     gets(info);
145     if(info[0]=='\0')
146       break;
147     strcpy(buf,info);
148     int nsend=send(s,buf,strlen(buf),0);
149   }
150   int ic=closesocket(s);
151 }

原文點擊這里


免責聲明!

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



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