`
abs200311235
  • 浏览: 122530 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Windows Socket编程

阅读更多
Windows Socket编程
一、基于TCP(面向连接)的socket编程
服务器端程序:
1、创建套接字(socket)。
2、将套接字绑定到一个本地地址和端口上(bind)。
3、将套接字设为监听模式,准备接受客户请求(listen)。
4、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。
5、用返回的套接字和客户端进行通信(send/recv)。
6、返回,等待另一客户请求。
7、关闭套接字。
客户端程序:
1、创建套接字(socket)。
2、向服务器发出连接请求(connect)。
3、和服务器端进行通信(send/recv)。
4、关闭套接字。

二、基于UDP(面向无连接)的socket编程
服务器端(接受端)程序:
1、创建套接字(socket)。
2、将套接字绑定到一个本地地址和端口上(bind)。
3、等待接收数据(recvfrom)。
4、关闭套接字。
客户端(发送端)程序:
1、创建套接字(socket)。
2、向服务器发送数据(sendto)。
3、关闭套接字。
其实对于建立连接后,可以相互发送信息,无所谓谁是服务器,谁是客户端。在开始的时候,一定是bind的一段,接收数据,这是死的。对于服务器和客户是在开始的时候划分的。


三、建立MFC的控制台应用程序,编写基于TCP/IP的服务器端应用。

1、int WSAStartup(
  WORD wVersionRequested, 
  LPWSADATA lpWSAData 
);
wVersionRequested的高位字节说明了socket的副版本号,低位字节说明了socket的主版本号。
LPWSADATA lpWSAData  指向WSADATA数据结构的指针,其中包含多个字段。
WSAStartup是socket应用程序调用的第一个函数,用于加载WindowsSockets版本号和WindowsSockets执行的细节参数。
具体代码如下:
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )return;

2、创建socket套接字
SOCKET socket(
  int af,      
  int type,    
  int protocol 
);
socket函数产生一个socket描述符和相关资源,和特定的协议联系。
af表示协议族,如果是TCP/IP or UDP协议,af等于AF_INET
type表示使用什么类型的socket,有三种SOCK_STREAM,SOCK_DGRAM,SOCK_RAW
如果是编写基于tcp的应用,使用SOCK_STREAM
如果编写基于udp的应用,使用SOCK_DGRAM.
protocol默认设为0。具体代码:
SOCKET SockSrv=socket(AF_INET,SOCK_STREAM,0);
………127.0.0.1本地回路地址,在本机器上发消息……………………

3、在服务器端将socket和地址、端口绑定。
int bind(
  SOCKET s,                         
  const struct sockaddr FAR *name,  
  int namelen                       
);
bind函数把产生的socket和指定的地址,端口联系起来,在服务器端使用。其中sockaddr(or SOCKADDR)用于存储ip地址结构(服务器的地址和端口),具体定义为:
struct sockaddr {
    unsigned short sa_family;
    char           sa_data[14];
};
sa_family表示使用的socket地址族,sa_data[14]定义了socket地址结构的最大存储单元。为了更详细的指定socket地址,可以使用SOCKADDR_IN 数据结构,它细分了TCP/IP Sockets 的地址字段,在程序中可以分别给每位字段赋预定的值,最后在bind时,进行SOCKADDR的强制转换,因为SOCKADDR和SOCKADDR_IN结构大小相等。
struct sockaddr_in{
    short            sin_family;
    unsigned short      sin_port;
    struct   in_addr      sin_addr;
    char               sin_zero[8];
};
struct   in_addr {
    union   {
         struct{
             unsigned  char   s_b1,
                              s_b2,
                              s_b3,
                              s_b4;
        }  S_un_b;
             struct  {
             unsigned  short  s_w1,
                              s_w2;
              }  S_un_w;
               unsigned long  S_addr;
     } S_un;
};
在sockaddr中除sin_family外,其他都是网络字节顺序表示,因此需要显示转换数据的字节顺序,把主机上的字节顺序变换为网络的字节顺序。其中u_short htons(
  u_short hostshort 
);
是转换无符号短整型数。用于端口号的转换。
u_long htonl(
  u_long hostlong 
);
是转换无符号长整型数。用于S_addr的转换。
namelen表示地址结构的长度。sizeof(sockaddr)即可得到。
具体代码如下:
SOCKADDR_IN addrSRV;
addrSRV.sin_family=AF_INET;
addrSRV.sin_addr.S_un.S_addr=ADDR_ANY;
addrSRV.sin_port=htons(6000);
bind(SockSrv,(SOCKADDR*)&addrSRV,sizeof(SOCKADDR));
ADDR_ANY表示不详细指定具体的网卡ip地址,它也不需要字节顺序转换,因为值为0,对于非零值则一定需要转换。

4、设置socket为监听模式
int listen(
  SOCKET s,   
  int backlog 
);
s表示待设定的socket,backlog表示队列的最大等待数。
具体代码如下:
listen(SockSrv,10);

5、开始接受来自客户端的请求
SOCKET accept(
  SOCKET s,
  struct sockaddr FAR *addr, 
  int FAR *addrlen 
);
s是处于监听状态的socket,*addr是请求连接的客户端的地址信息,*addrlen 是客户端地址结构的长度。返回一个socket,然后可以使用返回的socket和客户端通信。
具体代码如下:
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
SOCKET SockConn=accept(SockSrv,(SOCKADDR*)&addrClient,&len);
这里的len必须进行初始化,否则会产生错误。

6、发送信息给客户端
int send(
  SOCKET s,             
  const char FAR *buf, 
  int len,              
  int flags             
);
s是应答上客户端的socket,即使用accept返回的socket。
*buf保存要传送给客户端的信息,是一个字符指针。
len保存字符数组的长度。
flags指定函数调用的方式。默认使用0。
具体代码如下:
send(SockConn,"Welcome to www.sun.com!",sizeof("Welcome to www.sun.com!")+1,0);

7、接收来自客户端的信息
int recv(
  SOCKET s,      
  char FAR *buf, 
  int len,       
  int flags      
);
s是和客户端通信的socket
*buf存放接收的信息
len存放buf的大小
flags指定函数的调用方式,默认为0。
具体代码如下:
char recvBuf[100];
recv(SockConn,recvBuf,100,0);
printf("%s\n",recvBuf);

8、通信完毕后要关闭socket
int closesocket(SOCKET s  );
具体代码如下:
closesocket(SockConn);

还要在程序的最后关闭WSA
int  WSACleanup (void);
所以int WSAStartup(
  WORD wVersionRequested, 
  LPWSADATA lpWSAData 
);
和int  WSACleanup (void);成对出现。
对于以上的函数,要求包含Winsock2.h头文件,同时连接时使用Ws2_32.lib库(在工程->设置--->连接  中加入库文件);否则,编译运行会产生错误。


9、总结:
      首先要了解socket编程的原理,在原理的指导下,每一个通信的步骤都对应Windows Socket的一个函数,调用函数完成预定的通信。

四、编写基于TCP/IP的客户端应用程序。
1、加载Windows Socket版本
int WSAStartup(
  WORD wVersionRequested, 
  LPWSADATA lpWSAData 
);
typedef struct WSAData {
  WORD                  wVersion;
  WORD                  wHighVersion;
  char                  szDescription[WSADESCRIPTION_LEN+1];
  char                  szSystemStatus[WSASYS_STATUS_LEN+1];
  unsigned short        iMaxSockets;
  unsigned short        iMaxUdpDg;
  char FAR *            lpVendorInfo;
} WSADATA, *LPWSADATA;

成功加载返回0。wVersionRequested的高位字节说明了socket的副版本号,低位字节说明了socket的主版本号。
2、创建socket套接字
SOCKET socket(
  int af,      
  int type,    
  int protocol 
);
SOCKET SockClient=socket(AF_INET,SOCK_STREAM,0);

3、使用socket套接字发送建立请求
int connect(
  SOCKET s,                         
  const struct sockaddr FAR *name, 
  int namelen                       
);
其中s是客户端的套接字,*name存储要连接到的服务器端的地址信息,在此函数之前要详细说明各字段的值。
具体代码如下:
SOCKADDR_IN SockSrv;
SockSrv.sin_family=AF_INET;
SockSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
SockSrv.sin_port=htons(6000); connect(SockClient,(SOCKADDR*)&SockSrv,sizeof(SOCKADDR));
inet_addr()是将ip地址字符串转换成适合IN_ADDR 结构的ip地址。

4、接收来自服务器端的信息。
具体代码如下:
char recvBuf[100];
recv(SockClient,recvBuf,100,0);
printf("%s\n",recvBuf);

5、发送信息给服务器。
send(SockClient,"I am chenlei!",sizeof("I am chenlei!")+1,0);
6、可以继续发送和接收。
7、通信完毕关闭socket
int closesocket(SOCKET s  );
具体代码如下:
closesocket(SockClient);
还有相关操作和服务器端的一样。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/chenlei5662/archive/2008/08/20/2801228.aspx
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics