linux socket通信组件,Linux下socket简单通信
#define MAXLINE 4096
/**************程序流程*******************
1 用socket()函數(shù)創(chuàng)建一個socket
2 用bind()綁定到一個本地的地址,這樣其他的socket可以用connect()連接上去
3 用listen()指出愿意接收連接并指定進來的連接的隊列限制
4 用accept()函數(shù)來接收連接
*****************************************/
int main(int argc, char const *argv[])
{
int listenfd; //監(jiān)聽套接字描述符,管理客戶端連接到服務(wù)端的個數(shù)
int connfd; //連接套接字描述符,被send()和recv()函數(shù)所使用
struct sockaddr_in servaddr; //套接字地址結(jié)構(gòu)體
//結(jié)構(gòu)體具體定義見sockaddr_in.jpg
char buff[MAXLINE];
int n;
if((listenfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
//socket函數(shù)向系統(tǒng)申請一個通信端口 原型:int socket(int domain, int type, int protocol);
//domain@ PF_INET, AF_INET: Ipv4網(wǎng)絡(luò)協(xié)議; PF_INET6, AF_INET6: Ipv6網(wǎng)絡(luò)協(xié)議
//type@ SOCK_STREAM: 提供面向連接的穩(wěn)定數(shù)據(jù)傳輸,即TCP協(xié)議。SOCK_DGRAM: 使用不連續(xù)不可靠的數(shù)據(jù)包連接。
//protocal@ 傳輸協(xié)議編號,一般設(shè)置為0就好。
printf("create socket error: %s(errno:%d)\n",strerror(errno),errno);
//errno 為全局變量,包含在中,程序出錯時自動填充。錯誤號的源代碼定義:https://www.cnblogs.com/xrcun/p/3210889.html
//strerror()函數(shù)不是線程安全的,strerror_r()是線程安全的。
return 0;
}
memset(&servaddr,0,sizeof(servaddr)); //初始化結(jié)構(gòu)體
servaddr.sin_family = AF_INET; //設(shè)置地址家族,此處為IPv4
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//設(shè)置地址:servaddr.sin_addr.s_addr是ip地址,
//htonl()的作用:將主機的unsigned long值轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序(32位);作用相反的函數(shù)即把網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)化成主機序列為ntohl()函數(shù);htons()就是轉(zhuǎn)化為short類型的(16位兩字節(jié))
//機器上可能有多塊網(wǎng)卡,也就有多個IP地址,這時候你要選擇綁定在哪個IP上面,如果指定為INADDR_ANY,那么系統(tǒng)將綁定默認的網(wǎng)卡【即IP地址】
//servaddr.sin_addr.s_addr = inet_addr("192.168.81.130")手動設(shè)置服務(wù)器的IP地址;inet_addr()作用是將一個IP字符串轉(zhuǎn)化為一個網(wǎng)絡(luò)字節(jié)序的整數(shù)值,inet_ntoa()作用與其相反
servaddr.sin_port = htons(6666); //設(shè)置端口
if(bind(listenfd,(struct sockaddr*) &servaddr , sizeof(servaddr)) == -1){
//sockaddr的缺陷是:把目標(biāo)地址和端口信息混在一起了; sockaddr_in解決了sockaddr的缺陷,但是參數(shù)傳遞時需要類型轉(zhuǎn)換為sockaddr。
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
return 0;
}
if(listen(listenfd,10) == -1){
//監(jiān)聽端口后,被動套接字會等待連接,此時系統(tǒng)維護著兩個隊列,第一個隊列存放的是已建立連接的套接字(即完成三次握手后的),第二個隊列存放的是未建立連接的套接字(處在三次握手中的)。每次accept函數(shù)會將第一個隊列中的套接字返回,進行通信,以完成網(wǎng)絡(luò)傳輸。
//所以listen的第二個參數(shù)意味著這兩個隊列的總和大小
printf("listen socket error:%s(errno: %d)\n",strerror(errno),errno);
return 0;
}
printf("========== waiting for client's request =============\n");
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
while(1){
if( connfd == -1){
//等待客戶端連接,連接將加入到等待接受(accept())的隊列中
//第一個參數(shù)為本地(服務(wù)端監(jiān)聽描述符),第二個參數(shù)存有客戶端IP地址和端口號,第三個參數(shù)為sockaddr*指向區(qū)域的長度
//accept在有客戶端連接上來時會被阻塞
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
n = recv(connfd,buff,MAXLINE,0);
//buff:緩沖區(qū)名字 MAXLINE:緩沖區(qū)大小 最后一個參數(shù)(flags):
//通常flags設(shè)置為0,此時recv()函數(shù)讀取tcp 緩沖區(qū)中的數(shù)據(jù)到buf中,并從tcp 緩沖區(qū)中移除已讀取的數(shù)據(jù),
//如果把flags設(shè)置為MSG_PEEK,僅僅是把tcp 緩沖區(qū)中的數(shù)據(jù)讀取到buf中,沒有把已讀取的數(shù)據(jù)從tcp 緩沖區(qū)中移除,如果再次調(diào)用recv()函數(shù)仍然可以讀到剛才讀到的數(shù)據(jù)。
buff[n] = '\0';
//scanf之類的字符串處理函數(shù)會自動將緩沖區(qū)的最后一字節(jié)設(shè)置為 '\0',但是諸如read()等函數(shù)就不會自動設(shè)置
//TCP緩沖區(qū)大小一般分為三級,詳見 https://blog.csdn.net/nawenqiang/article/details/81503870
printf("recv msg from client: %s\n",buff);
memset(buff,0,sizeof(buff)/sizeof(char) ); //發(fā)送完一次后清空緩沖區(qū),為下一次發(fā)送做好準(zhǔn)備。
}
close(connfd);
close(listenfd);
return 0;
}
總結(jié)
以上是生活随笔為你收集整理的linux socket通信组件,Linux下socket简单通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ps流 转发_RTP协议全解析(H264
- 下一篇: linux 以某个用户执行,Linux