windows下实现socket通讯与Linux下有些不同,windows下需要初始化socket、确认WinSock DLL支持版本;以及头文件等的使用。
下面是他们的区别之处:
头文件
windows使用的是winsock2.h头文件linux下netinet/in.h;unistd.h(close函数);sys/socket.h(常用函数:bind()、listen()等)
定义地址的不同(sockaddr_in的结构的区别)
windows下addr_var.sin_addr.S_un.S_addrlinux下addr_var.sin_addr.s_addr
初始化
windows下需要用WSAStartup启动Ws2_32.lib,并且要用#pragma comment(lib,"Ws2_32")来告知编译器链接该lib。linux下不需要
关闭套接字的方式
windows下closesocket(sock)linux下close(sock)
设置非阻塞方式
windows下ioctlsocket()函数linux下fcntl(),在fcntl.h头文件中
获取错误码
windows下getlasterror()/WSAGetLastError()linux下,未能成功执行的socket操作会返回-1;如果包含了errno.h,就会设置errno变量
读取数据的函数
windows下为recv()函数linux下为read()函数
服务端
#include <winsock2.h>
#pragma comment(lib, "ws2_32")
#define SPORT "8023"
#define SIP "127.0.0.1"
#define RECVLEN 1024 //接收的限制长度
#define MAXNUM 16 //最大连接个数
int myServer(){
WORD wVersionRequested;
WSADATA wsaData;
int serverfd = -1 ,connfd = -1;
int namelen = 0;
int ret = 0;
//客户端请求的数据字段
char clientbuf[1024];
int clientlen = 0;
//我们需要完成的二进制流的传输,需要使用unsigned char来实现,因为c里没有byte数据类型
char * pbuf = NULL;//指向客户端请求的数据的指针
//服务器返回的数据
char serverbuf[1024];
int serverlen = 0;
//本地地址、端口赋值
struct sockaddr_in sin;
int optval = 1;
/*
Winsock2.h中
#define AF_INET 0
#define PF_INET AF_INET
*/
//PROTOCL FAMILY 协议族,使用AF_INET、PF_INET都可以
sin.sin_family = PF_INET;
//htons()函数将一个16位的无符号短整型数据由主机分列体式格式转换成收集分列体式格式
printf("端口:%s\n",SPORT);
sin.sin_port = htons(atoi(SPORT));
//inet_addr()函数将网络地址转化为二进制数字
printf("地址:%s\n",SIP);
sin.sin_addr.S_un.S_addr = inet_addr(SIP);
namelen = sizeof sin;
//WinSock初始化
wVersionRequested=MAKEWORD(2, 2); //希望使用的WinSock DLL 的版本
ret=WSAStartup(wVersionRequested, &wsaData);
if(ret!=0)
{
printf("WSAStartup() failed!\n");
return -1;
}
//服务器端套接字
//SOCK_STREAM是一个有序、可靠、面向连接的双字节流。它们是在AF_INET域中通过TCP/IP连接实现的。他们也是AF_UNIX域中常见的套接字类型。
//IPPROTO_TCP 和 IPPROTO_IP代表两种不同的协议,分别代表IP协议族里面的TCP协议和IP协议
serverfd = socket(sin.sin_family,SOCK_STREAM,IPPROTO_TCP);
if(serverfd == INVALID_SOCKET){
printf("创建server的socket套接字失败serverfd:%d\n",serverfd);
return -1;
}
//绑定本地地址、端口
//sin:是一个指向包含有本机ip地址和端口号等信息的sockaddr类型的指针
//(const struct sockaddr*)&sin:由于结构体:sockaddr和sockaddr_in长度可以进行转化,指向某个结构的指针可以指向另一个。
ret = bind(serverfd,(const struct sockaddr*)&sin,sizeof(sin));
if(ret == SOCKET_ERROR){
printf("绑定本地地址、端口失败\n");
closesocket(serverfd); //关闭套接字
WSACleanup();
return -2;
}
//监听客户端的连接
//不做特殊处理此时阻塞等待客户端的请求连接
ret = listen(serverfd,MAXNUM);
if(ret == SOCKET_ERROR){
printf("监听客户端的连接失败\n");
closesocket(serverfd); //关闭套接字
WSACleanup();
return -3;
}
//取消掉阻塞等待客户端连接,我们可以这样处理。
//ioctlsocket(serverfd,FIONBIO,&optval);
printf("正在等待客户端的连接\n");
//循环监听客户端的请求
while(1){
//分配新的connfd描述符和客户端通讯
connfd = accept(serverfd,(struct sockaddr*)&sin,&namelen);
if(connfd == INVALID_SOCKET ){
printf("和客户端通讯失败\n");
closesocket(serverfd); //关闭套接字
WSACleanup();
return -4;
}
//循环接收客户端请求的数据
memset(clientbuf,0x00,sizeof(clientbuf));
clientlen = sizeof(clientbuf);
pbuf = (char*)&clientbuf;
while(clientlen > 0 ){
//ret为客户端请求数据的长度
ret = recv(connfd,pbuf,RECVLEN,0);
if(ret == SOCKET_ERROR){
printf("读取客户端的请求数据失败\n");
closesocket(serverfd); //关闭套接字
WSACleanup();
return -1;
}
//客户端已经关闭连接{
if (ret == 0){
printf("客户端已经没有请求数据了\n");
break;
}
clientlen -= ret;
pbuf += ret;
}//while(clientlen > 0 ){循环接收客户端请求的数据
printf("客户端请求信息:%s\n",clientbuf);
//服务端返回的内容
strcpy(serverbuf,"我已收到您的请求\n");
serverlen = sizeof(serverbuf);
ret = send(connfd,serverbuf,serverlen,0);
if(ret == SOCKET_ERROR){
printf("读取客户端的请求数据失败\n");
closesocket(serverfd); //关闭套接字
WSACleanup();
return -1;
}
}//while(1){循环监听客户端的请求
// closesocket(serverfd);
// WSACleanup();
return 0;
}
客户端
#include "StdAfx.h"
#include <winsock2.h>
#pragma comment(lib, "ws2_32")
#define SPORT "8023"
#define SIP "127.0.0.1"
#define RECVLEN 1024 //接收的限制长度
int myClient(){
WORD wVersionRequested;
WSADATA wsaData;
int clientfd = -1 ,acceptfd = -1;
int namelen = 0;
int ret = 0;
//客户端请求的数据字段
char clientbuf[1024];
int clientlen = 0;
//服务器返回的数据
char serverbuf[1024];
int serverlen = 0;
//本地地址、端口赋值
struct sockaddr_in sin;
int optval = 1;
char * pbuf = NULL;//指向服务端端返回的数据的指针
sin.sin_family=PF_INET;
sin.sin_port = htons(atoi(SPORT));
sin.sin_addr.S_un.S_addr= inet_addr(SIP);
//WinSock初始化
wVersionRequested = MAKEWORD(2, 2); //希望使用的WinSock DLL的版本
ret = WSAStartup(wVersionRequested, &wsaData);
if(ret!=0)
{
printf("WSAStartup() failed!\n");
return -1;
}
//确认WinSock DLL支持版本2.2
if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
printf("Invalid WinSock version!\n");
return -1;
}
//创建客户端套接字
clientfd = socket(sin.sin_family,SOCK_STREAM,IPPROTO_TCP);
if(clientfd == INVALID_SOCKET){
printf("创建客户端套接字失败\n");
return -1;
}
//向服务器发起请求
namelen = sizeof(sin);
ret = connect(clientfd,(const struct sockaddr*)&sin,namelen);
//此时会阻塞服务器返回的应答,当有服务器返回时才真正连接。
if(ret== SOCKET_ERROR){
closesocket(clientfd); //关闭套接字
WSACleanup();
printf("连接服务器失败\n");
return -2;
}
memset(clientbuf,0x00,sizeof(clientbuf));
//客户端输入
printf("请输入您的请求:\n");
scanf("%s",clientbuf);
//strcpy(clientbuf,"客户端请求。。");
clientlen = sizeof(clientbuf);
ret = send(clientfd,clientbuf,clientlen,0);
if(ret == SOCKET_ERROR){
closesocket(clientfd); //关闭套接字
WSACleanup();
printf("客户端请求数据发送失败\n");
return -3;
}
//循环接收服务端的请求数据
memset(serverbuf,0x00,sizeof(serverbuf));
serverlen = sizeof(serverbuf);
pbuf = (char*)&serverbuf;
while(serverlen > 0 ){
//ret为客户端请求数据的长度
ret = recv(clientfd,pbuf,RECVLEN,0);
if(ret == SOCKET_ERROR){
printf("读取服务器的请求数据失败\n");
closesocket(clientfd); //关闭套接字
WSACleanup();
return -1;
}
if (ret == 0){
printf("服务端已经没有返回数据了\n");
break;
}
serverlen -= ret;
pbuf += ret;
}//while(clientlen > 0 ){循环接收客户端请求的数据
printf("服务端返回的信息:%s\n",serverbuf);
closesocket(clientfd); //关闭套接字
return 0;
}