- IPv4套接口地址結構
- IPv4套接口地址結構通常也稱為“網際套接字地址結構”,它以“sockaddr_in”命名,定義在頭文件中
- LINUX結構下的常用結構,一般創建套接字的時候都要將這個結構里面的值進行初始化
-
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 */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ };
- socket函數
-
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);
//創建一個套接字用於通信 參數分析
domain:指定通信協議族(protocol family),常用取值AF_INET(IPv4)
type:指定socket類型, 流式套接字SOCK_STREAM,數據報套接字SOCK_DGRAM,原始套接字SOCK_RAW
protocol:協議類型,常用取值0, 使用默認協議
返回值:
成功: 返回非負整數,套接字;
失敗: 返回-1
-
- bind函數
-
int
bind(
int
sockfd,
const
struct
sockaddr *addr, socklen_t addrlen); //
addrlen--地址長度
綁定一個本地地址到套接字。綁定函數 參數: sockfd:socket函數返回的套接字 addr:要綁定的地址
-
- listen函數
-
int listen(int sockfd, int backlog);
listen函數應該用在調用socket和bind函數之后, 並且用在調用accept之前, 用於將一個套接字從一個主動套接字轉變成為被動套接字。
使得一個進程可以接受其它進程的請求,從而成為一個服務器進程。在TCP服務器編程中listen函數把進程變為一個服務器,並指定相應的套接字變為被動連接。
sockfd 一個已綁定未被連接的套接字描述符
backlog說明:
對於給定的監聽套接口,內核要維護兩個隊列:
1、已由客戶發出並到達服務器,服務器正在等待完成相應的TCP三路握手過程(SYN_RCVD狀態)
2、已完成連接的隊列(ESTABLISHED狀態)
但是兩個隊列長度之和不能超過backlog
-
- accept函數
-
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd:服務器套接字
addr:將返回對等方的套接字地址, 不關心的話, 可以設置為NULL
addrlen:返回對等方的套接字地址長度, 不關心的話可以設置成為NULL, 否則一定要初始化
-
- connect函數
-
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
建立一個連接至addr所指定的套接字
參數:
sockfd:未連接套接字
addr:要連接的套接字地址
addrlen:第二個參數addr長度
-
- inet_aton
- 將一個字符串的ip地址字節轉換成了一個一個32位的網絡序列IP地址
int inet_aton(const char *string, struct in_addr*addr);
string是表示的是要轉換的字符串ip地址,后面的是網絡序列ip地址
成功返回非0,不成功返回-1
- 將一個字符串的ip地址字節轉換成了一個一個32位的網絡序列IP地址
- 客戶端和服務器端練習(server\client)
- 分別建立兩個工程,一個server,一個client。然后先打開服務端,再打開客戶端進行測試,源代碼如下。
//服務器端 #pragma comment(lib,"ws2_32.lib") #include<iostream> #include<winsock2.h> #include<string> #include<stdio.h> using namespace std; int main(){ //創建套接字 WORD myVersionRequest; WSADATA wsaData; //包含系統所支持的winsocket版本信息 myVersionRequest = MAKEWORD(1, 1); //初始化版本1.1 int err; err = WSAStartup(myVersionRequest, &wsaData); if (!err){ cout << "已經打開套接字" << endl; } else{ //進一步綁定套接字 cout << "套接字未打開" << endl; return 0; } //創建可識別套接字 調用socket函數 SOCKET serSocket = socket(AF_INET, SOCK_STREAM, 0); //需要綁定的參數 SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//ip地址 addr.sin_port = htons(3000);//綁定端口 //將套接字綁定到指定的網絡地址 bind(serSocket, (SOCKADDR*)&addr, sizeof(SOCKADDR));//綁定完成 //監聽連接 listen(serSocket, 10); //第二個參數代表能夠接受的最多的連接數 //建立客戶端的連接 SOCKADDR_IN clientsocket; int len = sizeof(SOCKADDR); SOCKET serConn; //等待客戶端的連接 serConn = accept(serSocket, (SOCKADDR*)&clientsocket, &len); //返回一個數字 接受函數,不是-1就是接受連接成功 cout << "客戶端" << inet_ntoa(clientsocket.sin_addr) << "已連接" << endl; //客戶端已連接 while (1) { char sendBuf[100]; //發送的字節 sprintf_s(sendBuf, "server : welcome %s to server.", inet_ntoa(clientsocket.sin_addr)); //轉換函數,將二進制的ip序列轉換成char數組 //在對應的IP處並且將這行字打印到那里 send(serConn, sendBuf, strlen(sendBuf) + 1, 0); char receiveBuf[100]; //收到的字節 //接收客戶端傳來的信息 recv(serConn, receiveBuf, strlen(receiveBuf) + 1, 0); char* quit = "quit"; //如果客戶端傳來了quit信號,則服務端關閉,客戶端也關閉 if (!strcmp(receiveBuf, quit)) { break; } printf("%s\n", receiveBuf); } closesocket(serConn); //關閉 WSACleanup(); //釋放資源的操作 }
//客戶端 #pragma comment(lib,"ws2_32.lib") #include<winsock2.h> #include<iostream> #include<string> #include<conio.h> using namespace std; int main(){ WORD versionRequired; WSADATA wsaData; //包含系統所支持的WinStock版本信息 versionRequired = MAKEWORD(1, 1); //初始化版本1.1 int err; //注冊WinStock,返回狀態 err = WSAStartup(versionRequired, &wsaData);//協議庫的版本信息 if (!err) { //返回結果為0表示初始化失敗{ cout << LPSTR("客戶端套接字已經打開!\n"); } else{ //調用WSAGetLastError()查看錯誤信息 cout << ("客戶端套接字打開失敗:") << WSAGetLastError() << endl; return 0;//結束 } /* 創建套接字: 流式套接字: SOCK_STREAM , IPPROTO_TCP 數據報套接字: SOCK_DGRAM , IPPROTO_UDP */ SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //創建流式套接字 SOCKADDR_IN clientsock_in; //專門針對Internet 通信域的Winsock地址結構 clientsock_in.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //通過inet_addr結構指定套接字的主機IP地址 clientsock_in.sin_family = AF_INET; //指定協議家族:AF_INET clientsock_in.sin_port = htons(3000); //指定將要分配給套接字的傳輸層端口號:6000 //開始連接 int fail = connect(clientSocket, (SOCKADDR*)&clientsock_in, sizeof(SOCKADDR)); if (fail){ cout << "與服務端連接失敗!程序將退出..." << endl; _getch(); return 0; } string s; while (cin >> s){ char receiveBuf[100]; //接收數據 recv(clientSocket, receiveBuf, 101, 0); cout << receiveBuf << endl; //發送數據 send(clientSocket, s.c_str(), s.length() + 1, 0); if (s == "quit"){ break; } } closesocket(clientSocket);//關閉套接字 if (WSACleanup() == SOCKET_ERROR){ cout << "套接字關閉失敗:" << WSAGetLastError() << endl; } else{ cout << "套接字成功關閉." << endl; } _getch(); return 0; }
-
程序運行截圖
- 分別建立兩個工程,一個server,一個client。然后先打開服務端,再打開客戶端進行測試,源代碼如下。