#pragma comment (lib,"ws2_32.lib")

#include <Winsock2.h>

#include <stdio.h>

如你所知,简单的网络编程就是称为客户端和服务器的两台主机进行通信。显然通信双方要有一个统一的标识,电话机的比方就很好。这个标识不仅仅是IP地址或者端口号,我们可以将二者结合起来。称之为套接字,socket。在网络编程中socket无疑是关键的部分,因此网络编程也常常被叫做socket编程。

叫什么不重要,重要的是原理。本文的目的也正是这样,我们试图搞懂它。

为了减少篇幅,关于协议,TCP,UDP,ISO七层模型等,这些基础知识这里就不做说明了。我们采用的是客户/服务器的模式。又客户对服务器做出通信请求,而服务器对其响应。另外本文的所有程序都可以在vc6.0中实现。

首先我们先大体文字介绍一下双方的通信流程。比如服务器端:

1,打开通信通道并告知本地主机,服务器端可在某一地址和端口接收客户端请求。

2,等待客户请求到达该端口。

3,接收到重复的服务请求,处理并发送应答信号。如果是并发的请求,那么就启动一个新进程。服务完成后,关闭此进程与客户的通信链路,并终止。

4,返回第二步,也就是继续等待新的请求

5,关闭服务器

客户端所做的要更为简单

1,打开一个通信通道,并连接到服务器所在主机的特定端口

2,向服务器端发送请求报文,等待并接受应答,然后可继续提出请求

3,通信结束,关闭通信通道并终止

为了更接近程序实现,可以吧流程简化一下。当然TCP和UDP的实现方法略有不同。事实上windows socket程序只是在伯克利的基础上加了一下异步函数,和符合wondows消息驱动特性的网络事件异步选择机制。根据TCP和UDP的协议远离的区别,可以分为流式套接字(SOCK_STREAM)和数据包套接字(SOCK_DGRAM)

流式套接字服务器端可分为七步

1,创建套接字(socket函数)

2,将套接字绑定到指定端口上(bind函数)

3,将套接字设定为监听模式,准备接受客户请求(listen函数)

4,等待客户请求的到来,当请求到来后接收连接请求,返回一个新的对应于此连接的套接字(accept函数)

5,用返回的套接字和客户进行通信(send/recv函数)

6,返回,等待一个新的客户请求

7,关闭套接字(closesocket函数)

客户端程序可分为四步

1,创建套接字

2,向服务器发出链接请求(connect函数)

3, 和服务器进行通信(send/recv函数)

4, 关闭套接字

基于UDP的套接字服务器端稍有不同,创建,绑定,接收,关闭即可因为是无连接的少去了监听和等待的步骤。同样在客户端也少去了请求连接的过程。

TCP代码实现:首先要建两个工程,不妨设为tcpsrv和tcpclient,分别为客户端和服务器端

tcpsrv.cpp

/*第一步:加载套接字库 WSAstartup函数,另外也可对套接字库进行版本协商,两个参数,wVersionRequested指定请求的版本号,需要注意的是高字节指定副版本,低字节指定主版本。第二个参数是一个指向wsadata的指针,WSAStartup用其加载的库版本有关的信息填在这个结构中,接收windows socket */

WORD wVersionRequested;//定义一个word类型的变量

WSADATA wsaData;

int err;

wVersionRequested = MAKEWORD( 1, 1 );

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ) {

return;

}

if ( LOBYTE( wsaData.wVersion ) != 1

HIBYTE( wsaData.wVersion ) != 1 ) {

WSACleanup( );//终止对winsock库的使用

return;

}

/* 1.创建套接字,socket函数,返回一个套接字的描述符,三个参数,一个参数af指定地址族,第二个参数指定套接字的类型也就是TCP或UDP,第三个参数是与特定的地址家族相关的协议,如果指定为0,那么他就根据地址格式和套接字的类别, 自动为你设置一个合适的协议 如果该函数调用成功,他将返回一个新的SOCKET数据类型的套接字描述符。如果失败则返回一个 INVALID_SOCKET错误信息通过WSAGetLastID_SOCKET函数返回。  */

SOCKET socksrv=socket(AF_INET,SOCK_STREAM,0);

/*2.绑定套接字函数bind,接收三个参数,第一个指定要绑定的套接字,第二个参数指定该套接字的本地地址信息,是一个指向sockaddr

结构的指针变量,由于地址结构是为所有地址家族准备的,这个结构可能随所拥有网络协议不同而不同。所以要用第三个参数指定该地址结构的长度,显然要事先定义sockaddr结构体。

另外,因为实际要求的是内存区,所以对于不同的协议家族,用不同的结构来替换。常用的两个函数

inet_addr(),将点十进制的IP地址转换为适合分配给s_addr的u_long类型的数值

inet_ntoa()函数起相反的作用*/

SOCKADDR_IN addrsrv;

addrsrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY)

addrsrv.sin_family=AF_INET;

addrsrv.sin_port=htons(6000);

bind(socksrv,(SOCKADDR*)&addrsrv,sizeof(SOCKADDR));

//4.将套接字设置为监听模式listen函数,第一个参数指定套机字,第二个参数设置等待连接队列的长度,此处设为5

listen(socaksrv,5);

/*5.设置循环,不断地连接请求的到来,

首先定义一个地址结构体变量来接受客户的地质结构信息。然后定一个整形变量存储地址长度,为防止调用失败设置一个初始值

*/

SOCKADDR_IN addrClient;

int len=sizeof(SOCKADDR);

/*

.在while循环中,用一个函数来等待客户的连接到来并接受客户的连接请求。accept函数,成功后会返回一个新的连接套接字的描述符,而原来的套接字则继续监听客户的连接请求

*/

while(1)

{

SOCKET sockConn=accept(socksrv,(SOCKADDR*)&addrClient,&len);

/*7.进行通信,向客户端发送数据,send函数       */

char sendBuf[100];

sprintf(sendBuf,"welcome %s to MFC",inet_ntoa(addrClient.sin_addr));//ip地址

send(sockConn,sendBuf,strlen(sendBuf)+1,0);

/*8.同时可以从客户端接受数据用recv函数*/

char recvBuf[100];

recv(sockConn,recvBuf,100,0);

printf("%s\n",recvBuf);//打印接收到的数据

//9.关闭套接字

closesocket(sockConn);

}

相应的客户端程序,tcpclient.cpp

void main()

{

WORD wVersionRequested;//定义一个word类型的变量

WSADATA wsaData;

int err;

wVersionRequested = MAKEWORD( 1, 1 );

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ) {

return;

}

if ( LOBYTE( wsaData.wVersion ) != 1 ||

HIBYTE( wsaData.wVersion ) != 1 ) {

WSACleanup( );

return;

}

//第一步创建套接字

SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);

//第二步,因为客户端不需要绑定,可直接去连接服务器端, //connect函数

SOCKADDR_IN addrsrv;

addrsrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1")

//127.0.0.1因为只在本机测试,故此处用本机的IP地址

addrsrv.addr_family=AF_INET;

addrsrv.addr_port=htons(6000);

connect(sockClient,(SOCKADDR*)&addrsrv,sizeof(SOCKADDR));

//第三步接收数据

char recvBuf[100];

recv(sockClient,recvBuf,100,0);

printf("%s\n",recvBuf);//将接受到的数据打印出来

send(sockClient,"this is client",strlen("this is client")+1,0);

//第四步 关闭套接字

closesocket(sockClient);

WSAClenup();

}

UDP的相对来说比较简单

客户端:

void main()

{

WORD wVersionRequested;//定义一个word类型的变量

WSADATA wsaData;

int err;

wVersionRequested = MAKEWORD( 1, 1 );

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ) {

return;

}

if ( LOBYTE( wsaData.wVersion ) != 1 ||

HIBYTE( wsaData.wVersion ) != 1 ) {

WSACleanup( );

return;

}

//第一步同样是新建套接字

SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);

//第二步直接发送数据 用sendto函数

/*

int sendto(SOCKET s, const char FAR *buf, int len, int flags, const struct sockaddr FAR *to,  设定目的套接字的地址结构体信息

int tolen        地址结构体长度                );

*/

SOCKADDR_IN addrsrv;

addrsrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");

addrsrv.sin_family=AF_INET;

addrsrv.sin_port=htons(6000);

sendto(sockClient,"hello",strlen("hello")+1,0,

(SOCKADDR*)&addrsrv,sizeof(SOCKADDR));

closesocket(sockClient);

WSACleanup();

}

服务器端:

void main()

{

WORD wVersionRequested;//定义一个word类型的变量

WSADATA wsaData;

int err;

wVersionRequested = MAKEWORD( 1, 1 );

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ) {

return;

}

if ( LOBYTE( wsaData.wVersion ) != 1 ||

HIBYTE( wsaData.wVersion ) != 1 ) {

WSACleanup( );

return;

}     //第一步同样的创建套接字

SOCKET socksrv=socket(AF_INET,SOCK_DGRAM,0);

SOCKADDR_IN addrsrv;

addrsrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

addrsrv.sin_family=AF_INET;

addrsrv.sin_port=htons(6000);

//绑定套接字

bind(socksrv,(SOCKADDR*)&addrsrv,sizeof(SOCKADDR));

/*接收数据 此处与TCP不同

int recvfrom(  SOCKET s, char FAR* buf,  接收数据的buf

int len,                     长度

int flags,                  0

struct sockaddr FAR *from,   地址结构体指针,表明是一个返回izhi

int FAR *fromlen      设置初始值,并且有返回值      );

首先定义地址结构的变量

*/

SOCKADDR_IN addrClient;

int len=sizeof(SOCKADDR);

char recvBuf[100];

recvfrom(socksrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);

printf("%s \n",recvBuf);

closesocket(socksrv);

WSACleanup();

}

另外加贴一个具有聊天功能的小程序,udp实现

服务器端:

void main()

{

WORD wVersionRequested;//定义一个word类型的变量

WSADATA wsaData;

int err;

wVersionRequested = MAKEWORD( 1, 1 );

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ) {

return;

}

if ( LOBYTE( wsaData.wVersion ) != 1 ||

HIBYTE( wsaData.wVersion ) != 1 ) {

WSACleanup( );

return;

}

SOCKET socksrv=socket(AF_INET,SOCK_DGRAM,0);

SOCKADDR_IN addrsrv;

addrsrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

addrsrv.sin_family=AF_INET;

addrsrv.sin_port=htons(6000);

bind(socksrv,(SOCKADDR*)&addrsrv,sizeof(SOCKADDR));

char recvBuf[100];

char sendBuf[100];

char tempBuf[200];

SOCKADDR_IN addrClient;

int len=sizeof(SOCKADDR);

while(1)

{

recvfrom(socksrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);

if('q'==recvBuf[0])//判断如果第一个字符是Q则退出

{

sendto(socksrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClient,len);

printf("chat end !\n");

break;

}

sprintf(tempBuf,"%s say:%s",inet_ntoa(addrClient.sin_addr),recvBuf);

//如果不是Q则将客户端的IP地址和发送的数据格式化完放到tempBUf中

printf("%s\n",tempBuf);

printf("please input data:\n");

//等待用户输入

gets(sendBuf);//从标准输入流中获取一行数据

sendto(socksrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClient,len);

//将用户输入的数据发送到客户端

}

closesocket(socksrv);

WSACleanup();

}

客户端:

void main()

{

WORD wVersionRequested;//定义一个word类型的变量

WSADATA wsaData;

int err;

wVersionRequested = MAKEWORD( 1, 1 );

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ) {

return;

}

if ( LOBYTE( wsaData.wVersion ) != 1 ||

HIBYTE( wsaData.wVersion ) != 1 ) {

WSACleanup( );

return;

}

SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);

SOCKADDR_IN addrsrv;

addrsrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");

addrsrv.sin_family=AF_INET;

addrsrv.sin_port=htons(6000);

char recvBuf[100];

char sendBuf[100];

char tempBuf[200];

int len=sizeof(SOCKADDR);

while(1)

{

printf("please input data:\n");

gets(sendBuf);

sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,

(SOCKADDR*)&addrsrv,len);

//等待服务器端的回应

recvfrom(sockClient,recvBuf,100,0,

(SOCKADDR*)&addrsrv,&len);

if('q'==recvBuf[0])

{

sendto(sockClient,"q",strlen("q")+1,0,

(SOCKADDR*)&addrsrv,len);

printf("chat end!\n");

break;

}

sprintf(tempBuf,"%s say :%s",inet_ntoa(addrsrv.sin_addr),recvBuf);

printf("%s\n",tempBuf);

}

closesocket(sockClient);

WSACleanup();

}

[windows网络编程]tcp/udp编程初步详解-转相关推荐

  1. uip tcp udp运行流程详解

    uip_process运行流程 uip_process(u8_t flag) (1)if(flag == UIP_UDP_SEND_CONN),若是则goto udp_send;不是则向下执行: (2 ...

  2. 网络编程(Tcp/Udp实现聊天、文件上传)

    网络编程 1.1 概述 计算机网络是指将位置不同的多台[计算机 通过通信线路连接起来,实现资源共享和信息传递的计算机系统 1.2 网络通信的要素 ip和端口 网络通信协议(tcp/udp) 1.3 I ...

  3. TCP/UDP编程中的问题汇总

    TCP/UDP编程中的问题汇总 TCP和UDP发送大文件的问题. 答: 发送端: 发送时,先发送文件的名称及大小等信息. 然后,设置一个缓冲区的大小,假设为4K. 再循环读4K的文件内容,并发送,直到 ...

  4. 原生JAVA的TCP/UDP编程

    一.TCP/UDP对比 二.TCP编程 TCP字节流编程 - Server端: public class TCPServerStream {public static void main(String ...

  5. Java TCP/UDP编程

    java TCP & UDP编程 UDP编程 TCP编程 UDP编程 UPD: User Datagram Protocol.面向无连接,不安全. UDP中,发送方无需知道接收方是否接受,只考 ...

  6. 传输层协议TCP和UDP的区别详解

    一.TCP协议 1.TCP的优点: 可靠,稳定 TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认.窗口.重传.拥塞控制机制,在数据传完后,还会断开连接用来节约系 ...

  7. python黑帽编程视频_Python黑帽编程 3.4 跨越VLAN详解

    VLAN(Virtual Local Area Network),是基于以太网交互技术构建的虚拟网络,既可以将同一物理网络划分成多个VALN,也可以跨越物理网络障碍,将不同子网中的用户划到同一个VLA ...

  8. 蚂蚁算法python_Python编程实现蚁群算法详解

    简介 蚁群算法(ant colony optimization, ACO),又称蚂蚁算法,是一种用来在图中寻找优化路径的机率型算法.它由Marco Dorigo于1992年在他的博士论文中提出,其灵感 ...

  9. Java多线程编程中Future模式的详解

    转载自 https://www.cnblogs.com/winkey4986/p/6203225.html Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker ...

最新文章

  1. optee3.14中MMU页表查询的所需配置--深入解读
  2. Java常用集合体系以及相互区别
  3. hibernate注解实体类(Emp.java)
  4. linux 线程 拷贝,linux下实现多线程拷贝命令
  5. python如何实现共享报表系统_使用python来实现报表自动化-阿里云开发者社区
  6. 如何给mysql表添加百万条数据_给mysql一百万条数据的表添加索引
  7. java set循环取值_java循环遍历类属性 get 和set值方法
  8. ISV客户博客系列:Persistent Systems 使用Windows Azure交付基于Java的CloudNinja项目
  9. 用了这个评估优化LiteOS镜像利器,我有点飘...
  10. qt ui界面无法移动控件_都是知识点!移动端UI设计最基本的10种APP界面类型(上)...
  11. 【bzoj 3131】[Sdoi2013]淘金
  12. java游戏开发入门(一) - HelloWorld
  13. JMeter中BeanShell的使用方法和常用语法
  14. imp-00017 oracle 942,IMP导入遇到IMP-00017,ORA-00942
  15. 薅羊毛php源码,薅羊毛软件-抢福袋源码分享
  16. K8s 开先河、技能全栈、业务“无感”,深度解读云原生的这一年
  17. 面试题:搜狐百度 看1 复杂一点的面试题 笔试题
  18. 四季电台应用项目源码
  19. 浅谈5G通信面临的电磁兼容挑战及解决方法
  20. 微信如何通过ip访问服务器项目,vue2.0 在微信端如何使用本地IP访问项目

热门文章

  1. java留言板功能齐全源码_各类Java微信开发框架源码对比(建议收藏)
  2. Resumable.js - 基于HTML5 File API的可断点续传的文件上传插件
  3. introduction of servlet filter
  4. Spring Boot 学习系列(01)—从0到1,只需两分钟
  5. maven-compiler-plugin的理解
  6. Deployment vs ReplicationController in Kubernetes
  7. 【转载】JS中bind方法与函数柯里化
  8. ubuntu 12.04(64位)下搭建android5.0开发环境 (win7 虚拟机)
  9. Python核心编程答案(自整理)
  10. LNMMP架构的实现