关闭

windows上Socket编程简述

标签: socket
737人阅读 评论(0) 收藏 举报
分类:

首先可以用vs新建两个控制台项目,一个作为客户端,一个作为服务器,代码分别如下:

#include <winsock2.h>   ///client
#include <mswsock.h>
#include <iostream>

using namespace std;

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "mswsock.lib")

DWORD IniSOCKDLL()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err = 0;

	wVersionRequested = MAKEWORD(2, 2);
	err = WSAStartup(wVersionRequested, &wsaData);
	return err;
}

int main(int argc, char* argv[])
{
	IniSOCKDLL();
	SOCKET sc = WSASocket(AF_INET,
		SOCK_STREAM,
		0,
		NULL,
		0,
		NULL);

	SOCKADDR_IN addr;
	int len;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	addr.sin_port = htons(1002);
	len = sizeof(addr);

	connect(sc, (struct sockaddr *)&addr, len);

	char buff[1024];
	ZeroMemory(buff, 1024);
	memcpy(buff, "123", 3);
	send(sc, buff, 3, 0);

	recv(sc, buff, 1024, 0);
	cout << buff << endl;

	closesocket(sc);
	return 0;
}
#include <winsock2.h>   ///server
#include <mswsock.h>
#include <iostream>

using namespace std;

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "mswsock.lib")

DWORD IniSOCKDLL()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err = 0;

	wVersionRequested = MAKEWORD(2, 2);
	err = WSAStartup(wVersionRequested, &wsaData);
	return err;
}

int main(int argc, char* argv[])
{
	cout << "程序开始" << endl;
	IniSOCKDLL();
	SOCKET ss = WSASocket(AF_INET,
		SOCK_STREAM,
		0,
		NULL,
		0,
		NULL);

	SOCKADDR_IN addr;
	int len;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(1002);
	len = sizeof(addr);

	bind(ss, (PSOCKADDR)&addr, len);
	listen(ss, 5);

	SOCKET sc = accept(ss, (PSOCKADDR)&addr, &len);
	char buff[1024];
	ZeroMemory(buff, 1024);
	recv(sc, buff, 1024, 0);
	cout << buff << endl;

	ZeroMemory(buff, 1024);
	memcpy(buff, "123", 3);
	send(sc, buff, 3, 0);

	closesocket(sc);
	closesocket(ss);
	return 0;
}

127.0.0.1 是回送地址,指本地机,一般用来测试使用。回送地址(127.x.x.x)是本机回送地址(Loopback Address),即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,协议软件立即返回,不进行任何网络传输。


作为服务器,你要绑定【bind】到本地的IP地址上进行监听【listen】,但是你的机器上可能有多块网卡,也就有多个IP地址,这时候你要选择绑定在哪个IP上面,如果指定为INADDR_ANY,那么系统将绑定默认的网卡【即IP地址】。

作为客户端,你要连接【connect】到远端的服务器,也是要指定远端服务器的(ip, port)对。
当然,在这种情况下,不可能将IP地址指定为INADDR_ANY,系统会疯掉的。


TCP 网络编程步骤:



对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三次握手。通常的情况,客户端的 connect() 函数默认会一直阻塞,直到三次握手成功或超时失败才返回(正常的情况,这个过程很快完成)。

listen()函数对于服务器,它是被动连接的。举一个生活中的例子,通常的情况下,移动的客服(相当于服务器)是等待着客户(相当于客户端)电话的到来。而这个过程,需要调用listen()函数。

int listen(int sockfd, int backlog);

三次握手的连接队列,这里详细的介绍一下 listen() 函数的第二个参数( backlog)的作用:告诉内核连接队列的长度。为了更好的理解 backlog 参数,我们必须认识到内核为任何一个给定的监听套接口维护两个队列:

1、未完成连接队列(incomplete connection queue),每个这样的 SYN 分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的 TCP三次握手过程。这些套接口处于 SYN_RCVD 状态。

2、已完成连接队列(completed connection queue),每个已完成 TCP 三次握手过程的客户对应其中一项。这些套接口处于 ESTABLISHED 状态。

backlog 参数历史上被定义为上面两个队列的大小之和,大多数实现默认值为 5,当服务器把这个完成连接队列的某个连接取走后,这个队列的位置又空出一个,这样来回实现动态平衡,但在高并发 web 服务器中此值显然不够。

accept()函数功能是,从处于 established 状态的连接队列头部取出一个已经完成的连接,如果这个队列没有已经完成的连接,accept()函数就会阻塞,直到取出队列中已完成的用户连接为止。



关于TCP和UDP,如果带宽足够的情况下采用UDP传输数据方便快捷,带宽不足的情况下采用TCP确保数据传输不丢失。

一、TCP与UDP的区别

 基于连接与无连接 
  对系统资源的要求(TCP较多,UDP少) 
  UDP程序结构较简单 
  流模式与数据报模式 
  TCP保证数据正确性,UDP可能丢包 
  TCP保证数据顺序,UDP不保证 
  部分满足以下几点要求时,应该采用UDP 面向数据报方式 网络数据大多为短消息 
  拥有大量Client 
  对数据安全性无特殊要求 
  网络负担非常重,但对响应速度要求高 
  具体编程时的区别 socket()的参数不同 
  UDP Server不需要调用listen和accept 
  UDP收发数据用sendto/recvfrom函数 
  TCP:地址信息在connect/accept时确定 
  UDP:在sendto/recvfrom函数中每次均 需指定地址信息 
  UDP:shutdown函数无效

二、man----socket
    通过查看socket的man手册可以看到socket函数的第一个参数的值可以为下面这些值: 
  Name Purpose 
  PF_UNIX, PF_LOCAL Local communication 
  PF_INET IPv4 Internet protocols 
  PF_INET6 IPv6 Internet protocols 
  PF_IPX IPX - Novell protocols 
  PF_NETLINK Kernel user interface device 
  PF_X25 ITU-T X.25 / ISO-8208 protocol 
  PF_AX25 Amateur radio AX.25 protocol 
  PF_ATMPVC Access to raw ATM PVCs 
  PF_APPLETALK Appletalk 
  PF_PACKET Low level packet interface

 

三、编程区别
     通常我们在说到网络编程时默认是指TCP编程,即用前面提到的socket函数创建一个socket用于TCP通讯,函数参数我们通常填为SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0),这表示建立一个socket用于流式网络通讯。 
   SOCK_STREAM这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,也是双向的,即任何一方都可以收发数据,协议本身提供了一些保障机制保证它是可靠的、有序的,即每个包按照发送的顺序到达接收方。 

  而SOCK_DGRAM这种是User Datagram Protocol协议的网络通讯,它是无连接的,不可靠的,因为通讯双方发送数据后不知道对方是否已经收到数据,是否正常收到数据。任何一方建立一个socket以后就可以用sendto发送数据,也可以用recvfrom接收数据。根本不关心对方是否存在,是否发送了数据。它的特点是通讯速度比较快。大家都知道TCP是要经过三次握手的,而UDP没有。 

  基于上述不同,UDP和TCP编程步骤也有些不同,如下: 
  TCP编程的服务器端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt(); * 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind(); 
  4、开启监听,用函数listen(); 
  5、接收客户端上来的连接,用函数accept(); 
  6、收发数据,用函数send()和recv(),或者read()和write(); 
  7、关闭网络连接; 
  8、关闭监听; 

  TCP编程的客户端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 
  4、设置要连接的对方的IP地址和端口等属性; 
  5、连接服务器,用函数connect(); 
  6、收发数据,用函数send()和recv(),或者read()和write(); 
  7、关闭网络连接;


  与之对应的UDP编程步骤要简单许多,分别如下: 
  UDP编程的服务器端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind(); 
  4、循环接收数据,用函数recvfrom(); 
  5、关闭网络连接; 

  UDP编程的客户端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 
  4、设置对方的IP地址和端口等属性; 
  5、发送数据,用函数sendto(); 
  6、关闭网络连接;


一下是UDP socket编程,可以用vs新建两个控制台项目,一个作为客户端,一个作为服务器,代码分别如下:

#include <WinSock2.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")

int main()
{
	// initial socket library
	WORD wVerisonRequested;
	WSADATA wsaData;
	int err;
	wVerisonRequested = MAKEWORD(1, 1);
	err = WSAStartup(wVerisonRequested, &wsaData);
	if (err != 0)
	{
		return -1;
	}
	// create socket
	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);

	char sendBuf[100] = "this is lisi\n";
	sendto(sockClient, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrServer, sizeof(SOCKADDR));

	int len = sizeof(addrServer);
	char recvBuf[100];
	recvfrom(sockClient, recvBuf, 100, 0, (SOCKADDR *)&addrServer, &len);
	printf("%s", recvBuf);

	closesocket(sockClient);
	WSACleanup();
	getchar();
}

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

int main()
{
	WORD wVerisonRequested;
	WSADATA wsaData;
	int err;
	wVerisonRequested = MAKEWORD(1, 1);
	err = WSAStartup(wVerisonRequested, &wsaData);
	if (err != 0)
	{
		return -1;
	}

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

	SOCKADDR_IN addrServer;
	addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrServer.sin_family = AF_INET;
	addrServer.sin_port = htons(6000);
	bind(sockServer, (SOCKADDR *)&addrServer, sizeof(addrServer));

	SOCKADDR_IN addrClient;
	int len = sizeof(addrClient);
	char recvBuf[100];
	recvfrom(sockServer, recvBuf, 100, 0, (SOCKADDR *)&addrClient, &len);
	printf("%s", recvBuf);

	char sendBuf[100] = "this is lisi'son\n";
	sendto(sockServer, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR));

	closesocket(sockServer);
	WSACleanup();
	getchar();
	return 0;
}



问题1:WSASocket()和socket()函数两者有什么区别呀?

我在学windows网络编程时,看见有两个函数都是创建一个SOCKET,这两个函数创建出来的socket有什么区别呀?

他们只是获取的参数信息不同而已吗,是不是用在不同的地方?

  1. SOCKET WSASocket(int af, int type, int protocol,  
  2.                  LPWSAPROTOCOL_INFO lpprotocolinfo,  
  3.                  GROUP g, DWORD dwflags)  
  4. SOCKET socket(int af, int type, int protocol)  
socket和UNIX兼容,等价于用默认选项调用WSASocket。
WSASocket可以使用WinSock特有功能,比如重叠IO,用dwflags指定。
问题2:WSASocket中的WSA是什么意思?
wsa的a是指api,用于区别spi,因为在spi中还有wspsocket,wspaccept等...
在spi中:
WSP(Winsock提供者):用于传送服务提供者函数。
NSP(名字空间提供者):用于名字空间提供者函数。
WPU(Winsock提供者上调):供服务提供者调用的Ws2_ 32.dll支持函数使用。
WSC(Winsock配置):供在Winsock 2中安装服务提供者的函数使用。
再说一下winsock分两部分:winsock api,winsock spi。。。

WSASocket()在Socket()上扩展了一些功能,可以让你详细的来“定制”一个socket如果只是简单的使用就没有必要使用WSASocket()
WSASocket用于非阻塞,很好用,Socket用于阻塞,也可采用多线程实现非阻塞
没多大差别,一个是socket1,一个是socket2,不过你想跨平台调用,就要用socket1的函数,因为socket2是微软专门为未来扩展搞的东西,linux/UNIX不知道


WSASocket()

简述:创建一个与指定传送服务提供者捆绑的套接口,可选地创建和/或加入一个套接口组。
 #include <winsock2.h>
SOCKET WSAAPI WSASocket ( int af, int type, int protocol, LPPROTOCOL_INFO lpProtocolInfo, Group g,int iFlags);

af:地址族描述。目前仅支持PF_INET格式,亦即ARPA Internet地址格式。
type:新套接口的类型描述。
protocol:套接口使用的特定协议,如果调用者不愿指定协议则定为0。
lpProtocolInfo:一个指向PROTOCOL_INFO结构的指针,该结构定义所创建套接口的特性。如果本参数非零,则前三个参数(af, type, protocol)被忽略。
g:套接口组的描述字。
iFlags:套接口属性描述。
 
返回值:若无错误发生,WSASocket()返回新套接口的描述字。否则的话,返回 INVALID_SOCKET,应用程序可定调用WSAGetLastError()来获取相应的错误代码。


SOCK_DGRAM和SOCK_STREAM

sock_stream 是有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料(如文件)传送。
sock_dgram 是无保障的面向消息的socket , 主要用于在网络上发广播信息。
SOCK_STREAM是基于TCP的,数据传输比较有保障。SOCK_DGRAM是基于UDP的,专门用于局域网,基于广播
SOCK_STREAM 是数据流,一般是tcp/ip协议的编程,SOCK_DGRAM分是数据抱,是udp协议网络编程
解释一下:
关于UDP
1、UDP协议适用端口分辨运行在同一台设备上的多个应用程序,UDP有不提供数据报分组、组装和不能对数据包进行排序的缺点。
也就是说,当报文发送之后,是无法得知其是否安全完整到达的。
2、在网络质量令人不十分满意的环境下,UDP协议数据包丢失会比较严重。
3、但是由于UDP的特性:它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
关于TCP
1、TCP提供端到端、全双工通信;采用字节流方式,如果字节流太长,将其分段;提供紧急数据传送功能。
2、TCP特性:
(1)面向连接的传输; (2)端到端的通信; (3)高可靠性,确保传输数据的正确性,不出现丢失或乱序; (4)全双工方式传输; (5)采用字节流方式,即以字节为单位传输字节序列; (6)紧急数据传送功能。
所以如果你需要传输的数据是准确的,建议采用TCP,也就是sock_stream
如果你传输的是视频音频等数据,丢几个包也无所谓的,可以采用UDP,也就是sock_dgram


1
0

猜你在找
【直播】机器学习&数据挖掘7周实训--韦玮
【套餐】系统集成项目管理工程师顺利通关--徐朋
【直播】3小时掌握Docker最佳实战-徐西宁
【套餐】机器学习系列套餐(算法+实战)--唐宇迪
【直播】计算机视觉原理及实战--屈教授
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之矩阵--黄博士
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之凸优化--马博士
【套餐】Javascript 设计模式实战--曾亮
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:186716次
    • 积分:3900
    • 等级:
    • 排名:第8093名
    • 原创:193篇
    • 转载:121篇
    • 译文:0篇
    • 评论:28条
    文章分类
    最新评论