国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > C > 正文

用C語(yǔ)言進(jìn)行最基本的socket編程

2020-01-26 14:49:18
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

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

簡(jiǎn)單的發(fā)送和接收實(shí)現(xiàn)

服務(wù)器端接收代碼:

#include <Winsock2.h>#pragma comment(lib,"Ws2_32.lib")#include <stdio.h>#include <memory.h>void main(){WSAData wsd;WSAStartup(MAKEWORD(2,0),&wsd);SOCKET s =NULL;s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);struct sockaddr_in ch;memset(&ch,0,sizeof(ch));ch.sin_family=AF_INET;ch.sin_addr.s_addr=INADDR_ANY;ch.sin_port=htons(1041);int b=bind(s,(struct sockaddr *) &ch,sizeof(ch));#define QUEUE_SIZE 5int l=listen(s,QUEUE_SIZE);printf("正在監(jiān)聽本機(jī)的1041端口!/n");SOCKET sc=accept(s,0,0);printf("客戶端已經(jīng)連接到本機(jī)的1041端口!/n");#define BUF_SIZE 4096int receByt=0;while(1){char buf[BUF_SIZE];receByt=recv(sc,buf,BUF_SIZE,0);buf[receByt]='/0';if(receByt>0){printf("接收的消息是:%s/n",buf);}else{printf("接收消息結(jié)束!");break;}}int ic=closesocket(sc);int is=closesocket(s);}

客戶端發(fā)送的代碼:

#include <Winsock2.h>#pragma comment(lib,"Ws2_32.lib")#include <stdio.h>#include <memory.h>#include <string.h>void main(){WSAData wsd;WSAStartup(MAKEWORD(2,0),&wsd);SOCKET s =NULL;s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);struct sockaddr_in ch;memset(&ch,0,sizeof(ch));ch.sin_family=AF_INET;ch.sin_addr.s_addr=inet_addr("127.0.0.1");ch.sin_port=htons(1041);int c=connect(s,(struct sockaddr *) &ch,sizeof(ch));printf("已經(jīng)連接到服務(wù)器的1041端口!現(xiàn)在可以向服務(wù)器發(fā)送消息了!/n");#define BUF_SIZE 4096char info[1024],buf[BUF_SIZE];while(1){gets(info);if(info[0]=='/0')break;strcpy(buf,info);int nsend=send(s,buf,strlen(buf),0);}int ic=closesocket(s);}

程序代碼經(jīng)過(guò)了優(yōu)化,并且整合多線程,把接收和發(fā)送放到同一個(gè)文件中,使用參數(shù)模式調(diào)用發(fā)送和接收模塊。增加了創(chuàng)建SOCKET的創(chuàng)建的時(shí)候s句柄(或?qū)ο螅┡袛喾祷刂凳欠駷镮NVALID_SOCKET,以及socket的bind操作的返回值是否為SOCKET_ERROR,其他socket的操作應(yīng)該也判斷SOCKET_ERROR,以保證程序的穩(wěn)定性,這里只是測(cè)試代碼就不去寫這么多了,剩下的就由你個(gè)人發(fā)揮。

#include <Winsock2.h>#pragma comment(lib,"Ws2_32.lib")#include <stdio.h>#include <memory.h>#include <string.h>#include <pthread.h>void Receive();void Send();void creatThread();SOCKET s =NULL;pthread_t t[1000];int threadCount=0;void main(int argc,char* argv[]){  printf("本程序制作人學(xué)號(hào):713901040041/n");  printf("程序說(shuō)明:服務(wù)器端和客戶端為同一個(gè)程序,請(qǐng)使用不同的參數(shù)運(yùn)行./n");  printf("接收程序請(qǐng)使用 r參數(shù);發(fā)送程序請(qǐng)使用 s參數(shù)。/n");  //printf("len : %d/n", argc);  //printf("count %d/n",argc);  //printf("value: %s/n",argv[1]);  //printf("%d",argv[1][0]=='r');  if(argc<=1)  {    printf("please input program arguments .../n");    exit(0);  }  if(argc>1 && argv[1][0]=='r')  {    printf("run receive .../n");    Receive();  }  if(argc>1 && argv[1][0]=='s')  {    printf("run send .../n");    Send();  }}void* receiveWork(void * args){  SOCKET sc=accept(s,0,0);  if(sc==INVALID_SOCKET)  {    printf("sc Error");  }  creatThread();  printf("----------客戶端已經(jīng)連接到本機(jī)的%d線程連接!/n",threadCount-2);#define BUF_SIZE 4096  int receByt=0;  while(1)  {    char buf[BUF_SIZE];    receByt=recv(sc,buf,BUF_SIZE,0);    buf[receByt]='/0';    if(receByt>0)    {      printf("線程接收的消息是:%s/n",buf);    }    else    {      printf("客戶端已退出,");      break;    }        }  int ic=closesocket(sc);  printf("服務(wù)器結(jié)束連接!/n");  return NULL;}void creatThread(){  pthread_create(&t[threadCount++],NULL,receiveWork,NULL);}void Receive(){  WSAData wsd;  WSAStartup(MAKEWORD(2,0),&wsd);    s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);  if(s==INVALID_SOCKET)  {    printf("socket created Error");  }  struct sockaddr_in ch;  memset(&ch,0,sizeof(ch));  ch.sin_family=AF_INET;  ch.sin_addr.s_addr=INADDR_ANY;  ch.sin_port=htons(1041);  int b=bind(s,(struct sockaddr *) &ch,sizeof(ch));  if(b==SOCKET_ERROR)  {    printf("bind 失敗,出錯(cuò)代碼是:%d/n",WSAGetLastError());    exit(0);  }#define QUEUE_SIZE 5  int l=listen(s,QUEUE_SIZE);  printf("正在監(jiān)聽本機(jī)的1041端口!/n");    creatThread();  for(int i=0;i<1000;i++)  {    pthread_join(t[i],NULL);  }int is=closesocket(s);}void Send(){  WSAData wsd;  WSAStartup(MAKEWORD(2,0),&wsd);  SOCKET s =NULL;  s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);  if(s==INVALID_SOCKET)  {    printf("socket created Error");  }  struct sockaddr_in ch;  memset(&ch,0,sizeof(ch));  ch.sin_family=AF_INET;  ch.sin_addr.s_addr=inet_addr("127.0.0.1");  ch.sin_port=htons(1041);  int c=connect(s,(struct sockaddr *) &ch,sizeof(ch));  printf("已經(jīng)連接到服務(wù)器的1041端口!現(xiàn)在可以向服務(wù)器發(fā)送消息了!/n");#define BUF_SIZE 4096  char info[1024],buf[BUF_SIZE];  while(1)  {    gets(info);    if(info[0]=='/0')      break;    strcpy(buf,info);    int nsend=send(s,buf,strlen(buf),0);  }  int ic=closesocket(s);}

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表

圖片精選

主站蜘蛛池模板: 彩票| 沛县| 外汇| 土默特右旗| 芜湖市| 靖西县| 龙泉市| 永寿县| 惠水县| 上蔡县| 科尔| 砚山县| 莱西市| 禹城市| 黄骅市| 涟源市| 吕梁市| 纳雍县| 平阴县| 山西省| 广平县| 镇赉县| 凤城市| 迭部县| 博湖县| 屏南县| 花垣县| 安阳县| 卓资县| 论坛| 股票| 谷城县| 东乡| 宁阳县| 翼城县| 宝丰县| 大竹县| 子长县| 泾阳县| 宝丰县| 正蓝旗|