Windows Socket编程

5 篇文章 0 订阅
2 篇文章 0 订阅

Windows Socket编程总结

听会、看会、甚至把代码引用过来,不一定是真正的会了。

前几天去一家公司面试,有上机操作的题目,要求是这样的:开两个线程,一个是服务器线程一个是客户端线程,要求客户端给服务器发送一个“你好,服务器!”,服务器收到后给客户端回复“你好,客户端!”,然后客户端和服务器同时关闭。

由于最近一直在做一个屏幕监控的软件,socket很常用,但是大部分socket通信代码都是直接“引用过来的”,直接配置一下IP和端口,甚至,后来还把IP 和端口放到了.ini文件中。所以自己写出来的socket代码千疮百孔,惨不忍睹。。。回来后痛定思痛,认真的总结了以下socket通信的步骤:(以TCP为例)

TCP的步骤很规范:(总体思路是这样,可能和一些教科书有出入)

服务器端:

1 创建套接字 socket

2 定义地址信息(IP /PORT) SOCKADDR_IN

3 绑定 bind

4 监听 listen

5 接收连接 accept

6收发数据send/recv

客户端:

1 创建套接字

2 定义地址(这里是服务器地址) SOCKADDR_IN

3 连接 connect

4 收发数据 send/recv

一、初始化工作

MSDN中原话如下:

The WSAStartupfunction initiates use of Ws2_32.dll by a process。

意思就是你如果想用socket那套函数,得先做一些初始化:

1 引入头文件: #include <Winsock2.h>

2 在工程中加入socket的动态库文件:在Project->Settings->Link在 【Object/library modules:】一项中加入Ws2_32.lib (至于动态库为什么是 .lib结尾的,这里不做赘述,可以看看动态库那部分知识),然后点击OK。

3 WSAStartup代码参考 MSDN 中 WSAStartup下面的实例代码,直接粘过来就OK。

二、服务器端编程

做完了初始化,现在到服务器端的编写了

1 创建套接字

SOCKET sockServer;
sockServer=socket(AF_INET,SOCK_STREAM,0);
//第一个参数定义了Address family specification,windows下只有AF_INET
//第二个参数是选择UDP(SOCK_DGRAM) 、 TCP(SOCK_STREAM) 方式
//第三个设置成0就可以了
2 定义地址信息

// sockaddr_in的结构如下
struct sockaddr_in{
 short sin_family; //地址族,对于IP地址,必须是AF_INET
 unsigned short sin_port;//端口
 IN_ADDR sin_addr;//ip地址
 char sin_zero[8];//只是一个填充数
};
//实际的代码:	
SOCKADDR_IN addr={0};
addr.sin_family=AF_INET;
addr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//把IP地址转换成网络字节序,。
addr.sin_port=htons(8899);//端口 需大于 1024

此处注意:

a) 这里用SOCKADDR_IN 代表 SOCKADDR是因为 SOCKADDR是为所有的地址家族准备的,这个结构可能(通常会)随着所使用的网络协议不同而不同。在TCP/IP协议中,用SOCKADDR_IN结构来替换sockaddr,以方便填写地址信息。

b)INADDR_ANY允许套接字向任何分配给本地机器的IP地址发送或者接收数据。例如多个网卡的机器,每个网卡都有自己的IP,用INADDR_ANY允许一个独立应用接收发自多个接口的回应

c) htonl是把一个unsigned long的数转换为一个网络字节序

3 绑定

bind(sockServer,(SOCKADDR*)&addr,sizeof(SOCKADDR))
//把 sockServer绑定到addr的地址,由于网络协议的不确定性,最后一个参数是addr地址的大小
4 监听

listen(sockServer,5)
//监听连接请求,第一个是连接套接字,第二个是等待连接队列的最大长度,注意此处不是在一个端口最大连接数
5 接收连接

SOCKADDR_IN addrClient={0};
int len=sizeof(SOCKADDR);
char* buffer=new char[50];
SOCKET sockClient=accept(sockServer,(SOCKADDR*)&addrClient,&len);
// 第一个参数是服务器套接字,第二个是接收客户端的地址,第三个是地址的长度
//accept的返回值是接收的客户端的socket
//本机测试正常情况下,如果之前绑定和监听没有问题的话,accept是阻塞的
6 收发数据

//前边几部都没有错误的话,服务器就可以收发数据了,此处是客户端先发送数据,服务器再回复:
// recv对应的是已经建立连接的套接字
if (SOCKET_ERROR==recv(sockClient,buffer,50,0))
	{
		AfxMessageBox("接收数据失败");
		return FALSE;
	}
	CString str;
	str.Format("%s",buffer);
	AfxMessageBox(str);
	memset(buffer,0,50);

	strcpy(buffer,"我是服务器,客户端,我收到了!");
	//此处发送服务器信息到客户端,收发都是以已经建立连接对应的套接字
	send(sockClient,buffer,strlen(buffer)+1,0);	

	//释放资源
	delete[] buffer;
	closesocket(sockServer);//关闭套接字
	WSACleanup();
三、客户端编程

客户端代码跟服务器代码很类似

首先初始化资源。。。
其次是socket编程:

1 创建套接字
SOCKET sockClient;
sockClient =socket(AF_INET,SOCK_STREAM,0);
2 定义IP和端口资源

//1 定义资源
SOCKADDR_IN addr={0};//
addr.sin_family=AF_INET;//Address family; must be AF_INET.
addr.sin_addr.S_un.S_addr=inet_addr(“127.0.0.1”);
addr.sin_port=htons(8899);
3 连接

int ret=connect(sockClient,(SOCKADDR*)&addr,sizeof(SOCKADDR));
if (ret==SOCKET_ERROR)
{
	return FALSE;
}
//针对对服务器端的IP和端口进行连接
4 收发数据

char* buffer=new char[50];
strcpy(buffer,"我是客户端,服务器你收到了吗");
send(sockClient,buffer,strlen(buffer)+1,0);
recv(sockClient,buffer,50,0);
AfxMessageBox(CString(buffer));
//由于客户地址不需要进行绑定,所以直接用已建立连接的SOCKET进行数据的收发
//最后释放资源。。。
delete[] buffer;
closesocket(sockClient);
WSACleanup();
多线程部分略。。。详见代码

四、UDP

UDP服务器端通信

1 定义套接字 socket

2 初始化IP/端口资源 SOCKADDR_IN

3 绑定 bind

4 收发数据 recvfrom/ sendto

UDP客户端通信

1 定义套接字 socket

2 指定服务器IP/端口资源 SOCKADDR_IN

3 收发数据 recvfrom/ sendto

代码略。。。 源码发信li459461891@hotmail.com
五、总结
 

1 一定按照指定网络字节序初始化IP和PORT

几个有用的函数(详细介绍见MSDN)

       inet_addr-把点分十进制的IP地址转化为in_addr结构体的地址;

       inet_ntoa-把in_addr格式的IP地址转换成一个点分十进制IP地址;

       htons       -把一个u_short类型的主机序列数转化成一个网络字节序的u_short;

       htonl       -把一个u_long类型的主机序列数转换成一个网络字节序的u_long

2 注意服务器端,不要用绑定的socket收发数据,而是用建立连接的socket收发数据

3 UDP连接方式中,sendto的第五个参数const struct sockaddr *to,一定搞清楚地址。

 

socket编程看似简单,主要思想也就那么几步,但是很容易出问题。我面试时候的问题在于:没有按照指定的网络字节序初始化IP和PORT,多么痛的领悟!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值