socket是進程通訊的一種方式,經過調用一些API能夠實現進程間通訊,創建鏈接以及收發信息的過程以下圖所示:ios
這些函數的用法以下:ubuntu
一、int socket(int protocolFamily, int type, int protocol); 返回描述符sockfd緩存
調用socket建立以後,返回的描述符存在於協議族空間中,但沒有一個具體的地址,必需要經過bind才行服務器
二、int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);socket
如, IPV4:函數
struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ }; struct in_addr{ uint32_t s_addr; // address in network byte order };
三、int listen(int sockfd, int backlog);服務器監聽函數ui
listen函數將socket變爲被動類型的, 等待客戶鏈接請求spa
四、int connect(int sockfd, const struct *addr, socklen_t addrlen);3d
五、int accept(int sockfd, struct sockaddr* addr, socklen_t *addrlen);unix
accept()成功返回一個SOCKET描述符,表示接收到的套接字的描述符。不然返回值INVALID_SOCKET。
六、ssize_t send(int sockfd, const void *buf, ssize_t len, int flags);
send將本身的數據copy到內核的send buffer,返回值爲[-1,size]:
七、ssize_t recv(int sockfd, void *buf, ssize_t len, int flags);
各參數意義基本與send相同,recv操做將內核中的數據拷貝到應用程序內部
返回值是[-1,size]:
八、int close(int fd);
一個echo小例子,客戶端向服務器發送什麼服務器就給客戶端返回什麼信息:
一、服務器代碼:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> // 基本系統數據類型,是系統的基本系統數據類型的頭文件 #include<sys/socket.h> #include<netinet/in.h> // 互聯網地址族 #include<arpa/inet.h> //IP地址轉換函數inet_pton #include<unistd.h> //close #include<iostream> int main(int argc, char** argv){ int socketfd, bindfd, connectfd; char buffer[4096]; struct sockaddr_in serverAddress; int sendSize, recvSize; //printf("================== create socket ======================\n"); // create socket socketfd = socket(AF_INET, SOCK_STREAM, 0); if(socketfd == -1){ printf("Create socket error:%s(errno:%d)\n", strerror(errno),errno); exit(0); } //printf("================== set address ========================\n"); //set server address memset(&serverAddress, 0, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; serverAddress.sin_port = htons(800); //serverAddress.sin_addr.s_addr = htonl(127.0.0.1); inet_pton(AF_INET, "127.0.0.1", &serverAddress.sin_addr); std::cout << serverAddress.sin_addr.s_addr << '\n'; //printf("================== bind address ========================\n"); // bind address to the socket bindfd = bind(socketfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)); if(bindfd == -1){ printf("bind socket error:%s(errno:%d)\n", strerror(errno),errno); exit(0); } //printf("================== listen ========================\n"); if(listen(socketfd, 10) == -1){ printf("listen socket error:%s(errno:%d)\n",strerror(errno),errno); exit(0); } printf("================== waiting connect ========================\n"); while(1){ sleep(2); // recvive a connect and accept connectfd = accept(socketfd, (struct sockaddr*)NULL, NULL); if(connectfd == -1){ printf("connet socket error:%s(errno:%d)\n",strerror(errno),errno); continue; } // receive data recvSize = recv(connectfd, buffer, 4096, 0); if(recvSize == -1){ printf("recvive data error:%s(errno:%d)\n",strerror(errno),errno); continue; } printf("%s\n", buffer); // send data sendSize = send(connectfd, buffer, 4096, 0); if(sendSize == -1){ printf("send data error:%s(errno:%d)\n",strerror(errno),errno); continue; } close(connectfd); } close(socketfd); }
二、客戶端代碼:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> // 基本系統數據類型,是系統的基本系統數據類型的頭文件 #include<sys/socket.h> #include<netinet/in.h> // 互聯網地址族 #include<arpa/inet.h> //IP地址轉換函數inet_pton #include<unistd.h> //close #include<iostream> int main(){ int socketfd, connectfd; int sendSize, recvSize; struct sockaddr_in serverAddress; char sendBuf[4096]; char recvBuf[4096]; printf("================== create socket ========================\n"); socketfd = socket(AF_INET, SOCK_STREAM, 0); if(socketfd == -1){ printf("create socket error:%s(error%d)\n", strerror(errno),errno); exit(0); } memset(&serverAddress, 0, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; serverAddress.sin_port = htons(800); //serverAddress.sin_addr.s_addr = htonl("127.0.0.1"); inet_pton(AF_INET, "127.0.0.1", &serverAddress.sin_addr); std::cout << serverAddress.sin_addr.s_addr << '\n'; connectfd = connect(socketfd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)); if(connectfd == -1){ printf("connect server error:%s(error(%d))\n", strerror(errno), errno); exit(0); } printf("send message to server:\n"); //std::cin >> sendBuf; fgets(sendBuf, 4096, stdin); //printf("%s\n", sendBuf); sendSize = send(socketfd, sendBuf, strlen(sendBuf), 0); if(sendSize == -1){ printf("send data error:%s(error%d)\n", strerror(errno), errno); exit(0); } printf("wait echo from server:\n"); sleep(2); recvSize = recv(socketfd, recvBuf, sizeof(recvBuf), 0); if(recvSize < 0){ printf("recvive echo error:%s(error%d)\n", strerror(errno), errno); exit(0); } printf("%s", recvBuf); close(socketfd); }
在這過程當中遇到的幾個問題:
一、(客戶端)errno 111:connection refused
這個問題說明客戶端沒有找到應該鏈接的端口,須要作到:
因爲我客戶端和服務器端口號不匹配,就出現了這個問題
二、(服務器)errno 14:bad address
accept()函數的第二個參數指的是一個接收返回結果的緩存區,表示的是本次鏈接的客戶端的地址,若是無所謂客戶端地址能夠寫爲NULL,而一旦給這個參數賦了非NULL得值,可是這個緩存區又不可寫的話,就會出現bad address報錯
三、(服務器)errno 107: transport endpoint is not connected.
這個問題,是因爲我服務器端的recv和send函數的第一個參數寫的本地socket描述符形成的,事實上,這應該是已鏈接的socket描述符