linux网络编程-socket(2)
當(dāng)客戶端調(diào)用close函數(shù)的時(shí)候,服務(wù)器的read函數(shù)讀到的數(shù)據(jù)是0讀到文件結(jié)束通知,表示對(duì)端關(guān)閉了tcp連接
我們現(xiàn)實(shí)實(shí)現(xiàn)下面的功能:
?
?1、tcp客戶端從標(biāo)準(zhǔn)的輸入流中得到輸入數(shù)據(jù)發(fā)送到服務(wù)器,服務(wù)器收到數(shù)據(jù)之后,不做任何改變,將書法返回給客戶端,客戶端收到服務(wù)器的數(shù)據(jù)之后,在標(biāo)準(zhǔn)輸出流中輸出
?
上面代碼中PF_INET和AF_INET都是一樣的都是代碼tcp的協(xié)議族
tcp協(xié)議對(duì)應(yīng)的流式套接字,所以寫成sock_STREAM
第三個(gè)參數(shù)可以寫成IPPPOTO_TCP或者0都是可以的
第四個(gè)結(jié)構(gòu)體中服務(wù)器綁定的地址可以使用inet_addr函數(shù)將本機(jī)的點(diǎn)分十進(jìn)制的ip地址轉(zhuǎn)換成32位的網(wǎng)絡(luò)地址
也可以使用htonl函數(shù)將本機(jī)的任何地址轉(zhuǎn)換成32位的函數(shù),其中INADDR_ANY,表示本機(jī)的任何地址都可以
對(duì)于端口也必須是網(wǎng)絡(luò)字節(jié)序,所以需要使用htons將本機(jī)端口轉(zhuǎn)換成16位的無符號(hào)網(wǎng)絡(luò)字節(jié)端口
Read函數(shù)
????Ssize_t read(int fd,void *buf,size_t nbyte)
????Read函數(shù)是負(fù)責(zé)從fd中讀取內(nèi)容,當(dāng)讀取成功時(shí),read返回實(shí)際讀取到的字節(jié)數(shù),如果返回值是0,表示已經(jīng)讀取到文件的結(jié)束了,小于0表示是讀取錯(cuò)誤。
????如果錯(cuò)誤是EINTR表示在寫的時(shí)候出現(xiàn)了中斷錯(cuò)誤,如果是EPIPE表示網(wǎng)絡(luò)連接出現(xiàn)了問題。
?
Write函數(shù)
????Ssize_t write(int fd,const void *buf,size_t nbytes);
????Write函數(shù)將buf中的nbytes字節(jié)內(nèi)容寫入到文件描述符中,成功返回寫的字節(jié)數(shù),失敗返回-1.并設(shè)置errno變量。在網(wǎng)絡(luò)程序中,當(dāng)我們向套接字文件描述舒服寫數(shù)據(jù)時(shí)有兩種可能:
我們來看下服務(wù)器的代碼:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h>/* *定義一個(gè)宏,輸出錯(cuò)誤信息并且退出 */ #define ERR_EXIT(m) \do \{\perror(m);\exit(EXIT_FAILURE);\}while(0)int main(int argc, char *argv[]) {int serv_sock;struct sockaddr_in serv_addr;serv_sock = socket(AF_INET, SOCK_STREAM, 0);if (serv_sock == -1){ERR_EXIT("socket創(chuàng)建失敗");}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(9999);if((connect(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)))<0){ERR_EXIT("客戶端connect失敗");}char revbuf[1024];char sendbuf[1024];memset(revbuf,0,sizeof(revbuf));memset(sendbuf,0,sizeof(revbuf));while((fgets(sendbuf,sizeof(sendbuf),stdin)!= NULL)){write(serv_sock,sendbuf,strlen(sendbuf));read(serv_sock,revbuf,sizeof(revbuf));fputs(revbuf,stdout);//讀到多少數(shù)據(jù)就給客戶端返回多少字節(jié)的數(shù)據(jù) memset(sendbuf,0,sizeof(revbuf));memset(revbuf,0,sizeof(revbuf));}close(serv_sock);return 0; }
?服務(wù)器代碼:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h>/* *定義一個(gè)宏,輸出錯(cuò)誤信息并且退出 */ #define ERR_EXIT(m) \ do \ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0)int main(int argc, char *argv[]) {int serv_sock;int clnt_sock;struct sockaddr_in serv_addr;struct sockaddr_in clnt_addr;socklen_t clnt_addr_size;serv_sock = socket(AF_INET, SOCK_STREAM, 0);if (serv_sock == -1){ERR_EXIT("socket創(chuàng)建失敗");}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(9999);if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1){ERR_EXIT("bind失敗");}//SOMAXCON系統(tǒng)默認(rèn)的最大的客戶端的連接數(shù)據(jù) , (listen(serv_sock, 5)表示最大允許5個(gè)客戶端的連接 if (listen(serv_sock, SOMAXCONN) == -1){ERR_EXIT("listen失敗"); }clnt_addr_size = sizeof(clnt_addr);clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);if (clnt_sock == -1){ERR_EXIT("accept失敗"); }char revbuf[1024];while(1){memset(revbuf,0,sizeof(revbuf));int len = read(clnt_sock,revbuf,sizeof(revbuf)); //len讀到數(shù)據(jù)的字節(jié)長(zhǎng)度 if(len == 0){ //說明客戶端終止了數(shù)據(jù)的發(fā)送 break;}fputs(revbuf,stdout);//讀到多少數(shù)據(jù)就給客戶端返回多少字節(jié)的數(shù)據(jù) write(clnt_sock,revbuf,len);}close(clnt_sock);close(serv_sock);return 0; }void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }?
?
在ubuntu系統(tǒng)上執(zhí)行編譯:
gcc client.c -o client
執(zhí)行客戶端程序就是./client
編譯服務(wù)器程序
gcc server.c -o server
執(zhí)行服務(wù)器程序
./server
?上面的代碼只能對(duì)應(yīng)一個(gè)客戶端連接一個(gè)服務(wù)器,如果一個(gè)服務(wù)器要支持多個(gè)客戶端的請(qǐng)求,請(qǐng)看socke(37)章節(jié)的代碼
?
我們來看服務(wù)器的函數(shù):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <pthread.h> /* *定義一個(gè)宏,輸出錯(cuò)誤信息并且退出 */ #define ERR_EXIT(m) \ do \ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0)void*thread_exc(void* arg){pthread_detach(pthread_self()); //將線程設(shè)置成分離狀態(tài),避免僵尸線程 int clnt_sock = (int)arg;char revbuf[1024];while(1){memset(revbuf,0,sizeof(revbuf));int len = read(clnt_sock,revbuf,sizeof(revbuf)); //len讀到數(shù)據(jù)的字節(jié)長(zhǎng)度 if(len == 0){ //說明客戶端終止了數(shù)據(jù)的發(fā)送 break;}fputs(revbuf,stdout);//讀到多少數(shù)據(jù)就給客戶端返回多少字節(jié)的數(shù)據(jù) write(clnt_sock,revbuf,len);}close(clnt_sock); //記得關(guān)閉線程 }int main(int argc, char *argv[]) {int serv_sock;int clnt_sock;struct sockaddr_in serv_addr;struct sockaddr_in clnt_addr;socklen_t clnt_addr_size;serv_sock = socket(AF_INET, SOCK_STREAM, 0);if (serv_sock == -1){ERR_EXIT("socket創(chuàng)建失敗");}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(9999);if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1){ERR_EXIT("bind失敗");}//SOMAXCON系統(tǒng)默認(rèn)的最大的客戶端的連接數(shù)據(jù) , (listen(serv_sock, 5)表示最大允許5個(gè)客戶端的連接 if (listen(serv_sock, SOMAXCONN) == -1){ERR_EXIT("listen失敗"); }while(1){ //在while循環(huán)中一直等待客戶端的監(jiān)聽 clnt_addr_size = sizeof(clnt_addr);clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);if (clnt_sock == -1){ERR_EXIT("accept失敗"); }//每一個(gè)客戶端的請(qǐng)求都開啟一個(gè)線程進(jìn)行處理 pthread_t thread_id ;int ret;//將clnt_sock通過第三個(gè)參數(shù)傳遞到線程函數(shù)中 if((ret = pthread_create(&thread_id,NULL,thread_exc,(void*)clnt_sock)) != 0){ERR_EXIT("線程創(chuàng)建失敗");}}close(serv_sock);return 0; }void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }?
我們來看客戶端的函數(shù):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h>/* *定義一個(gè)宏,輸出錯(cuò)誤信息并且退出 */ #define ERR_EXIT(m) \do \{\perror(m);\exit(EXIT_FAILURE);\}while(0)int main(int argc, char *argv[]) {int serv_sock;struct sockaddr_in serv_addr;serv_sock = socket(AF_INET, SOCK_STREAM, 0);if (serv_sock == -1){ERR_EXIT("socket創(chuàng)建失敗");}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(9999);if((connect(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)))<0){ERR_EXIT("客戶端connect失敗");}char revbuf[1024];char sendbuf[1024];memset(revbuf,0,sizeof(revbuf));memset(sendbuf,0,sizeof(revbuf));while((fgets(sendbuf,sizeof(sendbuf),stdin)!= NULL)){write(serv_sock,sendbuf,strlen(sendbuf));read(serv_sock,revbuf,sizeof(revbuf));fputs(revbuf,stdout);//讀到多少數(shù)據(jù)就給客戶端返回多少字節(jié)的數(shù)據(jù) memset(sendbuf,0,sizeof(revbuf));memset(revbuf,0,sizeof(revbuf));}close(serv_sock);return 0; }?
?
對(duì)應(yīng)服務(wù)器的函數(shù):有一點(diǎn)很關(guān)鍵在創(chuàng)建線程的的執(zhí)行函數(shù)的入口處調(diào)用 pthread_detach(pthread_self()); //將線程設(shè)置成分離狀態(tài),避免僵尸線程?
第二在客戶端關(guān)閉連接,服務(wù)器read字節(jié)數(shù)為0的時(shí)候,記得關(guān)閉客戶端的連接
close(clnt_sock); //記得關(guān)閉線程
}
我們來看程序運(yùn)行的效果
服務(wù)器端收到了客戶端1和客戶端2的數(shù)據(jù)
?
?客戶端1:
客戶端2:
?
這里千萬不能將線程的地址傳遞進(jìn)行會(huì)存在多線程隱患的問題,千萬不能寫成(void*)&clnt_sock
void*thread_exc(void* arg){
pthread_detach(pthread_self()); //將線程設(shè)置成分離狀態(tài),避免僵尸線程
int clnt_sock = *(*int)arg;
這樣會(huì)存在多線程隱患的問題,當(dāng)?shù)谝粋€(gè)線程正在執(zhí)行thread_exc執(zhí)行int clnt_sock = *(*int)arg或者自己線程的sockid的時(shí)候,此時(shí)第二個(gè)線程創(chuàng)建了成功改變了sockid的值,第一個(gè)線程通過*(*int)arg獲得的sockid就是剛剛創(chuàng)建的第二個(gè)線程的。所以這里存在線程安全問題,所以不能使用指針傳遞,所以必須使用值傳遞
但是上面的代碼還存在一個(gè)小bug
就是將int類型強(qiáng)制轉(zhuǎn)換成了void*類型void*)clnt_sock)存在問題,例如在64位的系統(tǒng)上指針void*是8個(gè)字節(jié),int是4個(gè)字節(jié),存在轉(zhuǎn)換的問題,可以使用下面的方式進(jìn)行解決
?
程序的代碼修改如下所示:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <pthread.h> /* *定義一個(gè)宏,輸出錯(cuò)誤信息并且退出 */ #define ERR_EXIT(m) \ do \ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0)void*thread_exc(void* arg){pthread_detach(pthread_self()); //將線程設(shè)置成分離狀態(tài),避免僵尸線程 int clnt_sock = *((int*)arg);//記得關(guān)閉指針free(arg); char revbuf[1024];while(1){memset(revbuf,0,sizeof(revbuf));int len = read(clnt_sock,revbuf,sizeof(revbuf)); //len讀到數(shù)據(jù)的字節(jié)長(zhǎng)度 if(len == 0){ //說明客戶端終止了數(shù)據(jù)的發(fā)送 break;}fputs(revbuf,stdout);//讀到多少數(shù)據(jù)就給客戶端返回多少字節(jié)的數(shù)據(jù) write(clnt_sock,revbuf,len);}close(clnt_sock); //記得關(guān)閉線程 }int main(int argc, char *argv[]) {int serv_sock;int clnt_sock;struct sockaddr_in serv_addr;struct sockaddr_in clnt_addr;socklen_t clnt_addr_size;serv_sock = socket(AF_INET, SOCK_STREAM, 0);if (serv_sock == -1){ERR_EXIT("socket創(chuàng)建失敗");}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(9999);if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1){ERR_EXIT("bind失敗");}//SOMAXCON系統(tǒng)默認(rèn)的最大的客戶端的連接數(shù)據(jù) , (listen(serv_sock, 5)表示最大允許5個(gè)客戶端的連接 if (listen(serv_sock, SOMAXCONN) == -1){ERR_EXIT("listen失敗"); }while(1){ //在while循環(huán)中一直等待客戶端的監(jiān)聽 clnt_addr_size = sizeof(clnt_addr);clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);if (clnt_sock == -1){ERR_EXIT("accept失敗"); }//每一個(gè)客戶端的請(qǐng)求都開啟一個(gè)線程進(jìn)行處理 pthread_t thread_id ;int ret;//將clnt_sock通過第三個(gè)參數(shù)傳遞到線程函數(shù)中 int * p = (int*)malloc(sizeof(int));*p = clnt_sock;if((ret = pthread_create(&thread_id,NULL,thread_exc, p ))!= 0){ERR_EXIT("線程創(chuàng)建失敗");}}close(serv_sock);return 0; }void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/kebibuluan/p/7086423.html
總結(jié)
以上是生活随笔為你收集整理的linux网络编程-socket(2)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Node.js 博客实例(一)简单博客
- 下一篇: 如何使用CSS创建巧妙的动画提示框