網絡編程的基本概念:
1. 基本結構
struct sockaddr{
unsigned short sa_family;
char sa_data[14];
};
struct sockaddr_in{
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr{
unsiged long s_addr;
};
說明:sockaddr結構是用於函數參數使用的,sockaddr_in 其實內部數據和sockaddr結構一樣,只不過定義不一樣,定義sockaddr_in結構只是為了編程是填入地址族,ip地址和端口方便,在調用套接字函數時,需要強制類型轉換為sockaddr。為什麼這麼做,估計是sockaddr結構定義的比較早,所以不忍丟棄。
2. 基本轉換函數
*網絡字節順序:網絡采用大尾方式,inter386采用小尾方式
*網絡數字轉換
htos host to network short
hotl host to network long
ntos network to host short
ntol network to host long
*網絡地址轉換
inet_addr() 將字符串型IP地址轉換為無符號long int
inet_ntoa() 將IP地址數字轉換為字符串
3. 基本套接字函數
這裡只說其中幾個比較重要的函數
socket(ip_family,data_type,protocol);
bind(socket, struct sockaddr, len);
指定一個本地的端口用來進行通信,使用本地ip和port填充結構
connect(socket,struct sockaddr,len);
任意指定一個未用端口,內部調用bind進行綁定,使用遠程ip和port填充結構
listen(socket,backlog);
backlog 未經處理的連接請求隊列中可以容納的最大數目。
accept(listen_socket,out struct sockaddr,len);
accept拿出listen函數放入等待隊列中的第一條消息進行處理,然後返回這個消息的管理套接字。
注意:在服務器端,函數listen會將在客戶端函數connect發來的請求排成隊列,然後交由accept來處理,因此函數accept返回客戶端通信套接字,並返回客戶端的ip地址,通信端口等信息;在客戶端,connect函數在內部任意指定一個未用端口,然後綁定,用於和服務器端通信。
accept如果接不到請求,會阻塞。
accept如果接到請求,TCP的3次握手過程已完成,後面就可以用send和recv函數發送和接受數據。
4. 代碼示例
客戶端代碼:
#include
#include
#include
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA WSData;
SOCKET ConnectSocket;
if (WSAStartup(MAKEWORD(2,2),&WSData) != 0)
{
printf("socket initial error !
");
return 1;
}
ConnectSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (ConnectSocket == SOCKET_ERROR)
{
printf("create socket error!
");
WSACleanup();
return 1;
}
sockaddr_in Client;
Client.sin_family = AF_INET;
Client.sin_addr.s_addr = inet_addr("192.168.8.21");
Client.sin_port = htons(4600);
// connect to server
if (connect(ConnectSocket,(sockaddr*)&Client,sizeof(Client)) != 0)
{
printf("connect error!
");
return 1;
}
// translate data
char SendBuf[100] = "hi";
send(ConnectSocket,SendBuf,lstrlenA(SendBuf)+1,0);
char RecvBuf[101];
recv(ConnectSocket,RecvBuf,lstrlenA(RecvBuf)+1,0);
printf("%s
",RecvBuf);
// close socket
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
服務器端代碼:
#include
#include
#include
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA wsaData;
SOCKET ListenSocket;
int iResult;
// 初始化socket
iResult = WSAStartup(MAKEWORD(2,2),&wsaData);
if(iResult != 0)
{
printf("WSAStartup failed:%d
",iResult);
return 1;
}
// 創建socket
ListenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET)
{
printf("Error at socket():%d
",WSAGetLastError());
WSACleanup();
return 1;
}
// band socket
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("192.168.8.21");
service.sin_port = htons(4600);
if (bind(ListenSocket,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR)
{
printf("bind() failed.
");
closesocket(ListenSocket);
return 1;
}
// listen socket
if (listen(ListenSocket,SOMAXCONN) == SOCKET_ERROR)
{
printf("listen() failed.
");
closesocket(ListenSocket);
return 1;
}
// accept a socket and use it recv or send
sockaddr_in Client;
SOCKET ConnectSocket;
int len = sizeof(sockaddr_in);
while (1)
{
ConnectSocket= accept(ListenSocket,(sockaddr*)&Client,&len);
char RecvBuf[100];
recv(ConnectSocket,RecvBuf,lstrlenA(RecvBuf)+1,0);
printf("%s
",RecvBuf);
char SendBuf[100] = "hello man !";
send(ConnectSocket,SendBuf,lstrlenA(SendBuf)+1,0);
}
// close socket
closesocket(ConnectSocket);
closesocket(ListenSocket);
WSACleanup();
return 0;
}