zoukankan      html  css  js  c++  java
  • 用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端口!
    ");
    22 SOCKET sc=accept(s,0,0);
    23 printf("客户端已经连接到本机的1041端口!
    ");
    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]='';
    31 if(receByt>0)
    32 {
    33 printf("接收的消息是:%s
    ",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端口!现在可以向服务器发送消息了!
    ");
    22 #define BUF_SIZE 4096
    23 char info[1024],buf[BUF_SIZE];
    24  
    25 while(1)
    26 {
    27 gets(info);
    28 if(info[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
    ");
     20   printf("程序说明:服务器端和客户端为同一个程序,请使用不同的参数运行.
    ");
     21   printf("接收程序请使用 r参数;发送程序请使用 s参数。
    ");
     22   //printf("len : %d
    ", argc);
     23   //printf("count %d
    ",argc);
     24   //printf("value: %s
    ",argv[1]);
     25   //printf("%d",argv[1][0]=='r');
     26  
     27   if(argc<=1)
     28   {
     29     printf("please input program arguments ...
    ");
     30     exit(0);
     31   }
     32   if(argc>1 && argv[1][0]=='r')
     33   {
     34     printf("run receive ...
    ");
     35     Receive();
     36   }
     37   if(argc>1 && argv[1][0]=='s')
     38   {
     39     printf("run send ...
    ");
     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线程连接!
    ",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]='';
     62     if(receByt>0)
     63     {
     64       printf("线程接收的消息是:%s
    ",buf);
     65     }
     66     else
     67     {
     68       printf("客户端已退出,");
     69       break;
     70     }
     71        
     72   }
     73   int ic=closesocket(sc);
     74   printf("服务器结束连接!
    ");
     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
    ",WSAGetLastError());
    102     exit(0);
    103   }
    104 #define QUEUE_SIZE 5
    105   int l=listen(s,QUEUE_SIZE);
    106   printf("正在监听本机的1041端口!
    ");
    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端口!现在可以向服务器发送消息了!
    ");
    139 #define BUF_SIZE 4096
    140   char info[1024],buf[BUF_SIZE];
    141  
    142   while(1)
    143   {
    144     gets(info);
    145     if(info[0]=='')
    146       break;
    147     strcpy(buf,info);
    148     int nsend=send(s,buf,strlen(buf),0);
    149   }
    150   int ic=closesocket(s);
    151 }

    原文点击这里

  • 相关阅读:
    Taxes
    Tennis Championship
    Urbanization
    字符串的匹配
    Alyona and a tree
    Alyona and mex
    Alyona and flowers
    Alyona and copybooks
    Subordinates
    线程的暂停、恢复和终止
  • 原文地址:https://www.cnblogs.com/tig666666/p/6638295.html
Copyright © 2011-2022 走看看