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

套接字地址(sockaddr、sockaddr_in)

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

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

 /*
* Socket address, internet style.
*/
struct sockaddr_in {
short sin_family;/* internet address family */
u_short sin_port;/* port number */
struct in_addr sin_addr;/* internet address */
char sin_zero[];/* padding bits */
};

这个结构提供了方便的手段来访问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()函数打开一个文件。

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

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

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

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

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

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

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

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

成功返回0,失败返回SOCKET_ERROR

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

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

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

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

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

客户端主动连接

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

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

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

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

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

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

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

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

获取hostname

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

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

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

 struct hostent* gethostbyname(const char* name);

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

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

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

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

I/O通信

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

发送数据

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

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

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

接收数据

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

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

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

关闭套接字(TCP连接)

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

WinSock TCP C/S通信示例

简单通信示例:

客户端代码:

 #include <winsock2.h>
#include <stdio.h> 
#define SERVPORT 5050 // 端口为5150
#define MAXDATASIZE 100
#define SERVIP “127.0.0.1” // 服务器IP地址为“127.0.0.1”,
#pragma comment(lib,"ws2_32.lib")
 
void main(int argc, char *argv[])
{
WSADATA wsaData;
SOCKET sConnect;
SOCKADDR_IN serverAddr;
int recvbytes;
char buf[MAXDATASIZE];
WSAStartup(MAKEWORD(,), &wsaData);
sConnect = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(SERVPORT);
serverAddr.sin_addr.s_addr = inet_addr(SERVIP);
memset(&(serverAddr.sin_zero), , sizeof(serverAddr.sin_zero));
if(
connect(sConnect, (SOCKADDR*)&serverAddr,sizeof(SOCKADDR))==SOCKET_ERROR)
{
printf("connect failed!\n");
return;
}
recvbytes = recv(sConnect, buf, MAXDATASIZE, );
if (recvbytes == SOCKET_ERROR){ printf("recv failed!\n");}
else{buf[recvbytes] = '\0';printf("%s\n",buf); } 
closesocket(sConnect);
WSACleanup();
}

服务端代码:

 #include <winsock2.h>
#include <stdio.h> 
#define SERVPORT 5050
#pragma comment(lib,"ws2_32.lib") 
void main(void)
{
WSADATA wsaData;
SOCKET sListen; // 监听socket
SOCKET sClient; // 连接socket
SOCKADDR_IN serverAddr; // 本机地址信息
SOCKADDR_IN clientAddr; // 客户端地址信息
int clientAddrLen; // 地址结构的长度
int nResult;
WSAStartup(MAKEWORD(,), &wsaData);
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(SERVPORT);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
memset(&(serverAddr.sin_zero), , sizeof(serverAddr.sin_zero));
nResult = bind(sListen, (SOCKADDR *)&serverAddr, sizeof(SOCKADDR));
if (nResult == SOCKET_ERROR)
{
printf("bind failed!\n"); 
return;

listen(sListen, );  
while()
{
clientAddrLen = sizeof (SOCKADDR);
sClient = accept(sListen, (SOCKADDR *)&clientAddr, &clientAddrLen);
if(sClient == INVALID_SOCKET){printf("Accept failed!"); }
else{
printf("Accepted client: %s : %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
nResult = send(sClient, "Connect success!", , );
if (nResult == SOCKET_ERROR){printf("send failed!");}
}
closesocket(sClient);
}
closesocket(sListen); 
WSACleanup();
}

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

  1. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  2. Linux Shell编程学习笔记——目录(附笔记资源下载)

    LinuxShell编程学习笔记目录附笔记资源下载 目录(?)[-] 写在前面 第一部分 Shell基础编程 第二部分 Linux Shell高级编程技巧 资源下载 写在前面 最近花了些时间学习She ...

  3. DirectX 11游戏编程学习笔记之8: 第6章Drawing in Direct3D(在Direct3D中绘制)(习题解答)

            本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com         注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...

  4. 多线程编程学习笔记——async和await(一)

    接上文 多线程编程学习笔记——任务并行库(一) 接上文 多线程编程学习笔记——任务并行库(二) 接上文 多线程编程学习笔记——任务并行库(三) 接上文 多线程编程学习笔记——任务并行库(四) 通过前面 ...

  5. 多线程编程学习笔记——async和await(二)

    接上文 多线程编程学习笔记——async和await(一) 三.   对连续的异步任务使用await操作符 本示例学习如何阅读有多个await方法方法时,程序的实际流程是怎么样的,理解await的异步 ...

  6. 多线程编程学习笔记——async和await(三)

    接上文 多线程编程学习笔记——async和await(一) 接上文 多线程编程学习笔记——async和await(二) 五.   处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多 ...

  7. 多线程编程学习笔记——使用异步IO(一)

    接上文 多线程编程学习笔记——使用并发集合(一) 接上文 多线程编程学习笔记——使用并发集合(二) 接上文 多线程编程学习笔记——使用并发集合(三) 假设以下场景,如果在客户端运行程序,最的事情之一是 ...

  8. 多线程编程学习笔记——编写一个异步的HTTP服务器和客户端

    接上文 多线程编程学习笔记——使用异步IO 二.   编写一个异步的HTTP服务器和客户端 本节展示了如何编写一个简单的异步HTTP服务器. 1.程序代码如下. using System; using ...

  9. 多线程编程学习笔记——异步调用WCF服务

    接上文 多线程编程学习笔记——使用异步IO 接上文 多线程编程学习笔记——编写一个异步的HTTP服务器和客户端 接上文 多线程编程学习笔记——异步操作数据库 本示例描述了如何创建一个WCF服务,并宿主 ...

随机推荐

  1. MySQL 线程池&连接池&长连接&短连接

    线程池 简介 1.mysql每连接每线程,mysql都分配一个单独的线程,该线程处理客户端发来的所有命令 2.每个线程会占用一定的系统资源,线程数越多消耗的系统资源也越多 3.线程的创建和销毁有一定的 ...

  2. ASP.NET Core中自定义路由约束

    路由约束 ASP.NET Core中,通过定义路由模板,可以在Url上传递变量,同时可以针对变量提供默认值.可选和约束. 约束的使用方法是在属性路由上添加指定的约束名,用法如下: // 单个使用 [R ...

  3. 人生苦短之---第一个Python程序

    第一个 Python 程序 目标 第一个 HelloPython 程序 Python 2.x 与 3​​.x 版本简介 执行 Python 程序的三种方式 解释器 —— python / python ...

  4. request和response简介

    Tomcat收到客户端的http请求,会针对每一次请求,分别创建一个代表请求的request对象.和代表响应的response对象. 既然request对象代表http请求,那么我们获取浏览器提交过来 ...

  5. IDEA内置git功能的使用教程

    IDEA内置git功能的使用教程 IDEA git  IDEA被公认为是最好的java开发工具,除了在代码助手.代码提示.重构工具等方面有比较好的支持,还在各类版本控制工具(git.tfs.svn.g ...

  6. es 基于match_phrase的模糊匹配原理及使用

    [版权声明]:本文章由danvid发布于http://danvid.cnblogs.com/,如需转载或部分使用请注明出处 在业务中经常会遇到类似数据库的"like"的模糊匹配需求 ...

  7. oracle中常用的对用户的操作

    创建用户:create user usr1 identified by pwd default tablespace ts1 temporary tablespace tmp; 删除用户:drop u ...

  8. C# 线程中使用delegate对控件进行操作

    如果在线程中想改变控件的值是不可以的,会报出以下错误. 那么,如何在线程中改变控件上的值呢?第一个想到的就是委托. 委托定义:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行 ...

  9. 3.spring环境搭建

    1. 导入jar     1.1 四个核心包一个日志包(commons-logging)

  10. C51 玄学问题,magic

    0x00 问题代码 void int0_isr(void) interrupt 0 { num++; if (num%2 == 1) { uint k; for(k=0;k<3;k++) { P ...