以下是linux套接字的用法和模式
1. socket套接字流程简介
socket套接字包含标准套接字(SOCK_STREAM,SOCK_DRAGM)以及原始套接字(SOCK_RAW),一般我们进行网络编程有标准套接字就够了,但如果要实现标准套接字(即TCP,UDP套接字)不能实现的功能,就需要用原始套接字了。这里还是主要总结一下标准套接字的用法。
如前所述,标准套接字分为TCP协议(SOCK_STREAM)和UDP协议(SOCK_DRAGM)两种type的工作流程,因为TCP是面向连接的服务,所以TCP网络编程会更复杂一点。不过不论是TCP还是UDP,其socket网络编程模式都是类似的,分为客户端和服务端。
- 客户端:在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序。
- 服务端: 和客户端相对应的程序即为服务端程序。被动的等待外面的程序来和自己通讯的程序称为服务端程序。
一般在网络应用中,获取服务的客户即是客户端,提供服务的服务器即是服务端,不过也有些程序是互为服务和客户端,这种情况下, 一个程序既是客户端也是服务端。 以下两图描述了TCP和UDP socket通信的流程:
- TCP socket流程:
- UDP socket流程:
可以看到,因为TCP是面向连接的服务,包含三次握手建立连接的过程,所以TCP的服务器模式比UDP多了listen,accept函数,TCP客户端模式比UDP多了connect函数。
2. socket标准套接字基本函数介绍
这张图将TCP数据交互的流程与socket函数作了一一对应,十分清楚,接下来就对其中的函数做一个整理介绍。
2.1 创建socket套接字
int socket(int family, int type, int protocol)
功能介绍:
在Linux操作系统中,一切皆文件,网络程序通过socket和其它几个函数的调用,会返回一个 通讯的文件描述符,我们可以将这个描述符看成普通的文件的描述符来操作,这就是linux的设备无关性的好处。。socket函数完成正确的操作是返回值大于0的文件描述符,当返回小于0的值时,操作错误。同样是返回一个文件描述符,但是会因为三个参数组合不同,对于数据具体的工作流程不同,对于应用层编程来说,这些也是不可见的。
参数说明:
- family:说明我们网络程序所在的主机采用的通讯协族(AF_INET和AF_UNIX等)。
AF_UNIX只能够用于单一的Unix 系统进程间通信,
而AF_INET是针对Internet的,因而可以允许在远程
- type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM,SOCK_RAW等)
SOCK_STREAM表明我们用的是TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流。
SOCK_DGRAM 表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信。
- protocol:具体的协议,对于标准套接字来说,其值是0,对于原始套接字来说就是具体的协议值。
2.2 地址端口绑定函数bind
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen)
功能介绍:
bind函数主要应用于服务器模式一端,其主要的功能是将addrlen长度 struct sockaddr类型的myaddr地址与sockfd文件描述符绑定到一起,在sockaddr中主要包含服务器端的协议族类型,网络地址和端口号等。在客户端模式中不需要使用bind函数。当bind函数返回0时,为正确绑定,返回-1,则为绑定失败。
参数说明:
- sockfd:是由socket调用返回的文件描述符。
- my_addr:是一个指向sockaddr的指针,由于struct sockaddr数据结构类型不方便设置,所以通常会通过对truct sockaddr_in进行数值结构设置,然后进行强制类型转换成struct sockaddr类型的数据,下面有两种类型数据结构的定义和对应关系图。
- addrlen:是sockaddr结构的长度。
typedef unsigned short sa_family_t;
struct in_addr {
__be32 s_addr;
};
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
sa_family_t sin_family;
__be16 sin_port;
struct in_addr sin_addr;
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- struct sockaddr_in和struct sockaddr的映射关系:
2.3 监听本地端口listen
int listen(int sockfd, int backlog)
功能介绍:
刚开始理解listen函数会有一个误区,就是认为其操作是在等在一个新的connect的到来,其实不是这样的,真正等待connect的是accept操作,listen的操作就是当有较多的client发起connect时,server端不能及时的处理已经建立的连接,这时就会将connect连接放在等待队列中缓存起来。这个等待队列的长度有listen中的backlog参数来设定。listen和accept函数是服务器模式特有的函数,客户端不需要这个函数。当listen运行成功时,返回0;运行失败时,返回值位-1。
参数说明:
- sockfd:是由socket调用返回的文件描述符
- backlog:server端可以缓存连接的最大个数,也就是等待队列的长度。
2.4 接受网络请求函数accept
int accept(int sockfd, struct sockaddr *client_addr, socklen_t *len)
功能介绍:
接受函数accept其实并不是真正的接受,而是客户端向服务器端监听端口发起的连接。对于TCP来说,accept从阻塞状态返回的时候,已经完成了三次握手的操作。Accept其实是取了一个已经处于connected状态的连接,然后把对方的协议族,网络地址以及端口都存在了client_addr中,返回一个用于操作的新的文件描述符,该文件描述符表示客户端与服务器端的连接,通过对该文件描述符操作,可以向client端发送和接收数据。同时之前socket创建的sockfd,则继续监听有没有新的连接到达本地端口。返回大于0的文件描述符则表示accept成功,否则失败。
参数说明:
- sockfd:是由socket调用返回的文件描述符
- client_addr是本地服务器端的一个struct sockaddr类型的变量,用于存放新连接的客户端的协议族,网络地址以及端口号等,是用来给客户端的程序填写的,无需服务端填写
- len:是第二个参数所指内容的长度,对于TCP来说其值可以用sizeof(struct sockaddr_in)来计算大小,说要说明的是accept的第三个参数要是指针的形式,因为这个值是要传给协议栈使用的。
2.5 连接目标服务器函数connect
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)
功能介绍:
连接函数connect是属于client端的操作函数,其目的是向服务器端发送连接请求,这也是从客户端发起TCP三次握手请求的开始,服务器端的协议族,网络地址以及端口都会填充到connect函数的serv_addr地址当中。当connect返回0时说明已经connect成功,返回值是-1时,表示connect失败。
参数说明:
- sockfd:是由socket调用返回的文件描述符
- serv_addr:是一个struct sockaddr类型的指针,这个参数中设置的是要连接的目标服务器的协议族,网络地址以及端口号
- addrlen:表示第二个参数内容的大小,与accept不同,这个值不是一个指针
以上是TCP建立连接所需的五个函数(UDP所需函数也一样,只不过因为没有连接所以不需要listen,accept,connect函数)。在服务器端和客户端建立连接之后是进行数据间的发送和接收,TCP主要使用的接收函数是read或recv,发送函数是write或send;而UDP使用的则是recvfrom和sendto。
2.6 TCP写函数write
ssize_t write(int sockfd, const void *buf, size_t nbytes)
功能介绍:
write函数将buf中的nbytes字节内容写入文件描述符fd。成功时返回写的字节数。失败时返回-1。 并设置errno变量。
参数说明:
- sockfd:对于服务端是accept调用返回的文件描述符,对于客户端是由socket调用返回的文件描述符
- buf:发送数据缓冲区,要发送的数据会放在这个指针指向的内容空间中;
- nbytes:发送缓冲区的大小
2.7 TCP读函数read
ssize_t read(int sockfd, const void *buf, size_t nbytes)
功能介绍:
read返回实际所读的字节数,如果返回的值是0 表示已经读到文件的结束了,小于0表示出现了错误。
参数说明:
- sockfd:对于服务端是accept调用返回的文件描述符,对于客户端是由socket调用返回的文件描述符
- buf:用于存储接收到的数据缓冲区,接收的数据将放到这个指针所指向的内容的空间中
- nbytes:接收缓冲区的大小
2.8 TCP发送及读取数据函数send,recv
int recv(int sockfd, void *buf, int len, int flags)
int send(int sockfd, void *buf, int len, int flags)
功能介绍:
recv和send函数提供了和read和write差不多的功能。不过它们提供 了第四个参数来控制读写操作。
参数说明:
前面的三个参数和read,write一样,第四个参数可以是0或者是以下的组合 (如果flags为0,则和read,write一样的操作。还有其它的几个选项,不过我们实际上用的很少)
MSG_DONTROUTE 不查找路由表
MSG_OOB 接受或者发送带外数据
MSG_PEEK 查看数据,并不从系统缓冲区移走数据
MSG_WAITALL 等待所有数据
2.9 UDP发送数据函数sendto
ssize_t sendto(int sockfd, const void *buf, size_t len, int flag, const struct sockaddr *to, socklen_t tolen)
功能介绍:
sendto函数主要根据填充的接收方的地址信息向客户端或者服务器端发送数据,接收方的地址信息会提前设置在struct sockaddr类型的参数指针中,当返回值-1时,表明发送失败,当返回值大于等于0时,表示发送成功,并且发送数据的大小会通过返回值传递回来。
参数说明:
- sockfd:由socket创建的文件描述符;
- buf:发送数据缓冲区,要发送的数据会放在这个指针指向的内容空间中;
- len:发送缓冲区的大小;
- to:一个struct sockaddr类型的指针,其指向地址的内容是接收方地址信息;
- tolen:表示第5个参数指向的数据内容的长度,传递的是值,可以用sizeof(struct sockaddr)计算。
2.10 UDP接收数据函数recvfrom
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
功能介绍:
对于该函数主要的功能是,从客户端或者服务器端接收数据以及发送方的地址信息存储到本地的struct sockaddr类型参数变量当中,如果函数返回-1,所说明接收数据失败,如果返回的是大于等于0的值,则说明函数接收到的数据的大小。因为可以设置文件描述符的状态为阻塞模式,所以在没有接收到数据时,recvfrom会一直处于阻塞状态,直到有数据接收到。
参数说明:
- sockfd:创建socket时的文件描述符
- buf:用于存储接收到的数据缓冲区,接收的数据将放到这个指针所指向的内容的空间中
- len:接收缓冲区的大小
- from:指向struct sockaddr的指针,接收发送方地址信息
- fromlen:表示第5个参数所指向内容的长度,可以使用sizeof(struct sockaddr)来定义大小,不过因为是要传给内核协议栈,所以使用了指针类型
数据的发送和接收结束以后需要关闭套接字,关闭套接字有两个函数close和shutdown。TCP连接是双向的(是可读写的),当我们使用close时,会把读写通道都关闭,有时侯我们希望只关闭一个方向,这个时候我们可以使用shutdown。针对不同的howto,系统回采取不同的关闭方式。
2.11 关闭函数close
int close(int sockfd)
功能介绍:
当我们使用close时,会把读写通道都关闭。不过,close只会关闭本进程的 socket id,但连接还是开着的,用这个socket id的其他进程还能用这个连接进行读写。使用close中止一个连接,其实它只是减少文件描述符的参考数,并不直接关闭连接,只有当描述符的参考数为0时才关闭。
参数说明:
2.12 关闭函数shutdown
int shutdown(int sockfd, int howto)
功能介绍:
shutdown函数针对不同的howto,系统回采取不同的关闭方式,可以选择只中止一个方向的连接。与close不同的是,shutdown不考虑文件描述符的参考数,直接关闭文件描述符。在多进程程序里面,如果有几个子进程共享一个套接字时,如果我们使用shutdown,那么所有的子进程都不能够操作了,这个时候若想只关闭其中一个子进程的套接字描述符我们只能够使用close。
参数说明:
- sockfd:创建socket时的文件描述符
- howto:针对不同的howto,系统回采取不同的关闭方式
howto=0这个时候系统会关闭读通道,但是可以继续往接字描述符写
howto=1关闭写通道,和上面相反,着时候就只可以读了
howto=2关闭读写通道,和close一样
3. 简单实例
server.c
====================================================================
#include
#include
#include
#include
#include
#include
#define HELLO_WORLD_SERVER_PORT 6666
#define LENGTH_OF_LISTEN_QUEUE 20
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
int main(int argc, char **argv)
{
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
int server_socket = socket(PF_INET,SOCK_STREAM,0);
if( server_socket < 0)
{
printf("Create Socket Failed!");
exit(1);
}
{
int opt =1;
setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
}
if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)))
{
printf("Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT);
exit(1);
}
if ( listen(server_socket, LENGTH_OF_LISTEN_QUEUE) )
{
printf("Server Listen Failed!");
exit(1);
}
while (1)
{
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
int new_server_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length);
if ( new_server_socket < 0)
{
printf("Server Accept Failed!\n");
break;
}
char buffer[BUFFER_SIZE];
bzero(buffer, BUFFER_SIZE);
length = recv(new_server_socket,buffer,BUFFER_SIZE,0);
if (length < 0)
{
printf("Server Recieve Data Failed!\n");
break;
}
char file_name[FILE_NAME_MAX_SIZE+1];
bzero(file_name, FILE_NAME_MAX_SIZE+1);
strncpy(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));
printf("%s\n",file_name);
FILE * fp = fopen(file_name,"r");
if(NULL == fp )
{
printf("File:\t%s Not Found\n", file_name);
}
else
{
bzero(buffer, BUFFER_SIZE);
int file_block_length = 0;
while( (file_block_length = fread(buffer,sizeof(char),BUFFER_SIZE,fp))>0)
{
printf("file_block_length = %d\n",file_block_length);
if(send(new_server_socket,buffer,file_block_length,0)<0)
{
printf("Send File:\t%s Failed\n", file_name);
break;
}
bzero(buffer, BUFFER_SIZE);
}
fclose(fp);
printf("File:\t%s Transfer Finished\n",file_name);
}
close(new_server_socket);
}
close(server_socket);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
client.c
====================================================================
#include
#include
#include
#include
#include
#include
#define HELLO_WORLD_SERVER_PORT 6666
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
int main(int argc, char **argv)
{
if (argc != 2)
{
printf("Usage: ./%s ServerIPAddress\n",argv[0]);
exit(1);
}
struct sockaddr_in client_addr;
bzero(&client_addr,sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = htons(INADDR_ANY);
client_addr.sin_port = htons(0);
int client_socket = socket(AF_INET,SOCK_STREAM,0);
if( client_socket < 0)
{
printf("Create Socket Failed!\n");
exit(1);
}
if( bind(client_socket,(struct sockaddr*)&client_addr,sizeof(client_addr)))
{
printf("Client Bind Port Failed!\n");
exit(1);
}
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
if(inet_aton(argv[1],&server_addr.sin_addr) == 0)
{
printf("Server IP Address Error!\n");
exit(1);
}
server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
socklen_t server_addr_length = sizeof(server_addr);
if(connect(client_socket,(struct sockaddr*)&server_addr, server_addr_length) < 0)
{
printf("Can Not Connect To %s!\n",argv[1]);
exit(1);
}
char file_name[FILE_NAME_MAX_SIZE+1];
bzero(file_name, FILE_NAME_MAX_SIZE+1);
printf("Please Input File Name On Server:\t");
scanf("%s", file_name);
char buffer[BUFFER_SIZE];
bzero(buffer,BUFFER_SIZE);
strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));
send(client_socket,buffer,BUFFER_SIZE,0);
FILE * fp = fopen(file_name,"w");
if(NULL == fp )
{
printf("File:\t%s Can Not Open To Write\n", file_name);
exit(1);
}
bzero(buffer,BUFFER_SIZE);
int length = 0;
while( length = recv(client_socket,buffer,BUFFER_SIZE,0))
{
if(length < 0)
{
printf("Recieve Data From Server %s Failed!\n", argv[1]);
break;
}
int write_length = fwrite(buffer,sizeof(char),length,fp);
if (write_lengthprintf("File:\t%s Write Failed\n", file_name);
break;
}
bzero(buffer,BUFFER_SIZE);
}
printf("Recieve File:\t %s From Server[%s] Finished\n",file_name, argv[1]);
close(fp);
close(client_socket);
return 0;
}
以下是window下套接字模式和用法
一.套接字I/O模式
阻塞模式(默认)和非阻塞模式
ioctlsocket函数可设置
二.套接字函数
1.WSAStartup
WSAStartup函数用于初始化wa2_32.dll动态链接库。
int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);
wVersionRequested:调用者使用的Windows Socket的版本
lpWSAData:一个WSADATA结构指针
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, FAR * LPWSADATA;
wVersion:调用者使用的ws_32.dll动态链接库的版本号
wHighVersion:ws_32.dll支持的最高版本,通常与wVersion相同
szDescription:套接字描述信息,无实际意义
szSystemStatus:系统的配置或状态信息,无实际意义
iMaxSockets:最多可以打开多少个套接字,在套接字版本2或以后版本中,该成员将被忽略
iMaxUdpDg:数据报的最大长度。在套接字版本2或以后版本中,该成员将被忽略
lpVendorInfo:套接字厂商信息。在套接字版本2或以后版本中,该成员将被忽略
2.socket
该函数用于创建一个套接字
SOCKET socket(int af,int type,int protocol);
af:一个地址家族,通常为AF_INET
type:套接字类型,如果为SOCK_STREAM,表示创建面向链接的流式套接字;为SOCK_DGRAM,表示创建面向无链接的数据报套接字;
为SOCK_RAW,表示创建原始套接字。
potocol:表示套接口所用的协议,如果用户不指定,可以设置为0
返回值:返回创建的套接字句柄
3.bind
该函数用于将套接字绑定到指定的端口和地址上
int bind(SOCKET s,const struct sockaddr FAR* name,int namelen);
s:套接字标识
name:一个sockaddr结构指针,该结构包含了要结合的地址和端口号
namelen:确定name缓冲区的长度
返回值:成功为0,否者为SOCKET_ERROR
4.listen
该函数用于将套接字设置为监听模式
int listen(SOCKET s,int backlog);
s:套接字标识
backlog:等待连接的最大队列长度。例如,如果backlog=2,此时有三个客户端同时发出连接请求,那么前两个客户端会放置在等待队列中,第三个 客户端会得到错误信息
对于流式套接字,必须处于监听模式才能接受客户端套接字的连接。
5.accept
该函数用于接受客户端的连接,对于流式套接字,必须处于监听状态,才能接受客户端的连接
SOCKET accept(SOCTET s,struct sockaddr FAR* addr,int FAR* addrlen);
s:一个套接字,应处于监听状态
addr:一个sockaddr_in结构指针,包含一组客户端的端口号、IP地址等信息
addrlen:用于接受参数addr的长度
返回值:一个新的套接字,它对应于已经接受的客户端连接,对于该客户端的所有后续操作,都应使用这个新的套接字
6.closesocket
该函数用于关闭套接字
int closesocket(SOCKET s);
其中,S用于标识一个套接字。如果参数s设置SO_DONTLINGER 选项,则调用该函数后会立即返回,但此时如果有数据尚未传送完毕,会继续传送数据,然后关闭套接字。
7.connect
该函数用于发送一个连接请求
int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);
s:一个套接字
name:套接字s想要连接的主机地址和端口号
namelen:name缓冲区的长度
返回值:如果函数执行成功,返回值为0;否则为SOCKET_ERROR,用户可以通过WSAGETLASTERROR得到其错误描述
8.htons
该函数将一个16位的无符号短整型数据由主机排列方式转换为网络排列方式
u_shout htons(u_shout hostshort);
返回值:16位的网络排列方式数据
9.htonl
该函数将一个32位的无符号短整型数据由主机排列方式转换为网络排列方式
u_shout htons(u_long hostlong);
返回值:32位的网络排列方式数据
10.inet_addr
该函数将一个由字符串表示的地址转换为32位的无符号长整型数据
unsigned lon inet_addr(const char FAR* cp);
cp:一个IP地址的字符串
返回值:32位无符号长整型
11.recv
该函数用于从面向连接的套接字中接受数据
int recv(SOCKET ,char FAR* buf,int len,int flags);
s:一个套接字
buf:接受数据的缓冲区
len:buf的长度
flags:函数的调用方式,如果为MSG_PEEK,表示查看传来的数据,数据被复制到接受缓冲区,但是不会从输入队列中移走;为MSG_OOB,表示来处理Out-Of-Band数据,也就是带外数据
12.send
该函数用于在面向连接方式的套接字间发送数据
int send(SOCKET s,const char FAR buf,int len,int flags);
s:一个套接字
buf:存放要发送数据的缓冲区
len:缓存区长度
flag:函数的调用方式
13.select
该函数用来检查一个或多个套接字是否处于可读可写或错误状态
int select(int nfds,fd_set FAR* readfds,fd_set FAR* writefds,fd_set FAR* exceptdfds,const struct timecval FAR* timeout);
nfds:无实际意义
readfds:一组被检查的可读的套接字
writefds:一组被检查可写的套接字
exceptfds:被检查有错误的套接字
timeout:函数的等待时间
14.WSACleanup
该函数用于释放为ws2_32.dll动态链接库初始化时分配的资源
int WSACleanup(void);
15.WSAAsyncSelect
该函数用于将网络中发生的事件关联到窗口的某个消息中
int WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long IEvent);
s:套接字
hWnd:接受消息的窗口句柄
wMsg:窗口接受来自套接字中的消息
IEvent:网路中发生的事件
16.ioctlsocket
该函数用于设置套接字的I/O模式
int ioctlsocket(SOCKET s,long cmd,u_long FAR* argp);
s:待更改I/O模式的套接字
cmd:对套接字的操作命令。cmd=FIOBIO,当argp为0时表示禁止非阻塞模式,当argp为非零时表示设置非阻塞模式。cmd=FIONREAD,表示从套接字中可以读取的数据量;cmd=SIOCATMARK,表示是否所有的带外数据都已被读入,这个命令仅适用于流式套接字,并且该套接字已被设置为可以在线接受带外数据(SO_OOBINLINE)
argp:命令参数
二者之间最大 的区别
SOCKET在原理上应该是一样的,只是不同系统的运行机置有些不同。
Socket 编程 windows到Linux代码移植遇到的问题
1、一些常用函数的移植
http://www.vckbase.com/document/viewdoc/?id=1586
2、网络
socket相关程序从windows移植到linux下需要注意的
1)头文件
windows下winsock.h/winsock2.h
linux下sys/socket.h
错误处理:errno.h
其他常用函数的头文件可到命令行下用man指令查询。
2)初始化
windows下需要用WSAStartup
linux下不需要(很方便),直接可以使用
3)关闭socket
windows下closesocket(...)
linux下close(...)
4)类型
windows下SOCKET
在linux下为int类型
5)绑定地址的结构体
名称相同,都是struct sockaddr、struct sockaddr_in,这两者通常转换使用;
在Windows下面名称都是大写,而在Linux下为小写
常用:
Linux下:
sockaddr_in destAddr;
destAdd.sin_family=AF_INET;
destAddr.sin_port=htons(2030);
destAddr.sin_addr.s_addr=inet_addr("192.168.1.1");
Windows下:
SOCKADDR_IN destAddr;
destAddr.sin_addr.S_un.S_addr=inet_addr("192.168.1.1");
但结构体中成员的名称不同
Windows中结构体成员
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
struct sockaddr {
u_short sa_family;
char sa_data[14];
};
struct in_addr {
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
};
6)获取错误码
windows下getlasterror()/WSAGetLastError()
linux下errno变量
7)设置非阻塞
windows下ioctlsocket()
linux下fcntl()
8)send函数最后一个参数
windows下一般设置为0
linux下最好设置为MSG_NOSIGNAL,如果不设置,在发送出错后有可 能会导致程序退出。
9)毫秒级时间获取
windows下GetTickCount()
linux下gettimeofday()
10)数据类型的一些转化
通用的:
小端到大端(网络协议使用)的转换:htonl, htons
点分十进制IP和整数之间的相互转换:inet_addr()(该函数将点分十进制转为整数),inet_aton(),inet_ntoa(),inet_pton()(linux下独有 该函数可以实现相互之间的转换)
使用到的头文件不相同,linux下用man命令查询。
另外注意:
linux下使用的套接字为伯克利套接字,因此在
select()函数的使用上(第一个参数的设置)也有区别;
windows下为了与伯克利套接字匹配,第一个参数是无所谓,一般可设为0;
int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!
3、多线程
多线程: (win)process.h --〉(linux)pthread.h
_beginthread --> pthread_create
_endthread --> pthread_exit