下面大概分几个方面进行罗列:
Linux要包含
[cpp]
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
等头文件,而windows下则是包含
[cpp]
#include <winsock.h>
。
Linux中socket为整形,Windows中为一个SOCKET。
Linux中关闭socket为close,Windows中为closesocket。
Linux中有变量socklen_t,Windows中直接为int。
因为linux中的socket与普通的fd一样,所以可以在TCP的socket中,发送与接收数据时,直接使用read和write。而windows只能使用recv和send。
设置socet选项,比如设置socket为非阻塞的。Linux下为
[cpp]
flag = fcntl (fd, F_GETFL);
fcntl (fd, F_SETFL, flag | O_NONBLOCK);
,Windows下为
[cpp]
flag = 1;
ioctlsocket (fd, FIONBIO, (unsigned long *) &flag);
。
当非阻塞socket的TCP连接正在进行时,Linux的错误号为EINPROGRESS,Windows的错误号为WSAEWOULDBLOCK。
file
Linux下面,文件换行是"\n",而windows下面是"\r\n"。
Linux下面,目录分隔符是"/",而windows下面是"\"。
Linux与Windows下面,均可以使用stat调用来查询文件信息。但是,Linux只支持2G大小,而Windows只支持4G大小。为了支持更大的文件查询,可以在Linux环境下加
_FILE_OFFSET_BITS=64定义,在Windows下面使用_stat64调用,入参为struct __stat64。
Linux中可根据stat的st_mode判断文件类型,有S_ISREG、S_ISDIR等宏。Windows中没有,需要自己定义相应的宏,如
[cpp]
#define S_ISREG(m) (((m) & 0170000) == (0100000))
#define S_ISDIR(m) (((m) & 0170000) == (0040000))
Linux中删除文件是unlink,Windows中为DeleteFile。
time
Linux中,time_t结构是长整形。而windows中,time_t结构是64位的整形。如果要在windows始time_t为32位无符号整形,可以加宏定义,_USE_32BIT_TIME_T。
Linux中,sleep的单位为秒。Windows中,Sleep的单位为毫秒。即,Linux下sleep (1),在Windows环境下则需要Sleep (1000)。
Windows中的timecmp宏,不支持大于等于或者小于等于。
Windows中没有struct timeval结构的加减宏可以使用,需要手动定义:
[cpp]
#define MICROSECONDS (1000 * 1000)
#define timeradd(t1, t2, t3) do { \
(t3)->tv_sec = (t1)->tv_sec + (t2)->tv_sec; \
(t3)->tv_usec = (t1)->tv_usec + (t2)->tv_usec % MICROSECONDS; \
if ((t1)->tv_usec + (t2)->tv_usec > MICROSECONDS) (t3)->tv_sec ++; \
} while (0)
#define timersub(t1, t2, t3) do { \
(t3)->tv_sec = (t1)->tv_sec - (t2)->tv_sec; \
(t3)->tv_usec = (t1)->tv_usec - (t2)->tv_usec; \
if ((t1)->tv_usec - (t2)->tv_usec < 0) (t3)->tv_usec --, (t3)->tv_usec += MICROSECONDS; \
} while (0)
调用进程
Linux下可以直接使用system来调用外部程序。Windows最好使用WinExec,因为WinExec可以支持是打开还是隐藏程序窗口。用WinExec的第二个入参指明,如
SW_SHOW/SW_HIDE。
杂项
Linux为srandom和random函数,Windows为srand和rand函数。
Linux为snprintf,Windows为_snprintf。
同理,Linux中的strcasecmp,Windows为_stricmp。
错误处理
Linux下面,通常使用全局变量errno来表示函数执行的错误号。Windows下要使用GetLastError ()调用来取得。
Linux环境下仅有的
这些函数或者宏,Windows中完全没有,需要用户手动实现。
atoll
[cpp]
long long
atoll (const char *p)
{
int minus = 0;
long long value = 0;
if (*p == '-')
{
minus ++;
p ++;
}
while (*p >= '0' && *p <= '9')
{
value *= 10;
value += *p - '0';
p ++;
}
return minus ? 0 - value : value;
}
gettimeofday
[cpp]
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define EPOCHFILETIME 11644473600000000Ui64
#else
#define EPOCHFILETIME 11644473600000000ULL
#endif
struct timezone
{
int tz_minuteswest;
int tz_dsttime;
};
int
gettimeofday (struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
LARGE_INTEGER li;
__int64 t;
static int tzflag;
if (tv)
{
GetSystemTimeAsFileTime (&ft);
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
t = li.QuadPart; /* In 100-nanosecond intervals */
t -= EPOCHFILETIME; /* Offset to the Epoch time */
t /= 10; /* In microseconds */
tv->tv_sec = (long) (t / 1000000);
tv->tv_usec = (long) (t % 1000000);
}
if (tz)
{
if (!tzflag)
{
_tzset ();
tzflag++;
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
}
编译相关
当前函数,Linux用__FUNCTION__表示,Windows用__func__表示。
--------------------------------------------------------------------------------
Socket 编程 windows到Linux代码移植遇到的问题
1)头文件
windows下winsock.h/winsock2.h
linux下sys/socket.h
错误处理:errno.h
2)初始化
windows下需要用WSAStartup
linux下不需要
3)关闭socket
windows下closesocket(...)
linux下close(...)
4)类型
windows下SOCKET
linux下int
如我用到的一些宏:
#ifdef WIN32
typedef int socklen_t;
typedef int ssize_t;
#endif
#ifdef __LINUX__
typedef int SOCKET;
typedef unsigned char BYTE;
typedef unsigned long DWORD;
#define FALSE 0
#define SOCKET_ERROR (-1)
#endif
5)获取错误码
windows下getlasterror()/WSAGetLastError()
linux下errno变量
6)设置非阻塞
windows下ioctlsocket()
linux下fcntl() <fcntl.h>
7)send函数最后一个参数
windows下一般设置为0
linux下最好设置为MSG_NOSIGNAL,如果不设置,在发送出错后有可 能会导致程序退出。
8)毫秒级时间获取
windows下GetTickCount()
linux下gettimeofday()
3、多线程
多线程: (win)process.h --〉(linux)pthread.h
_beginthread --> pthread_create
_endthread --> pthread_exit
-----------------------------------------------------------------
windows与linux平台使用的socket均继承自Berkeley socket(rfc3493),他们都支持select I/O模型,均支持使用getaddrinfo与getnameinfo实现协议无关编程。但存在细微差别,
主要有:
头文件及类库。windows使用winsock2.h(需要在windows.h前包含),并要链接库ws2_32.lib;linux使用netinet/in.h, netdb.h等。
windows下在使用socket之前与之后要分别使用WSAStartup与WSAClean。
关闭socket,windows使用closesocket,linux使用close。
send*与recv*函数参数之socket长度的类型,windows为int,linux为socklen_t,可预编译指令中处理这一差异,当平台为windows时#define socklen_t unsigned int。
select函数第一个参数,windows忽略该参数,linux下该参数表示集合中socket的上限值,一般设为sockfd(需select的socket) + 1。
windows下socket函数返回值类型为SOCKET(unsigned int),其中发生错误时返回INVALID_SOCKET(0),linux下socket函数返回值类型int, 发生错误时返回-1。
另外,如果绑定本机回环地址,windows下sendto函数可以通过,linux下sendto回报错:errno=22, Invalid arguement。一般情况下均绑定通配地址。
转载jlins
B. Linux 进程间套接字通信(Socket)基础知识
姓名:罗学元 学号:21181214375 学院:广州研究院
【嵌牛导读】Linux进程间套接字通信基础
【嵌牛鼻子】Linux 进程间套接字及通信介绍
【嵌牛提问】Linux进程间套接字包含哪些内容,如何实现通信
一、套接字(Socket)通信原理
套接字通信允许互联的位于不同计算机上的进程之间实现通信功能。
二、套接字的属性
套接字的特性由3个属性确定,它们分别是:域、类型和协议。
1. 套接字的域
它指定套接字通信中使用的网络介质,最常见的套接字域是AF_INET,它指的是Internet网络。当客户使用套接字进行跨网络的连接时,它就需要用到服务器计算机的IP地址和端口来指定一台联网机器上的某个特定服务,所以在使用socket作为通信的终点,服务器应用程序必须在开始通信之前绑定一个端口,服务器在指定的端口等待客户的连接。
另一个域AF_UNIX表示UNIX文件系统,就是文件输入/输出,它的地址就是文件名。
2. 套接字类型
因特网提供了两种通信机制:流(stream)和数据报(datagram),因而套接字的类型也就分为流套接字和数据报套接字。我们主要看流套接字。
流套接字由类型SOCK_STREAM指定,它们是在AF_INET域中通过TCP/IP连接实现,同时也是AF_UNIX中常用的套接字类型。
流套接字提供的是一个有序、可靠、双向字节流的连接,因此发送的数据可以确保不会丢失、重复或乱序到达,而且它还有一定的出错后重新发送的机制。
与流套接字相对的是由类型SOCK_DGRAM指定的数据报套接字,它不需要建立连接和维持一个连接,它们在AF_INET中通常是通过UDP/IP实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并不需要总是要建立和维持一个连接。
3.套接字协议
只要底层的传输机制允许不止一个协议来提供要求的套接字类型,我们就可以为套接字选择一个特定的协议。通常只需要使用默认值。
三、套接字地址
每个套接字都有其自己的地址格式,对于AF_UNIX域套接字来说,它的地址由结构sockaddr_un来描述,该结构定义在头文件
struct sockaddr_un{
sa_family_t sun_family; //AF_UNIX,它是一个短整型
char sum_path[]; //路径名
};
对于AF_INET域套接字来说,它的地址结构由sockaddr_in来描述,它至少包括以下几个成员:
struct sockaddr_in{
short int sin_family; //AN_INET
unsigned short int sin_port; //端口号
struct in_addr sin_addr; //IP地址
}
而in_addr被定义为:
struct in_addr{
unsigned long int s_addr;
}
四、基于流套接字的客户/服务器的工作流程
使用socket进行进程通信的进程采用的客户/服务器系统是如何工作的呢?
1.服务器端
首先,服务器应用程序用系统调用socket来创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。
接下来,服务器进程会给套接字起个名字,我们使用系统调用bind来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。
然后,系统调用listen来创建一个队列,并将其用于存放来自客户的进入连接。
最后,服务器通过系统调用accept来接受客户的连接。它会创建一个与原有的命名套接不同的新套接字,这个套接字只用于与这个特定客户端进行通信,而命名套接字(即原先的套接字)则被保留下来继续处理来自其他客户的连接。
2.客户端
基于socket的客户端比服务器端简单。同样,客户应用程序首先调用socket来创建一个未命名的套接字,然后讲服务器的命名套接字作为一个地址来调用connect与服务器建立连接。
一旦连接建立,我们就可以像使用底层的文件描述符那样用套接字来实现双向数据的通信。
C. linux用什么标识,描述和控制文件
Linux的文件标识符,和Windows的就是文件名不同,它相当于文件的一个指针。在linux的C语言中,除了用通用的《【C】文件读写问题》(点击打开链)中介绍过的fopen等c语言通用操作文件以外,其Linux应用请参考《【Linux】利用C语言文件流复制单一文件),还能用文件标识符还来操作文件。这也是Linux中C语言,最底层,最原始控制文件的方式,其函数open,read,write,close已经完美地表明这一点。同时,在Linux无论是文件、设备和管道,甚至是个可操作对象对可以视作文件来对待,具体表现为都可以用这个文件标识符困岁春来操作他们。
文件标识符非常独特,并不是指针,其变量类型就是大家非常常用的int。
至于这个东西怎么用,具体请看如下文件读写程序:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>//文件控制
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAXSIZE 512//缓冲区的大小
const char* FILEPATH="/tmp/file.txt";//文件目录
int main(void) {
int fd;//文件标识符,
char *s = "被折腾的文字";
char buffer[MAXSIZE+1];//用来接字符的缓冲区
int size;//读入的文件长度
if((fd=open(FILEPATH,O_CREAT|O_TRUNC|O_RDWR,0777))<0){
//O_CREAT如果指定文件不存在,则创建这个文件,O_EXCL如果要创建的文件已存在,则返回 -1,并且修改 errno 的值
//O_APPEND每次写操作都写入文件的末尾,O_TRUNC如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
//O_RDONLY只读模式,O_WRONLY只写模式,O_RDWR读写模式
//0777为最高权限
perror("打开文件失败!");
exit(1);
}
if((write(fd,s,strlen(s)))<0){
perror("写入文件失败!");
exit(1);
}
//lseek(fd,0,SEEK_SET);//即把文件指针移至buf文件的开始处
lseek(fd,-strlen(s),SEEK_END);//即把文件指针往从结尾处向前移动strlen(s)个字符
if((size=read(fd,buffer,MAXSIZE))<0){
perror("读入文件失败!");
exit(1);
}else{
buffer[size]='\0';//字符串数组封口
printf("%s\n",buffer);
}
if(close(fd)<0) {
perror("关键文件失败!");
exit(1);
}
unlink(FILEPATH);//删除文件
exit(0);
}
这个程序的一大堆头文件,是没办法的,因为所用到的函数需要的基本头文件就是这么多,但这些头文件都是Linux的基本头文件,能够拿来直接用。
程序首先打开并同时利用open的参数创建一个文件,之后向这个文件,通过read函数写入一个名为“缓冲区”buffer的字符数组,也就是字符串string的一些东西,然后,写入完毕,因为文件操作光标将会移到文件最后,所以我们要先将其移回文件头,再利用write函数读入这个文件的内容到buffer,并打印到屏幕,最后再用close关闭文件标识符与文件的连接,并利用unlink删除这个文件,如果没有close,unlink将不起作用,因为这个程序正在占用这个/tmp/file.txt,无法删除,如果要强制删除可以考虑remove函数。
所以,上述代码的运行结果如下图:
最后雀橘,这个文件自然是要被删除的了,肯定是没有的:
上述程序很简单,但我们更多应该关注这里文件标识符汪耐的作用。fd这个int就是文件标识符,相当于FILE *的作用,但他就是一个int。实质上,这个int非常独特,同open函数,int fd里面存着要被操作文件的地址,但它却又不是int *,之后的write和read函数都要根据这个fd所指明的方向来,你可以发现write,read参数都有一个地方,填入了fd,可要求填入的,却是一个int变量,这在windows里面是没有的,同时不了解文件标识符的人,看到write和read的使用可能是云里雾里的,之后的close就更不用说了,就是清楚这个fd与被操作文件的关联关系。
unlink函数则和文件标识符无关,需要一个文件路径的参数。
D. linux socket 的程序如何在windows下编译并运行
本质区别是这样的,linux下用的是伯克利socket,windows底下用的WinSocket.
两者其实是大同小异的,不同的地方在以下几点:
1 头文件不同,在linux下用到的关键头文件<sys/socket.h>
windows下用到的是 <Winsock2.h>
2 socket的初始化不一样,在windows下要有一个WSAStartup,而linux 下没有。
3 具体的细节不一样,linux下的c和windows下的c的一些语法有些出入,你自己找找。
下面我把windows下的socket通讯的最基本的结构给你,你对照着修改就可以了,一下的程序我调试了一下,编译过了。
客户端
#include <Winsock2.h>
#include <stdio.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
char recvBuf[100];
recv(sockClient,recvBuf,100,0);
printf("%s\n",recvBuf);
send(sockClient,"This is lisi",strlen("This is lisi")+1,0);
closesocket(sockClient);
WSACleanup();
}
服务器端
#include <Winsock2.h>
#include <stdio.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
listen(sockSrv,5);
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
while(1)
{
SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
char sendBuf[100];
sprintf(sendBuf,"来自服务器端",
inet_ntoa(addrClient.sin_addr));
send(sockConn,sendBuf,strlen(sendBuf)+1,0);
char recvBuf[100];
recv(sockConn,recvBuf,100,0);
printf("%s\n",recvBuf);
closesocket(sockConn);
}
}