Winsock基础编程

Winsock基础编程

Socket的英文原义是“孔”或“插座”。作为BSD
UNIX的进程通信机制,取后一种意思。通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电,
有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。

1 基于TCP

它是面向连接的,稳定的网络通信。它的通信流程如下所示:

<1>TCP 连接建立

TCP连接的建立需要经过三次握手,所谓的三次握手如下所示:

  • 客户端向服务器发送一个SYN J
  • 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
  • 客户端再想服务器发一个确认ACK K+1

通俗的讲就是:

A:B,我想连接你

B:A,我看到了,你连接吧

A:那我连了

B:等待接受。。。

<2>TCP连接终止

TCP连接的终止需要下面四次挥手。

  • 某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
  • 另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
  • 一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
  • 接收到这个FIN的源发送端TCP对它进行确认。

通俗的讲就是:

A:我要关了啊

B:你要关吗?

B:这么长时间没反应,那我要关了。

A:看到你要关了

B:关闭

<3>TCP稳定性的保证

它采用的是确认重传机制,没发送一个消息它都会启动一个定时器,观察一定时间内有没有收到对方的确认信息,如果收到就算了,没收到它就以为消息没发过去然后就会重新发送。正是由于这种确认重传机制导致了TCP通信的效率没UDP高。

2 基于UDP

它是提供面向无连接的,不可靠的通信协议。它的通信过程如下图所示:

3 相关函数介绍

<1>WSAStartup函数

intPASCAL FAR WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

wVersionRequested:用来指定加载Winsock库的版本,低位字节代表主版本号,高位字节代表副版本号。通常用MAKEWORD(x,y)表示版本号,其中x表示高位自己,y表示低位字节。

lpWSAData:返回的指向WSADATA结构体的指针,这个结构体里面包含着主要关于版本号的信息。

返回值:

成功:返回0。

失败:

WSASYSNOTREADY:表示网络设备没有准备好。

WSAVERNOTSUPPORTTED:Winsock的版本信息号不支持。

WSAEINPROGRESS:一个阻塞式的Winsock1.1存在于进程中。

WSAEPROCLIM:已经达到Winsock的使用量的上限。

WSAEFAULT:lpWSAData不是一个有效的指针。

注意:

这个函数winsock的初始化函数,如果它失败那么后面的所有相关Winsock的都不会被执行。

<2>socket函数

SOCKETsocket(int af,  int type,  int proctocol);

这个socket有点类似句柄,或者文件指针。

af:address family(地址族),一般都填AF_INIT,表示是在Internet上的Socket;

type:Socket的类型,当采用流连接方式时用SOCK_STREAM,用数据报文方式时用

SOCK_DGRAM。

proctocol:一般都为0,表示对2种类型的Socket分别采用缺省的TCP和UDP传输协议。

<3>bind函数

int bind(SOCKET s,  const sockaddr* name,  intnamelen);

这个函数的作用是把socket绑定到指定的IP地址以及端口。

s:要绑定的socket

name:指向sockaddr*的结构体指针。结构体如下所示:

struct sockaddr
{
 u_shortsa_family;
char sa_data[14];
};

但是通常在Winsock中用sockaddr_in结构体代替这个结构体,它如下所示:

struct sockaddr_in
{
short         sin_family;
unsigned short        sin_port;
struct  in_addr      sin_addr;
char         sin_zero[8];
};

其中in_addr是这样一个结构体

struct in_addr {
        union {
                struct{ UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
                struct{ USHORT s_w1,s_w2; } S_un_w;
                ULONG S_addr;
        } S_un;
}

我们可以看到这个结构体里面就有一个union,字节长度是4个字节。可以利用这个结构方便的将点分十进制格式的IP地址转换成u_long类型,并且将结果赋予S_addr。

namelen: name所指向的结构体的长度

注意:

端口号的范围是0-65536,但是1024一下的端口都被预占,所以在指定端口号的要在其上面。

另外,in_addr和sockaddr结构体的字节长度相等。

<4>listen函数

int listen(SOCKET s,  int backlog);

s: 启动监听的socket

backlog:等待连接队伍的最大长度,一般设置为1-5

<5>accept函数

SOCKET accept(SOCKET s,  sockaddr* addr,  int* addrlen);

s:监听状态的套接字

addr:保存客户端的信息,ip,端口号等

addrlen:地址信息的长度,这也是个返回值

注意:

当调用accept函数的时候,程序就会等待客户端调用connect函数发生连接。

<6>connect函数

Int connect(SOCKET s,const sockaddr*name,int namelen);

s:连接的套接字

addr:欲要连接的地址

addrlen:地址信息的长度

<7>send/recv函数

int send(SOCKET s,const char* buf,int len,int flags);

int recv(SOCKET s,const char* buf,int len,int flags);

s:发送信息的socket

buf:发送/接受 数据的缓冲区

len:发送/接受 信息的长度

flags:影响函数行为的值,一般设置为0

注意:

发送的长度一般要根据具体的字节长度来指定,接受的长度则是整个缓冲区的长度。

<8>sendto/recvfrom 函数

int recvfrom(SOCKET s, char* buf, int len,int flags, struct sockaddr* from, int* fromlen);

int sento(SOCKET s, char* buf, int len ,int flags , struct sockaddr* to , int tolen);

s:己方的socket

buf:缓冲区

len:缓冲区长度

flags:一般设置为0,是个影响函数行为的参数

from/to:返回值,表示对方的地址信息 / 输入的对方地址信息

fromlen/tolen:返回值,表示对方地址信息的长度 / 输入的对方地址信息长度

<9> closesocket函数

int closesocket(socket s);

关闭一个socket。

UDP的示例程序:

//server
#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")

void main()
{
         WORD wVersionRequested;
         WSADATA wsaData;
         interr;
         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) );
         charrecvBuf[100];
         charsendBuf[100];
         chartempBuf[200];

         SOCKADDR_IN addrClient;
         int len= sizeof(SOCKADDR);

         while(1)
         {
                   recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
                   if('q' == recvBuf[0])
                   {
                            sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClient,len);
                            printf("chat end");
                   }
                   sprintf(tempBuf,"%s say : %s",inet_ntoa(addrClient.sin_addr),recvBuf);
                   printf("%s\n",tempBuf);

                   printf("please input data:\n");
                   gets(sendBuf);
                   sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClient,len);
         }
         closesocket(sockSrv);
         WSACleanup();
}
//client
#include <WinSock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")

void main()
{
         WORD wVersionRequested;
         WSADATA wsaData;
         interr;

         wVersionRequested = MAKEWORD(1,1);
         err =WSAStartup(wVersionRequested,&wsaData);
         if(err!= 0)
         {
                   printf("WSAStartup failed\n");
                   return;
         }
         if(LOBYTE(wsaData.wVersion)!= 1 || HIBYTE(wsaData.wVersion) != 1)
         {
                   WSACleanup();
                   return;
         }

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

         SOCKADDR_IN addrServer;
         addrServer.sin_addr.S_un.S_addr =inet_addr("127.0.0.1");
         addrServer.sin_family = AF_INET;
         addrServer.sin_port = htons(6000);

         charrecvBuf[100];
         charsendBuf[100];
         chartempBuf[200];
         int len= sizeof(SOCKADDR);

         while(1)
         {
                   printf("please input data\n");
                   gets(sendBuf);
                   sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrServer,len);
                   recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrServer,&len);
                   if('q' == recvBuf[0])
                   {
                            sendto(sockClient,"q",strlen("q")+1,0,(SOCKADDR*)&addrServer,len);
                            printf("chat end\n");
                            break;
                   }
                   sprintf(tempBuf,"%s say :%s",inet_ntoa(addrServer.sin_addr),recvBuf);
                   printf("%s\n",tempBuf);

         }
         closesocket(sockClient);
         WSACleanup();
}
时间: 2024-10-10 00:25:41

Winsock基础编程的相关文章

计算概论(A)/基础编程练习2(8题)/7:整数的个数

1 #include<stdio.h> 2 int main() { 3 int k,temp,n[3] = {0}; 4 5 // 输入k个正整数 6 scanf("%d",&k); 7 8 // 循环读入和进行算术 9 while(scanf("%d",&temp)!=EOF) { 10 switch(temp) { 11 case 1: 12 n[0]++; 13 break; 14 case 5: 15 n[1]++; 16 br

计算概论(A)/基础编程练习2(8题)/5:点和正方形的关系

1 #include<stdio.h> 2 #include<math.h> 3 int main() { 4 // 输入坐标 5 float x, y; 6 while(scanf("%f %f", &x, &y) != EOF) { 7 // 计算坐标点与原点的欧氏距离 8 float dist=sqrt(x*x+y*y); 9 10 /* 11 // 简单判断横坐标和纵坐标的截距abs(x).abs(y)和与原点距离dist 注意:abs(

关于shell脚本基础编程第四篇

shell脚本基础编程第四篇本章主要内容:函数 函数 function:             function 名称 { 命令 ; } 或 name () { 命令 ; }           定义 shell 函数.               创建一个以 NAME 为名的 shell 函数.当作为一个简单的命令启用时,           NAME 函数执行调用 shell 的上下文中的 COMMANDs 命令.当 NAME           被启用时,参数作为 $1...$n 被传递

计算概论(A)/基础编程练习2(8题)/1:求平均年龄

1 #include<stdio.h> 2 int main() { 3 // 声明与初始化 4 int n, count=1, s=0, age=0; 5 6 // 输入学生人数 7 scanf("%d", &n); 8 9 // 循环读入 加和 10 while(count<=n) { 11 scanf("%d",&age); 12 s+=age; 13 count++; 14 } 15 16 // 计算平均年龄输出 17 pr

6、50道JAVA基础编程练习题跟答案

1 50道JAVA基础编程练习题 2 [程序1] 3 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 4 程序分析: 兔子的规律为数列1,1,2,3,5,8,13,21.... 5 public class Prog1{ 6 public static void main(String[] args){ 7 int n = 10; 8 System.out.println("第"+n+

计算概论(A)/基础编程练习(数据成分)/1:短信计费

1 #include<stdio.h> 2 int main() { 3 // 输入当月发送短信的总次数n和每次短信的字数words 4 int n,words; 5 scanf("%d",&n); 6 float price=0.0; 7 8 while(scanf("%d",&words)!=EOF) { 9 // 所发送的短信超过了70个字,则会按照每70个字一条短信的限制把它分割成多条短信发送 10 price+=(words%7

计算概论(A)/基础编程练习1(8题):1:大象喝水

计算概论(A)/基础编程练习1(8题)/1:大象喝水 地址:http://pkuic.openjudge.cn/base1/1/ 1 #include<stdio.h> 2 int main() { 3 /* 圆周率常数 */ 4 const float Pi = 3.14159; 5 6 /* 深h厘米 半径r厘米 均为整数 */ 7 int h, r; 8 scanf("%d %d", &h, &r); 9 10 /* 一桶水的升数 1升 = 1000毫

.NET基础编程之特性 - Attribute

这一篇文章是给大家介绍的是:.NET基础编程之特性 - Attribute,对这一部分掌握不熟悉的同学,可以仔细的看一下! 一.特性简介 特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集.类型.方法.属性等)相关联.特性与程序实体关联后,可在运行时使用"反射"查询特性. 特性具有以下属性: (1)特性可向程序中添加元数据.元数据是有关在程序中定义的类型的信息.所有的 .NET 程序集都包含指定的一组元数据,这些元数据描述在程序集中定义的类型和类型成员.可以添加自定义特性,以

Winsock网络编程笔记(4)----基本的理论知识

前面的笔记记录了Winsock的入门编程,领略了Winsock编程的乐趣..但这并不能算是掌握了Winsock,加深理论知识的理解才会让后续学习更加得心应手..因此,这篇笔记将记录一些有关Winsock的基本理论知识,由于是一篇笔记,鉴于看书速度有限,主要内容会慢慢地填入..错误在所难免的,希望看客更正..(*^__^*) 嘻嘻…… Winsock网络编程笔记(4)----基本的理论知识,布布扣,bubuko.com