Windows下C语言网络编程快速入门

简介:
导读:
  C语言的学习,一般的方式是,先学C,然后是C++,最好还要有汇编语言和微机原理基础,然后才是Visual C++。这样的方式,对学习者来说,要花费很多时间和耐力。而在学校教学中,也没有时间深入学习Windows编程的实用技术了。
  其实,具有了C语言基础后,再有一些基本的C++类的概念,就可以直接学习Windows C编程了。
   一、走近Windows C语言
  很多语言都把显示一个“Hello,World!”做为第一个入门程序, C语言的第一个程序是这样的:
  #include
  main()
  {
  printf(“Hello,World!”);
  }
  如果把main函数写成带参数的main函数,应该是:
  #include
  main(int arge,char *argv[])
  {
  printf(“Hello,World!”);
  }
  Windows C的第一个程序和这个程序在形式和原理上都是一致的,只是有两点不同:
  1. 主函数接收的形参不只是命令行中的字符串的个数和字符串的首地址。
  2. C语言的很多函数在Windows C中都可以继续使用,但象printf()屏幕显示等函数就不能继续使用了。因为Windows是多任务操作系统,屏幕已不再为某一个应用程序所独有,Windows C应用程序要显示字符串,需要使用Windows提供的API函数,开自己的窗口
  下面是一个最简单的,显示“Hello,World!”的Windows C程序:
  #include
  APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
  LPSTR lpCmdLine,int nCmdShow)
  {
  MessageBox(NULL,"Hello,World!","第一个Windows C程序",MB_OK|MB_ICONASTERISK);
  }
  主函数的形参有四个:
  1) Hinstance:接收程序运行时当前实例的句柄;
  2) HprivInstance:前一个实例的句柄;
  3) LpCmdLine:程序命令行指针;
  4) NcmdShow:一个用来指定窗口显示方式的整数。
  这几个参数的使用我们会在深入的学习中介绍的。
  显示Hello,Word!字符串,我们使用了一个MessageBox函数,这个函数会在屏幕上显示一个对话框,它的原型是:
  int MessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UNIT uType)
  四个参数分别是:
  1) HWnd:父窗口的句柄;
  2) LpText:要显示字符串的指针;
  3) LpCaption:对话框标题字符串的指针;
  4) UType:显示在对话框上的小图标的类型。
  使用这个函数要包含windows.h头文件。
  调试一下,怎么样?窗口上弹出了一个“第一个Windows C程序”对话框,上面有一行字:“Hello,World!”。
  世界真的很美好啊!!
  深入编程:
  在C语言中,函数的声明,如果没有指明返回值类型,缺省值为void,这个程序的主函数就没有返回值。不过,在Windows编程时,我们最好养成个好习惯,指明函数的返回值类型,因为在C++中,函数返回值类型是不可以缺省的。而我们在Windows C编程时,还是会用到C++的一些概念,这样做,有利于以后深入地学习。
  规范一点的程序应该是这样的:
  #include
  int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
  LPSTR lpCmdLine,int nCmdShow)
  {
  MessageBox(NULL,"Hello,World!","第一个Windows C程序",MB_OK|MB_ICONASTERISK);
  return 0;
  }
  这里,我们声明的类型为int型,并且返回一个值0,这样的函数就可以使用在复杂一点的函数调用中了。
  在这一节中,我们有几处都提到了句柄的概念,句柄和指针的概念不同,它是作为操作系统内部索引表中的一个值来使用的,这样可以防止应用程序直接访问名对象的内部结构,体现了Windows资源管理的优越性。譬如说,一个窗口找开之后,好对应内存中的一个内存块,这个窗口所在的内存快地址往往会由操作系统做动态的调整,但其却不会随之变化。不过,通过它可以访问这个窗口,所以在使用的时候,可以把它当做指针一样看待。
   二、 获取本地计算机的主机名和IP地址
  和C语言一样,函数是Windows C编程的最基本的单位。不过,Windows C主要使用API函数,而网络编程则主要使用Winsock提供的API函数。
  Winsock是90年代初,为了方便网络编程,由Microsoft联合了其他几家公司共同制定的一套WINDOWS下的网络编程接口,它是通过C语言的动态链接库方式提供给用户及软件开发者的,主要由winsock.h头文件和动态链接库winsock.dll组成,目前有两个版本:Winsock1.1和Winsock2.0。
  在Win32平台上,访问众多的基层网络协议,Winsock是首选接口。
  用Visual C++6.0编译Windows C程序,使用Winsock API函数时,首先要把wsock32.lib添加到它的库模块中,否刚在链接的时候,会出现“error LNK2001”错误。添加wsock32.lib的具体步骤是:打开工程菜单,选择设置,在弹出的Project settings对话框中,点击link选项卡,然后在对象/库模块文本框中添加wsock32.lib。
  最简单的网络编程是获取本机的主机名和IP地址,这个程序使用了WSAStart()、WSAClenaup()、gethostname()、gethostbyname()四个winsock API函数,这四个函数的功能和使用方法介绍如下:
  1. WSAStartup():
  【函数原型】
  int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);
  【使用说明】
  每一个使用winsock的应用程序,都必须进行WSAStart函数调用,并且只有在调用成功之后才能使用其它的winsock网络操作函数。
  WVersionRequired:<输入>表示欲使用的Winsock版本,这是一个WORD类型的整数,它的高位字节定义的是次版本号,低位字节定义的是主版本号。
  LpWSAData:<输出>是一个指向WSADATA资料的指针。这个资料我们一般不使用。
  返回值:调用成功返回0;否则,返回出错信息。
  2. WSAClenaup():
  【函数原型】
  int PASCAL FAR WSACleanup(void);
  【使用说明】
  winsock使用后,要调用WSACleanup函数关闭网络设备,以便释放其占用的资源。
  3.gethostname()
  【函数原型】
  int PASCAL FAR gethostname (char FAR * name, int namelen);
  【使用说明】
  该函数可以获取本地主机的主机名,其中:
  name:<输出>用于指向所获取的主机名的缓冲区的指针。
  Namelen:<输入>缓冲区的大小,以字节为单位。
  返回值:若无错误,返回0;否则,返回错误代吗。
  4.gethostbyname()
  【函数原型】
  struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name);
  【使用说明】
  该函数可以从主机名数据库中得到对应的“主机”。
  该函数唯一的参数name就是前面调用函数gethostname()得到的主机名。若无错误,刚返回一个指向hostent结构的批针,它可以标识一个“主机”列表。
  Hostent结构定义如下:
  Struct hostent
  {
  char FAR * h_name;
  char FAR FAR ** h_aliases;
  short h_addrtype;
  char FAR FAR ** h_addr_list;
  }
  其中:
  h_name:<输入>主机名地址(PC)。
  h_aliases:一个由主机备用名组成的空中止数组。
  H_addrtype:返回地址的类型,对于Winsock,这个域总是PF_INET。
  H_lenth:每个地址的长度(字节数),对应于PF_INET域应该为4。
  H_addr_list:应该以空指针结尾的主机地址的列表,返回的地址是以网络顺序排列的。
  其中,h_addr_list[0]存放的就是本地主机的4个字节的IP地址,即:
  h_addr_list[0][0].h_addr_list[0][1].h_addr_list[0][2].h_addr_list[0][3]
  一个简单的用消息框显示主机名和IP地址的源程序如下:
  #include
  int WSA_return;
  WSADATA WSAData;
  HOSTENT *host_entry;
  char host_name[256];
  char host_address[256];
  int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
  LPSTR lpCmdLine,int nCmdShow)
  {
  WSA_return=WSAStartup(0x0101,&WSAData);
  if(WSA_return==0)
  {
  gethostname(host_name,256);
  host_entry=gethostbyname(host_name);
  if(host_entry!=0)
  {
  wsprintf(host_address,"%d.%d.%d.%d",
  (host_entry->h_addr_list[0][0]&0x00ff),
  (host_entry->h_addr_list[0][1]&0x00ff),
  (host_entry->h_addr_list[0][2]&0x00ff),
  (host_entry->h_addr_list[0][3]&0x00ff));
  MessageBox(NULL,host_address,host_name,MB_OK);
  }
  }
  WSACleanup();
  return 0;
  }
  深入编程:
  前面显示IP地址的时候,我们使用的是消息框,规范一点的编程应该使用对话框,如何编辑一个对话框,很多书中都有介绍,编辑的对话框可参考图5的运行界面。
  头文件Get_IP.h如下:
  BOOL APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam);
  这个程序只使用了一个对话框过程,一般把这个过程的声明放在头文件中。
  源程序Get_IP.c:
  #include
  #include"Get_IP.h"
  #include"resource.h" //这个头文件在创建资源的时候会自动生成,
  //并会在插入资源时自动生成控件标识号.
  int WSA_return;
  WSADATA WSAData;
  HOSTENT *host_entry;
  char host_name[256];
  char host_address[256];
  int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
  LPSTR lpCmdLine,int nCmdShow)
  {
  WSA_return=WSAStartup(0x0101,&WSAData);
  if(WSA_return==0)
  {
  gethostname(host_name,256);
  host_entry=gethostbyname(host_name);
  if(host_entry!=0)
  {
  wsprintf(host_address,"%d.%d.%d.%d",
  (host_entry->h_addr_list[0][0]&0x00ff),
  (host_entry->h_addr_list[0][1]&0x00ff),
  (host_entry->h_addr_list[0][2]&0x00ff),
  (host_entry->h_addr_list[0][3]&0x00ff));
  }
  }
  WSACleanup();
  DialogBox(hInstance,"DIALOG1",NULL,(DLGPROC)Hostname_ipDlgPro);
  return 0;
  }
  BOOL APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,
  WPARAM wParam,LPARAM lParam)
  {
  switch(message)
  {
  case WM_INITDIALOG:
  return(TRUE);
  case WM_COMMAND:
  if(LOWORD(wParam)==IDOK)
  {
  SetDlgItemText(hDlg,IDC_EDIT1,host_name);
  SetDlgItemText(hDlg,IDC_EDIT2,host_address);
  SetDlgItemText(hDlg,IDCANCEL,"确定");
  }
  if(LOWORD(wParam)==IDCANCEL)
  EndDialog(hDlg,TRUE);
  return(TRUE);
  break;
  }
  return(FALSE);
  }
   三、利用VisualC++6.0编译Windows C程序
  利用Visual C++6.0编译Windows C程序一般要经过以下四个步骤:新建项目、添加代码、添加资源和编译链接。下面我们简单地介绍一下程序上面介绍的规范的获取本机的主机名和IP地址程序的编译过程:
  (一) 新建项目
  1.启动MicrosoftVisualC++,然后在【文件】菜单中先择【新建】命令,弹出如图1所示的【新建】对话框:
   176588.jpg
  
  图1
  2.在【新建】对话框中,系统打开的是默认的【工程】选项卡,【工程】选项卡左侧的列表框中有多种建立工程的方式,我们选中“Win32 Application”选项。
  3. 在【位置】文本框中输入新建工程的路径(例如:F:\),在【工程】文本框中输入工程名称(例如:Get_IP)。
  4. 选中【平台】列表框中的Win32复选框,然后单击【确定】按钮。
  5. 在随后的对话框中,都选择默认设置,完成后,进入图2示界面:
   176593.jpg
  
  图2
  (二) 添加代码
  在VisualC++6.0中,源代码一般存放在源代码文件和头文件中,往项目中添加源代码是非常方便的,为项目新建一个源代码文件一般要按下述方法操作:
  1. 选择【工程】|【添加工程】|【新建】选项,弹出图3所示【新建】对话框:
   176600.jpg
  
  图3
  2. 在对话框的【文件】选项卡中,左侧的列表框选中“C++ Source File”选项,右侧选中【添加工程】复选框,并在【文件】文本框中输入源文件名(例如:Get_IP.c)。
  3. 单击【确定】按钮,【新建】对话框将被闭,用户就可以在新建的Get_IP.c中输入程序的源代码了。
  4. 添加头文件Get_IP.h的方法和上面所述过程一样,只是在【文件】选项卡中,左侧的列表框要先中“C/C++ Header File”选项。在【文件】文本框中输入头文件名(例如:Get_IP.h)。
  (三) 添加资源
  在添加资源前,必须在项目中先添加一个资源文件,然后可利用Visual C++6.0提供的资源编辑器为项目新建一个资源,具体步骤如下:
  1. 选择【工程】|【添加工程】|【新建】选项,弹出图3所示【新建】对话框。
  2. 在对话框的【文件】选项卡中,左侧的列表框选中“Rsource Script”选项,右侧选中【添加工程】复选框,并在【文件】文本框中输入资源文件名(例如:Get_IP.rc)。
  3. 单击确定,回到主窗口后,选择【插入】|【资源】选项,打开【插入资源】对话框,如图4所示, 在【资源类型】列表框中选中“Dialog”选项,单击【新建】按钮,返回主窗口后,即可利用对话框编辑器进行编辑了。编辑后的对话框如图
   176603.jpg
  
  图4
   176605.jpg
  
  图5
  (四) 编译链接
  在添加了源代码与资源文件后,就可以对程序编译连接了,可按Ctrl+F7键编译,按F7键连接,按Ctrl+F5键运行程序。在连接前是要注意,资源文件Get_IP.rc也要进行编译。
  由于这个程序引用了Winsock API函数,在编译连接前,还要添加wsock32.dll,具体方法前面已经介绍过,这里就不再赘述了。
  一点看法:
  利用C语言编写Windows应用程序有两种方式:一种是Windows C编程方式,另一种是Visual C++编程方式。在一般情况下,Visual C++编程方式编写的程序源代码量小、开发时的工作量小、工作难度也较小,但编译后的代码量较大,运行速度略低;而Windows C编程方式编写的程序源代码量虽然较大,但可执行代码效率高。随着技术的进步,Visual C++编程方式已被广泛采用,但象网络编程等一些对速度要求高、对硬件操作较多的程序,大多数还是用Windows C编程方式开发的。另外,学习Windows C程序设计,还有助于更深入地了解Windows的内幕和Windows API。

  从教学角度讲,在学生具备了C语言和其它一些前导课程基础后,直接进入Windows C网络编程等实用编程技术课程,不仅可以让学生尽早地接触到前沿的实用编程技术,而且还可以极大地调动学生的学习积极性,在有限的时间里,学到更多的知识和技术。


本文转自feisky博客园博客,原文链接:http://www.cnblogs.com/feisky/archive/2008/04/11/1586590.html,如需转载请自行联系原作者

相关文章
|
14天前
|
网络协议 Unix C语言
C语言 网络编程(十六)广播和组播
广播和组播是网络通信的重要方式。广播允许一台主机向子网内所有主机发送数据包,常用于局域网内的消息传播;组播则将数据包发送给特定的一组主机,适用于视频会议等应用场景。广播地址如 `192.168.1.255` 用于同一子网的所有主机。组播地址如 `224.0.0.0` 至 `239.255.255.255` 标识特定主机群。C语言示例展示了如何通过 UDP 实现广播和组播通信。此外,UNIX域套接字用于同一机器上进程间的高效通信。
|
14天前
|
网络协议 算法 网络性能优化
C语言 网络编程(十五)套接字选项设置
`setsockopt()`函数用于设置套接字选项,如重复使用地址(`SO_REUSEADDR`)、端口(`SO_REUSEPORT`)及超时时间(`SO_RCVTIMEO`)。其参数包括套接字描述符、协议级别、选项名称、选项值及其长度。成功返回0,失败返回-1并设置`errno`。示例展示了如何创建TCP服务器并设置相关选项。配套的`getsockopt()`函数用于获取这些选项的值。
|
14天前
|
网络协议 C语言
C语言 网络编程(十三)并发的TCP服务端-以进程完成功能
这段代码实现了一个基于TCP协议的多进程并发服务端和客户端程序。服务端通过创建子进程来处理多个客户端连接,解决了粘包问题,并支持不定长数据传输。客户端则循环发送数据并接收服务端回传的信息,同样处理了粘包问题。程序通过自定义的数据长度前缀确保了数据的完整性和准确性。
|
14天前
|
网络协议 C语言
C语言 网络编程(十一)TCP通信创建流程---服务端
在服务器流程中,新增了绑定IP地址与端口号、建立监听队列及接受连接并创建新文件描述符等步骤。`bind`函数用于绑定IP地址与端口,`listen`函数建立监听队列并设置监听状态,`accept`函数则接受连接请求并创建新的文件描述符用于数据传输。套接字状态包括关闭(CLOSED)、同步发送(SYN-SENT)、同步接收(SYN-RECEIVE)和已建立连接(ESTABLISHED)。示例代码展示了TCP服务端程序如何初始化socket、绑定地址、监听连接请求以及接收和发送数据。
|
14天前
|
网络协议 C语言
C语言 网络编程(十四)并发的TCP服务端-以线程完成功能
这段代码实现了一个基于TCP协议的多线程服务器和客户端程序,服务器端通过为每个客户端创建独立的线程来处理并发请求,解决了粘包问题并支持不定长数据传输。服务器监听在IP地址`172.17.140.183`的`8080`端口上,接收客户端发来的数据,并将接收到的消息添加“-回传”后返回给客户端。客户端则可以循环输入并发送数据,同时接收服务器回传的信息。当输入“exit”时,客户端会结束与服务器的通信并关闭连接。
|
14天前
|
C语言
C语言 网络编程(八)并发的UDP服务端 以进程完成功能
这段代码展示了如何使用多进程处理 UDP 客户端和服务端通信。客户端通过发送登录请求与服务端建立连接,并与服务端新建的子进程进行数据交换。服务端则负责接收请求,验证登录信息,并创建子进程处理客户端的具体请求。子进程会创建一个新的套接字与客户端通信,实现数据收发功能。此方案有效利用了多进程的优势,提高了系统的并发处理能力。
|
14天前
|
网络协议 C语言
C语言 网络编程(十二)TCP通信创建-粘包
TCP通信中的“粘包”现象指的是由于协议特性,发送方的数据包被拆分并在接收方按序组装,导致多个数据包粘连或单个数据包分割。为避免粘包,可采用定长数据包或先传送数据长度再传送数据的方式。示例代码展示了通过在发送前添加数据长度信息,并在接收时先读取长度后读取数据的具体实现方法。此方案适用于长度不固定的数据传输场景。
|
14天前
|
C语言
C语言 网络编程(七)UDP通信创建流程
本文档详细介绍了使用 UDP 协议进行通信的过程,包括创建套接字、发送与接收消息等关键步骤。首先,通过 `socket()` 函数创建套接字,并设置相应的参数。接着,使用 `sendto()` 函数向指定地址发送数据。为了绑定地址,需要调用 `bind()` 函数。接收端则通过 `recvfrom()` 函数接收数据并获取发送方的地址信息。文档还提供了完整的代码示例,展示了如何实现 UDP 的发送端和服务端功能。
|
14天前
|
网络协议 C语言
C语言 网络编程(十)TCP通信创建流程---客户端
在TCP通信中,客户端需通过一系列步骤与服务器建立连接并进行数据传输。首先使用 `socket()` 函数创建一个流式套接字,然后通过 `connect()` 函数连接服务器。连接成功后,可以使用 `send()` 和 `recv()` 函数进行数据发送和接收。最后展示了一个完整的客户端示例代码,实现了与服务器的通信过程。
|
14天前
|
C语言
C语言 网络编程(九)并发的UDP服务端 以线程完成功能
这是一个基于UDP协议的客户端和服务端程序,其中服务端采用多线程并发处理客户端请求。客户端通过UDP向服务端发送登录请求,并根据登录结果与服务端的新子线程进行后续交互。服务端在主线程中接收客户端请求并创建新线程处理登录验证及后续通信,子线程创建新的套接字并与客户端进行数据交换。该程序展示了如何利用线程和UDP实现简单的并发服务器架构。