winsock编程学习笔记

以下部分转自博客http://blog.csdn.net/phunxm/article/details/5085869

套接字地址(sockaddr、sockaddr_in)

1 /*
2  * Structure used by kernel to store most addresses.
3  */
4 struct sockaddr {
5        u_short sa_family;/* address family,AF_X */
6        char    sa_data[14];/* up to 14 bytes of direct address */
7 };

包含了一些远程电脑的地址、端口和套接字的数目,它里面的数据是混杂在一起的。sa_data域的定义有些不确定性,注释暗示内容可能超过14个字节。这种不确定性是经过深思熟虑的。套接字是个非常强大的接口。多数人可能认为比Internet接口强不到哪里——大多数应用现在很可能都用它——套接字可被用于几乎任何种类的进程间通信,Internet(更精确的说是IP)只是其支持的协议簇中的一种。

1 /*
2  * Socket address, internet style.
3  */
4 struct sockaddr_in {
5        short   sin_family;/* internet address family */
6        u_short sin_port;/* port number */
7        struct  in_addr sin_addr;/* internet address */
8        char    sin_zero[8];/* padding bits */
9 };

这个结构提供了方便的手段来访问socket address(struct sockaddr)结构中的每一个元素。注意sin_zero[8]是为了使sockaddr和sockaddr_in结构具有相同的尺寸,使用sockaddr_in的时候要把sin_zero全部设为零(使用memset函数)。

Winsock库的加载和卸载

要使用Windows Socket API进行编程,首先必须调用WSAStartup()函数初始化Winsock动态库。

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

  • 参数一wVersionRequested:为我们要求初始化的Winsock版本号
  • 参数二lpWSAData:为实际初始化成功的WSA(Windows Socket API)版本信息。

套接字的创建和释放

要使用套接字,首先必须调用socket()函数创建一个套接字描述符,就如同操作文件时,首先得调用fopen()函数打开一个文件。

1 // The socket function creates a socket that is bound to a specific service provider.
2 SOCKET socket(int af,// [in] Address family specification.
3            int type,// [in] Type specification for the new socket.
4            int protocol// [in] Protocol to be used with the socket that is specific to the indicated address family.
5            );
1 // The closesocket function closes an existing socket.
2 int closesocket(
3     SOCKET s// [in] Descriptor identifying the socket to close.
4 );

af:通信协议的协议族 ,AF_INET(IPv4)或 AF_INET6(IPv6)

type:指定要创建的套接字的类型。SOCK_STREAM, SOCK_ DGRAM,SOCK_RAW

protocol指定应用程序所使用的通信协议 :

IPPROTO_TCP, IPPROTO_UDP, 0(如果不想指定)

绑定套接字到指定的IP地址和端口

对于传输套接字,在执行收发数据前需要对本地端口进行绑定,这是因为传输层需要使用端口来区分具体的通信端点

1 // The bind function associates a local address with a socket.
2 int bind(
3        SOCKET s, // [in] Descriptor identifying an unbound socket.
4        const struct sockaddr FAR *name, // [in] Address to assign to the socket from the SOCKADDR structure.
5        int namelen // [in] Length of the value in the name parameter.
6        );

成功返回0,失败返回SOCKET_ERROR

TCP服务器设置套接字进入监听状态

服务器为了接受连接,首先使用socket()函数创建一个套接字,然后使用bind()函数将它绑定到一个本地地址(端口),再用listen()函数为到达的连接指定一个backlog。

1 // The listen function places a socket a state where it is listening for an incoming connection.
2 int listen(
3         SOCKET s,// [in] Descriptor identifying a bound, unconnected socket.
4         int backlog// [in] Maximum length of the queue of pending connections.
5         );

backlog参数指定了正在等待连接的最大队列长度。这个参数非常重要,因为服务器完全可能同时收到几个连接请求。假定backlog参数为2,如果三个客户机同时发出请求,那么头两个会被放在一个“待决”(等待处理)队列中,以便应用程序依次为它们提供服务。而第三个连接会造成一个WSAECONNREFUSED错误。注意,一旦服务器接受了一个连接(accept返回),那个连接请求就会从队列中删去,以便别人可继续发出请求。

该函数执行成功返回0,失败返回SOCKET_ERROR

客户端主动连接

1 // The connect function establishes a connection to a specified socket.
2 int connect(
3           SOCKET s,// [in] Descriptor identifying an unconnected socket.
4           const struct sockaddr FAR *name,// [in] Name of the socket to which the connection should be established.
5           int namelen// [in] Length of name.
6          );

客户端是连接的发起者(initiate),它通过调用connect()函数主动(active)连接服务器。参数二填写欲连接的目标服务器的地址。如果连接的计算机并没有在指定端口上监听,则connect()调用返回SOCKET_ERROR,WSAGetLastError()=WSAECONNREFUSED;另一种错误是WSAETIMEOUT,例如由于路由或网络故障,客户端迟迟接受不到服务器回馈的[SYN,ACK]信号。

TCP服务器接受客户连接请求

1 // The accept function permits an incoming connection attempt on a socket.
2 SOCKET accept(
3            SOCKET s,// [in] Descriptor identifying a socket that has been placed in a listening state with the listen function.
4            struct sockaddr FAR *addr,// [out] receives the address of the connecting entity, as known to the communications layer.
5            int FAR *addrlen// [out] the length of addr.
6            );

服务器进入listen状态后,循环调用accept()接受客户的连接。参数一为监听套接字;参数二为远端客户的地址信息;该函数返回一个套接字句柄,负责后续与该远端客户的会话通信。监听套接字总是默默无闻的在门口守望(listen),迎接(accept)客户的到来并安排服务员(会话套接字)接客。

该函数执行成功返回新的socket,失败返回SOCKET_ERROR

在一个已绑定或已连接的套接字上获取连接名和对方地址信息获取sockaddr

1 int getsockname (SOCKET s, struct sockaddr *name, int* namelen);

获取hostname

Host即通常意义上的机器名(Machine Name)或域名(Domain Name)。

1 int gethostname (char FAR *name, int namelen);

gethostname()函数可以取得调用主机的机器名。返回的这个name传给gethostbyname()调用可以取得相应IP地址。

1 struct hostent* gethostbyname(const char* name);

gethostbyname()函数主要用来做DNS解析,传入域名(例如www.baidu.com),返回hostent结构。struct hostent存放主机信息。

 1 /*
 2  * Structures returned by network data base library, taken from the
 3  * BSD file netdb.h. All addresses are supplied in host order, and
 4  * returned in network order (suitable for use in system calls).
 5  */
 6 struct hostent {
 7        char   FAR *h_name;          /* official name of host */
 8        char   FAR *FAR *h_aliases; /* alias list */
 9        short  h_addrtype;            /* host address type */
10        short  h_length;              /* length of address */
11        char   FAR *FAR *h_addr_list;/* list of addresses */
12 #defineh_addr h_addr_list[0]         /* address, for backward compat */
13 };
14 /* Microsoft Windows Extended data types */
15 typedef struct hostent HOSTENT, *PHOSTENT, *LPHOSTENT;

以下代码段获取百度(www.baidu.com)机器名和地址。

1 struct hostent *pHostBaiDu = gethostbyname("www.baidu.com");
2 printf("Host name: %s/n", pHostBaiDu->h_name);
3 printf("IP Address: %s/n", inet_ntoa(*((struct in_addr*)pHostBaiDu->h_addr)));

I/O通信

从I/O的角度来看,套接字也是文件,它提供了同文件读写(fread()/fwrite())对应的收发数据操作接口:send()/recv()。

发送数据

1 // The send function sends data on a connected socket.
2 int send(
3        SOCKET s,// [in] Descriptor identifying a connected socket.
4        const char FAR *buf,// [in] Buffer containing the data to be transmitted.
5        int len,// [in] Length of the data in buf.
6        int flags// [in] Indicator specifying the way in which the call is made.
7        );

send()函数在一个已连接的套接字s上执行数据发送操作。对于客户机而言,发送的目标地址即connect()调用时所指定的地址;对于服务器而言,发送的目标地址即accept()调用所返回的地址。发送的内容为保存在缓冲区buf中,发送的内容长度为len。最后一个参数flags,通常情况下填0。

成功返回发送字节数,出错返回SOCKET_ERROR

接收数据

1 // The recv function receives data from a connected or bound socket.
2 int recv(
3        SOCKET s,// [in] Descriptor identifying a connected socket.
4        char FAR *buf,// [out] Buffer for the incoming data.
5        int len,// [in] Length of buf.
6        int flags// [in] Flag specifying the way in which the call is made.
7        );

recv()函数在一个已连接的套接字s上执行数据接收操作。对于客户机而言,数据的源地址即connect()调用时所指定的地址;对于服务器而言,数据的源地址即accept()调用所返回的地址。接收的内容为保存至长度为len的缓冲区buf,最后一个参数flags,通常情况下填0。

成功返回接收的数据的字节数量,失败返回SOCKET_ERROR,如果接受数据时网络中断返回0。

关闭套接字(TCP连接)

1 // The shutdown function disables sends or receives on a socket.
2 int shutdown(
3            SOCKET s,// [in] Descriptor identifying a socket.
4            int how// [in] Flag that describes what types of operation will no longer be allowed.
5            );

WinSock TCP C/S通信示例

简单通信示例:

客户端代码:

 1 #include <winsock2.h>
 2 #include <stdio.h> 
 3 #define SERVPORT    5050              // 端口为5150
 4 #define MAXDATASIZE 100
 5 #define SERVIP      “127.0.0.1”      // 服务器IP地址为“127.0.0.1”,
 6 #pragma comment(lib,"ws2_32.lib")
 7  
 8 void main(int argc, char *argv[])
 9 {
10     WSADATA              wsaData;
11     SOCKET               sConnect;
12     SOCKADDR_IN          serverAddr;
13     int            recvbytes;
14     char            buf[MAXDATASIZE];
15     WSAStartup(MAKEWORD(2,2), &wsaData);
16     sConnect = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
17     serverAddr.sin_family = AF_INET;
18     serverAddr.sin_port = htons(SERVPORT);
19     serverAddr.sin_addr.s_addr = inet_addr(SERVIP);
20     memset(&(serverAddr.sin_zero), 0, sizeof(serverAddr.sin_zero));
21     if(
22     connect(sConnect,   (SOCKADDR*)&serverAddr,sizeof(SOCKADDR))==SOCKET_ERROR)
23     {
24         printf("connect failed!\n");
25         return;
26     }
27     recvbytes = recv(sConnect, buf, MAXDATASIZE, 0);
28     if (recvbytes == SOCKET_ERROR){    printf("recv failed!\n");}
29     else{buf[recvbytes] = ‘\0‘;printf("%s\n",buf);    } 
30     closesocket(sConnect);
31     WSACleanup();
32 }
33     

服务端代码:

 1 #include <winsock2.h>
 2 #include <stdio.h> 
 3 #define SERVPORT    5050
 4 #pragma comment(lib,"ws2_32.lib") 
 5 void main(void)
 6 {
 7     WSADATA              wsaData;
 8     SOCKET               sListen;        // 监听socket
 9     SOCKET               sClient;        // 连接socket
10     SOCKADDR_IN          serverAddr;        // 本机地址信息
11     SOCKADDR_IN          clientAddr;        // 客户端地址信息
12     int                    clientAddrLen;    // 地址结构的长度
13     int                  nResult;
14     WSAStartup(MAKEWORD(2,2), &wsaData);
15     sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
16     serverAddr.sin_family = AF_INET;
17     serverAddr.sin_port = htons(SERVPORT);
18     serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
19     memset(&(serverAddr.sin_zero), 0, sizeof(serverAddr.sin_zero));
20     nResult = bind(sListen, (SOCKADDR *)&serverAddr, sizeof(SOCKADDR));
21    if (nResult == SOCKET_ERROR)
22    {
23         printf("bind failed!\n"); 
24     return;
25     } 
26     listen(sListen, 5);  
27     while(1)
28    {
29         clientAddrLen = sizeof (SOCKADDR);
30         sClient = accept(sListen, (SOCKADDR *)&clientAddr, &clientAddrLen);
31         if(sClient == INVALID_SOCKET){printf("Accept failed!");    }
32         else{
33             printf("Accepted client: %s : %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
34         nResult = send(sClient, "Connect success!", 16, 0);
35         if (nResult == SOCKET_ERROR){printf("send failed!");}
36     }
37     closesocket(sClient);
38     }
39      closesocket(sListen); 
40      WSACleanup();
41 }
时间: 2024-11-13 08:03:40

winsock编程学习笔记的相关文章

Java并发编程学习笔记

Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现Runnable接口并编写run方法,使得该任务可以执行你的命令.   class MyTask implements Runnable {    private String mName;     public MyTask(String name) {    mName = name;   }  

Linux Shell脚本编程学习笔记和实战

http://www.1987.name/141.html shell基础 终端打印.算术运算.常用变量 Linux下搜索指定目录下特定字符串并高亮显示匹配关键词 从键盘或文件中获取标准输入 [read命令] 文件的描述符和重定向 数组.关联数组和别名使用 函数的定义.执行.传参和递归函数 条件测试操作与流程控制语句 获取时间日期格式和延时 [date.sleep命令] 内部字段分隔符IFS和脚本的调试DEBUG 显示.读取或拼接文件内容 [cat命令] 文件查找与打印文件列表 [find命令]

linux网络编程学习笔记之二 -----错误异常处理和各种碎碎(更新中)

errno 在unix系统中对大部分系统调用非正常返回时,通常返回值为-1,并设置全局变量errno(errno.h),如socket(), bind(), accept(), listen().erron存放一个正整数来保存上次出错的错误值. 对线程而言,每个线程都有专用的errno变量,不必考虑同步问题. strerror converts to English (Note: use strerror_r for thread safety) perror is simplified str

JAVA GUI编程学习笔记目录

1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之GUI编程窗体事件 6.JAVA之GUI编程Action事件 7.JAVA之GUI编程鼠标事件 8.JAVA之GUI编程键盘码查询器 9.JAVA之GUI编程列出指定目录内容 10.JAVA之GUI编程弹出对话框Dialog 11.JAVA之GUI编程菜单 12.JAVA之GUI编程打开与保存文件 13.JAVA之GUI编程将程序打包jar JA

黑马程序员_JAVA UDP网络编程学习笔记

一.UDP网络编程概述 采用TCP协议通信时,客户端的Socket必须先与服务器建立连接,连接建立成功后,服务器端也会持有客户端连接的Socket,客户端的Socket与服务器端的Socket是对应的,它们构成了两个端点之间的虚拟通信链路.与TCP通信不同,UDP是面向无连接的.不可靠的基于数据包的传输协议.即应用进程(或程序)在使用UDP协议之前,不必先建立连接.自然,发送数据结束时也没有连接需要释放.因此,减少了开销和发送数据之前的延时.UDP也采用端口来区分进程. 在java中,java.

Java并发编程学习笔记(一)线程安全性 1

什么是线程安全性: 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的和可变的状态的访问."共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周期内可以发生变化. 一个对象是否需要线程安全的,取决于他是否被多个线程访问.这指的是在程序中访问对象的方式,而不是对象要实现的功能.要使得对象时线程安全的,需要采用同步机制来协同对对象可变状态的访问.如果无法实现协同,那么可能导致数据破坏以及其他不该出现的结果. 如果当多个线程访

FFmpeg编程学习笔记一

FFmpeg编程学习笔记一 1.为了学习ffmpeg编程需要单步调试,参照网上的教程用VS2013编译一次成功,之后随便写了个重采样音轨小程序,也就是把一个5.1声道的AC3文件分解成6个WAV文件的简单功能. 2.编译成功执行也正常,但速度奇慢,比同类软件eac3to慢了约5倍. 3.OK上网搜搜咋回事,一天,二天过去了无果. 4.无奈用VS2013的性能与诊断,分析出最占用时间的函数调用具然是:swr_convert参数里的lrintf() lrint() llrint() llrintf(

Android Socket编程学习笔记

通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.在Internet上的主机一般运行了多个服务软件,同时提供几种服务.每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务. 网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket.Socket通常用来实现客户方和服务方的连接.Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定. 在java中,Socke

linux网络编程学习笔记之六 -----I/O多路复用服务端

多进程和多线程的目的是在于最大限度地利用CPU资源,当某个进程不需要占用太多CPU资源,而是需要I/O资源时,可以采用I/O多路复用,基本思路是让内核把进程挂起,直到有I/O事件发生时,再把控制返回给程序.这种事件驱动模型的高效之处在于,省去了进程和线程上下文切换的开销.整个程序运行在单一的进程上下文中,所有的逻辑流共享整个进程的地址空间.缺点是,编码复杂,而且随着每个逻辑流并发粒度的减小,编码复杂度会继续上升. I/O多路复用典型应用场合(摘自UNP6.1) select的模型就是这样一个实现