① close的linux 中的close函數
頭文件:
#include <unistd.h>
int close(int fd);
返回值:成功返回0,出錯返回-1並設置errno
參數fd是要關閉的文件描述符。需要說明的是,當一個進程終止時,內核對該進程所有尚未關閉的文件描述符
調用close關閉,所以即使用戶程序不調用close,在終止時內核也會自動關閉它打開的所有文件。但是對於一
個長年累月運行的程序(比如網路伺服器),打開的文件描述符一定要記得關閉,否則隨著打開的文件越來越
多,會佔用大量文件描述符和系統資源。
應用於 Visual Basic 的 Close 語句
Close 語句:關閉Open語句所打開的輸入/輸出 (I/O) 文件。
語法
Close[filenumberlist]
可選的filenumberlist參數為一個或多個文件號,其中filenumber為任何有效的文件號,語法如下:
[[#]filenumber] [,[#]filenumber]. . .
說明
若省略filenumberlist,則將關閉Open語句打開的所有活動文件。
當關閉Output或Append打開的文件時,將屬於此文件的最終輸出緩沖區寫入操作系統緩沖區。所有與該文件相關聯的緩沖區空間都被釋放。
在執行Close語句時,文件與其文件號之間的關聯將終結。
② windows與linux 頭文件對照
1.linux和windows平台下,能夠對應的頭文件就是符合C11標準的頭文件。其他的頭文件不僅和平台有關系,還和平台下的編譯環境有關,很難畫上等號的。
2.C語言符合標準的頭文件
#include <assert.h> //設定插入點
#include <ctype.h> //字元處理
#include <errno.h> //定義錯誤碼
#include <float.h> //浮點數處理
#include <fstream.h> //文件輸入/輸出
#include <iomanip.h> //參數化輸入/輸出
#include <iostream.h> //數據流輸入/輸出
#include <limits.h> //定義各種數據類型最值常量
#include <locale.h> //定義本地化函數
#include <math.h> //定義數學函數
#include <stdio.h> //定義輸入/輸出函數
#include <stdlib.h> //定義雜項函數及內存分配函數
#include <string.h> //字元串處理
#include <strstrea.h> //基於數組的輸入/輸出
#include <time.h> //定義關於時間的函數
#include <wchar.h> //寬字元處理及輸入/輸出
#include <wctype.h> //寬字元分類
3.linux常用頭文件如下:
POSIX標準定義的頭文件
<dirent.h> 目錄項
<fcntl.h> 文件控制
<fnmatch.h> 文件名匹配類型
<glob.h> 路徑名模式匹配類型
<grp.h> 組文件
<netdb.h> 網路資料庫操作
<pwd.h> 口令文件
<regex.h> 正則表達式
<tar.h> TAR歸檔值
<termios.h> 終端I/O
<unistd.h> 符號常量
<utime.h> 文件時間
<wordexp.h> 字元擴展類型
-------------------------
<arpa/inet.h> INTERNET定義
<net/if.h> 套接字本地介面
<netinet/in.h> INTERNET地址族
<netinet/tcp.h> 傳輸控制協議定義
-------------------------
<sys/mman.h> 內存管理聲明
<sys/select.h> Select函數
<sys/socket.h> 套接字借口
<sys/stat.h> 文件狀態
<sys/times.h> 進程時間
<sys/types.h> 基本系統數據類型
<sys/un.h> UNIX域套接字定義
<sys/utsname.h> 系統名
<sys/wait.h> 進程式控制制
------------------------------
POSIX定義的XSI擴展頭文件
<cpio.h> cpio歸檔值
<dlfcn.h> 動態鏈接
<fmtmsg.h> 消息顯示結構
<ftw.h> 文件樹漫遊
<iconv.h> 代碼集轉換使用程序
<langinfo.h> 語言信息常量
<libgen.h> 模式匹配函數定義
<monetary.h> 貨幣類型
<ndbm.h> 資料庫操作
<nl_types.h> 消息類別
<poll.h> 輪詢函數
<search.h> 搜索表
<strings.h> 字元串操作
<syslog.h> 系統出錯日誌記錄
<ucontext.h> 用戶上下文
<ulimit.h> 用戶限制
<utmpx.h> 用戶帳戶資料庫
-----------------------------
<sys/ipc.h> IPC(命名管道)
<sys/msg.h> 消息隊列
<sys/resource.h>資源操作
<sys/sem.h> 信號量
<sys/shm.h> 共享存儲
<sys/statvfs.h> 文件系統信息
<sys/time.h> 時間類型
<sys/timeb.h> 附加的日期和時間定義
<sys/uio.h> 矢量I/O操作
------------------------------
POSIX定義的可選頭文件
<aio.h> 非同步I/O
<mqueue.h> 消息隊列
<pthread.h> 線程
<sched.h> 執行調度
<semaphore.h> 信號量
<spawn.h> 實時spawn介面
<stropts.h> XSI STREAMS介面
<trace.h> 事件跟蹤
③ 解釋一下linux驅動程序結構框架及工作原理
一、Linux device driver 的概念
系統調用是操作系統內核和應用程序之間的介面,設備驅動程序是操作系統內核和機器硬體之間的介面。設備驅動程序為應用程序屏蔽了硬體的細節,這樣在應用程序看來,硬體設備只是一個設備文件,應用程序可以象操作普通文件一樣對硬體設備進行操作。設備驅動程序是內核的一部分,它完成以下的功能:
1、對設備初始化和釋放;
2、把數據從內核傳送到硬體和從硬體讀取數據;
3、讀取應用程序傳送給設備文件的數據和回送應用程序請求的數據;
4、檢測和處理設備出現的錯誤。
在Linux操作系統下有三類主要的設備文件類型,一是字元設備,二是塊設備,三是網路設備。字元設備和塊設備的主要區別是:在對字元設備發出讀/寫請求時,實際的硬體I/O一般就緊接著發生了,塊設備則不然,它利用一塊系統內存作緩沖區,當用戶進程對設備請求能滿足用戶的要求,就返回請求的數據,如果不能,就調用請求函數來進行實際的I/O操作。塊設備是主要針對磁碟等慢速設備設計的,以免耗費過多的CPU時間來等待。
已經提到,用戶進程是通過設備文件來與實際的硬體打交道。每個設備文件都都有其文件屬性(c/b),表示是字元設備還是塊設備?另外每個文件都有兩個設備號,第一個是主設備號,標識驅動程序,第二個是從設備號,標識使用同一個設備驅動程序的不同的硬體設備,比如有兩個軟盤,就可以用從設備號來區分他們。設備文件的的主設備號必須與設備驅動程序在登記時申請的主設備號一致,否則用戶進程將無法訪問到驅動程序。
最後必須提到的是,在用戶進程調用驅動程序時,系統進入核心態,這時不再是搶先式調度。也就是說,系統必須在你的驅動程序的子函數返回後才能進行其他的工作。如果你的驅動程序陷入死循環,不幸的是你只有重新啟動機器了,然後就是漫長的fsck。
二、實例剖析
我們來寫一個最簡單的字元設備驅動程序。雖然它什麼也不做,但是通過它可以了解Linux的設備驅動程序的工作原理。把下面的C代碼輸入機器,你就會獲得一個真正的設備驅動程序。
由於用戶進程是通過設備文件同硬體打交道,對設備文件的操作方式不外乎就是一些系統調用,如 open,read,write,close…, 注意,不是fopen, fread,但是如何把系統調用和驅動程序關聯起來呢?這需要了解一個非常關鍵的數據結構:
STruct file_operatiONs {
int (*seek) (struct inode * ,struct file *, off_t ,int);
int (*read) (struct inode * ,struct file *, char ,int);
int (*write) (struct inode * ,struct file *, off_t ,int);
int (*readdir) (struct inode * ,struct file *, struct dirent * ,int);
int (*select) (struct inode * ,struct file *, int ,select_table *);
int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);
int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);
int (*open) (struct inode * ,struct file *);
int (*release) (struct inode * ,struct file *);
int (*fsync) (struct inode * ,struct file *);
int (*fasync) (struct inode * ,struct file *,int);
int (*check_media_change) (struct inode * ,struct file *);
int (*revalidate) (dev_t dev);
}
這個結構的每一個成員的名字都對應著一個系統調用。用戶進程利用系統調用在對設備文件進行諸如read/write操作時,系統調用通過設備文件的主設備號找到相應的設備驅動程序,然後讀取這個數據結構相應的函數指針,接著把控制權交給該函數。這是linux的設備驅動程序工作的基本原理。既然是這樣,則編寫設備驅動程序的主要工作就是編寫子函數,並填充file_operations的各個域。
下面就開始寫子程序。
#include <linux/types.h> 基本的類型定義
#include <linux/fs.h> 文件系統使用相關的頭文件
#include <linux/mm.h>
#include <linux/errno.h>
#include <asm/segment.h>
unsigned int test_major = 0;
static int read_test(struct inode *inode,struct file *file,char *buf,int count)
{
int left; 用戶空間和內核空間
if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT )
return -EFAULT;
for(left = count ; left > 0 ; left--)
{
__put_user(1,buf,1);
buf++;
}
return count;
}
這個函數是為read調用准備的。當調用read時,read_test()被調用,它把用戶的緩沖區全部寫1。buf 是read調用的一個參數。它是用戶進程空間的一個地址。但是在read_test被調用時,系統進入核心態。所以不能使用buf這個地址,必須用__put_user(),這是kernel提供的一個函數,用於向用戶傳送數據。另外還有很多類似功能的函數。請參考,在向用戶空間拷貝數據之前,必須驗證buf是否可用。這就用到函數verify_area。為了驗證BUF是否可以用。
static int write_test(struct inode *inode,struct file *file,const char *buf,int count)
{
return count;
}
static int open_test(struct inode *inode,struct file *file )
{
MOD_INC_USE_COUNT; 模塊計數加以,表示當前內核有個設備載入內核當中去
return 0;
}
static void release_test(struct inode *inode,struct file *file )
{
MOD_DEC_USE_COUNT;
}
這幾個函數都是空操作。實際調用發生時什麼也不做,他們僅僅為下面的結構提供函數指針。
struct file_operations test_fops = {?
read_test,
write_test,
open_test,
release_test,
};
設備驅動程序的主體可以說是寫好了。現在要把驅動程序嵌入內核。驅動程序可以按照兩種方式編譯。一種是編譯進kernel,另一種是編譯成模塊(moles),如果編譯進內核的話,會增加內核的大小,還要改動內核的源文件,而且不能動態的卸載,不利於調試,所以推薦使用模塊方式。
int init_mole(void)
{
int result;
result = register_chrdev(0, "test", &test_fops); 對設備操作的整個介面
if (result < 0) {
printk(KERN_INFO "test: can't get major number\n");
return result;
}
if (test_major == 0) test_major = result; /* dynamic */
return 0;
}
在用insmod命令將編譯好的模塊調入內存時,init_mole 函數被調用。在這里,init_mole只做了一件事,就是向系統的字元設備表登記了一個字元設備。register_chrdev需要三個參數,參數一是希望獲得的設備號,如果是零的話,系統將選擇一個沒有被佔用的設備號返回。參數二是設備文件名,參數三用來登記驅動程序實際執行操作的函數的指針。
如果登記成功,返回設備的主設備號,不成功,返回一個負值。
void cleanup_mole(void)
{
unregister_chrdev(test_major,"test");
}
在用rmmod卸載模塊時,cleanup_mole函數被調用,它釋放字元設備test在系統字元設備表中佔有的表項。
一個極其簡單的字元設備可以說寫好了,文件名就叫test.c吧。
下面編譯 :
$ gcc -O2 -DMODULE -D__KERNEL__ -c test.c –c表示輸出制定名,自動生成.o文件
得到文件test.o就是一個設備驅動程序。
如果設備驅動程序有多個文件,把每個文件按上面的命令行編譯,然後
ld ?-r ?file1.o ?file2.o ?-o ?molename。
驅動程序已經編譯好了,現在把它安裝到系統中去。
$ insmod ?–f ?test.o
如果安裝成功,在/proc/devices文件中就可以看到設備test,並可以看到它的主設備號。要卸載的話,運行 :
$ rmmod test
下一步要創建設備文件。
mknod /dev/test c major minor
c 是指字元設備,major是主設備號,就是在/proc/devices里看到的。
用shell命令
$ cat /proc/devices
就可以獲得主設備號,可以把上面的命令行加入你的shell script中去。
minor是從設備號,設置成0就可以了。
我們現在可以通過設備文件來訪問我們的驅動程序。寫一個小小的測試程序。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main()
{
int testdev;
int i;
char buf[10];
testdev = open("/dev/test",O_RDWR);
if ( testdev == -1 )
{
printf("Cann't open file \n");
exit(0);
}
read(testdev,buf,10);
for (i = 0; i < 10;i++)
printf("%d\n",buf[i]);
close(testdev);
}
編譯運行,看看是不是列印出全1
以上只是一個簡單的演示。真正實用的驅動程序要復雜的多,要處理如中斷,DMA,I/O port等問題。這些才是真正的難點。上述給出了一個簡單的字元設備驅動編寫的框架和原理,更為復雜的編寫需要去認真研究LINUX內核的運行機制和具體的設備運行的機制等等。希望大家好好掌握LINUX設備驅動程序編寫的方法。
④ linux中用close關閉一個未打開的文件,會發生什麼 為什麼
會返回-1
close()函數執行成功返回0,否則返回-1
#include <unistd.h>
int main(void)
{
int a=close(3);
printf("%d",a);
}
用3是因為0,1,2分別對應標准輸入,標准輸出,標准錯誤
[firefly@localhost Project]$ cc test.c
[firefly@localhost Project]$ ./a.out
-1[firefly@localhost Project]$
⑤ linux下socket編程中close()函數
只要不用抄close或fclose,不管把這個socket_fd值存到哪裡,都可以使用。比如:
int socket_fd = socket(...);
int socket_x = socket_fd;
那麼send(socket_x)和send(socket_fd)結果完全一致
⑥ socket編程在windows和linux下的區別
下面大概分幾個方面進行羅列:
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
⑦ linux下,C語言頭文件在哪
一、 C標准庫頭文件,以及Linux的標准庫文件的對應頭文件,默認放在/usr/include下。 如圖:
標識出回了最常用的幾答個頭文件。
二、 自定義頭文件,或者集成頭文件,需要在編譯的時候指定。可以在命令行中指定,也可以在makefile中指定。
指定自定義頭文件路徑方式為:
-IPATH1 -IPATH2...
如當前目錄下的inc文件夾,指定為頭文件, 那麼在編譯a.c時,可以命令寫作:
gcc a.c -I./inc -o a.out
⑧ Linux C中的Socket,shutdown函數和close函數有什麼不同
假設server和client 已經建立了連接,server調用了close, 發送FIN 段給client(其實不一定會發送FIN段,後面再說),此時server不能再通過
socket發送和接收數據,此時client調用read,如果接收到FIN 段會返回0,但client此時還是可以write 給server的,write調用只負責把數據交給TCP
發送緩沖區就可以成功返回了,所以不會出錯,而server收到數據後應答一個RST段,表示伺服器已經不能接收數據,連接重置,client收到RST段後無
法立刻通知應用層,只把這個狀態保存在TCP協議層。如果client再次調用攔枝跡write發數據給server,由於TCP協議層已經處於RST狀態了,因此不會將數據
發出,而是發一個SIGPIPE信號給應用層,SIGPIPE信號的預設處理動作是終止程序。
有時候代碼中需要連續多次調用write,可能還來不及調用read得知對方已關閉了連接就被SIGPIPE信號終止掉了,這就需要在初始化時調用sigaction處
理SIGPIPE信號,對於這個信號的處理我們通常忽略即可,signal(SIGPIPE, SIG_IGN); 如果SIGPIPE信號沒有導致進程異常退出,write返回-1並且
errno為EPIPE。
#include <unistd.h>
intclose(int fd);
close 關閉了自身數據傳輸的兩個方向。
#include <sys/socket.h>
intshutdown(int sockfd, int how);
shutdown 可以選擇關閉某個方向或者同時關閉兩個方向,shutdownhow = 1 or how = 2 (SHUT_WR or SHUT_RDWR),可以保證對等方接收到一個EOF字元(即發送了一個FIN段),而不管其他進程是否已經打開了這個套接字。而close不能保證,只有當某個sockfd的引用計數為0,close 才會發送FIN段,否則只是將引用計數減1而已。也就是說只有當所有進程(可能fork多個子進程都打開了這個套接字)都關閉了這個套接字,close 才會發送FIN段。
所以說,如果是調用shutdown how = 1 ,則意味著往一個已經接收FIN的套接字中寫是簡並允許的,接收到FIN段僅代表對方不再發送數據,但對方還是可以讀取數據的,可以讓對方可以繼續讀取緩沖區剩餘的數據。
下面使用shutdown 修改客戶端程序,在前面講過的使用select函數修改後的客戶端程序基礎上,修改很小一部分:
C++ Code
if (FD_ISSET(fd_stdin, &rset))
{
if (fgets(sendbuf, sizeof(sendbuf), stdin)== NULL)
{
stdineof = 1; //表示已經輸入完畢
/* 關閉sock的寫端,還能夠接收數據,在sock的緩沖區末尾添加一個FIN段 */
shutdown(sock, SHUT_WR);
}
else
{
writen(sock, sendbuf, strlen(sendbuf));
memset(sendbuf, 0, sizeof(sendbuf));
}
}
為了測試我們想要的效果,需要在select函數修改後的伺服器端程序的 134 行代碼之後,即writen 之前 sleep(4); 目的是接收到客戶端數據後搭帶不馬
上回射回去,睡眠4s 後在客戶端已經關閉連接的情況下再發送數據。
先運行伺服器端程序,再運行客戶端程序,在客戶端標准輸入,迅速敲入兩行:AAAAA\n BBBBB\n 然後按下ctrl+d 即fgets 會返回NULL,然後調用
shutdown關閉寫端,雖然伺服器端延時才發送數據,此時客戶端寫端已經關閉,但還是可以讀取到回射回來的數據,伺服器端最後得到一個FIN段,read
返回0,列印輸出 client close ,並且close(conn); 而客戶端在讀取服務端回射回來的兩次數據後,再次read 也返回0,故列印 server connect
close,break退出循環,進程順利退出。從下面的輸出還可以看出,因為延時的關系,所以不像以前那樣發射一行就回射一行。
simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$./echoser_select
recv connect ip=127.0.0.1 port=54010
fdsgfgd
gfedg
client close
...........................
simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$./echocli_select_shutdown
local ip=127.0.0.1 port=54010
fdsgfgd
gfedg
fdsgfgd
gfedg
1
gfedg
server connect close
如果我們將客戶端程序中的shutdown 改成了 close,那麼當延時後伺服器端發送數據給客戶端時,客戶端的讀端和寫端都已經關閉,第一次發AAAAA會
返回一個RST段,根據本文前面所說,再次發BBBBB直接產生SIGPIPE信號,默認會終止進程,但因為我們已經設置了忽略SIGPIPE信號,所以伺服器端進
程不會被終止,但客戶端也會出錯,因為回到while循環開頭,select阻塞等待時發現套接字的讀端已經關閉,所以不能再關心可讀事件了,select會返
回-1,錯誤碼是 EBADF: Bad File Descriptor。
⑨ linux close命令
你是指C語言的庫函數 close 吧? 它是用來關閉文件的,它的參數是調用 open 函數或者 create 函數成功後返回的文件句柄,是一個整型變數。用 close 的時候需要
#include <unistd.h>
舉例子:
/**************************** 源文件 eg.c ***********************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fnct.h>
/* 以上是調用 open 函數所需的頭文件 */
#include <unistd.h>
int main(int argc, char **argv) {
int fd;
if (argc == 1) {
沒有文件名,報錯,或者提供一個默認的文件名;
}
fd = open(argv[1], O_RDWR);
if(fd < 0) {
打開失敗,報錯,退出;
}
/* 成功打開文件之後 */
各種操作;
close(fd);
return(0);
}
⑩ Window和Linux下Socket的區別
socket編程在windows和linux下的區別有以下幾點銷租培:1)頭文件windows下winsock.h或winsock2.hlinux下netinet/in.h(大部分都在這兒),unistd.h(close函數在這兒),sys/socket.h(在in.h里已經包含了,可以省了)2)初始化windows下需要用WSAStartup啟動Ws2_32.lib,並且要用#pragmacomment(lib,"Ws2_32")來告知編譯器鏈接該lib。linux下不需要3)關閉socketwindows下closesocket()linux下close()4)類型windows下SOCKETlinux下int(我喜歡用long,這樣保證是4byte,因為-1我總喜歡寫成0xFFFF)5)獲取錯誤碼windows下getlasterror()/WSAGetLastError()linux下,未能成功執行的socket操作虧唯會返回-1;如果包含了errno.h,就會設置errno變數6)設置非阻塞windows下ioctlsocket()linux下fcntl(),需要頭文件fcntl.h7)send函數最後一個參數windows下一般設置為0linux下最好設置為MSG_NOSIGNAL,如果不設置,在發送出錯後有可能會導致程序退出8)毫秒級時間獲取windows下GetTickCount()linux下gettimeofday()9)多線程windows下包含process.h,使用_beginthread和_endthreadlinux下包含pthread.h,使用pthread_create和pthread_exit10)用IP定義一個地址(sockaddr_in的結構的區別)windows下addr_var.sin_addr.S_un.S_addrlinux下addr_var.sin_addr.s_addr而且Winsock里最後那個32bit的S_addr也有幾個以聯合(Union)的形式與它共享內存空間的成員變數(便於以其他方式賦值),而Linux的Socket沒有這個聯合,就是一個32bit的s_addr。遇到那種得到了是4個char的IP的形式(比如127一個,0一個,0一個和1一個共四個char),WinSock可以直接用4個S_b來賦值到S_addr里,而在Linux下,可以用邊向左移位(一下8bit,共四下)邊相加的方法賦值。11)異常處理linux下當連接斷開,還發數據的時候,不僅send()的返型戚回值會有反映,而且還會像系統發送一個異常消息,如果不作處理,系統會出BrokePipe,程序會退出。為此,send()函數的最後一個參數可以設MSG_NOSIGNAL,禁止send()函數向系統發送異常消息。