TCP/IP協議(面向連接協議)類似於打電話時,對方一定在手機附近並且此刻都在和對方進行通話。一定保證雙方都在線,才能進行數據傳輸。UDP/IP協議(無連接協議)就像郵箱,不保證對方一定在等你郵件且對方不在你也可以給對方發送數據。實際上TCP協議、UDP協議,還有重要的TCP協議中的三次握手(建立連接)和四次揮手(關閉連接)等在網上也都解釋得非常詳細了,所以我就不多說了。
Server端程序代碼:
/*
* 服務器端 Server.c
*
*/
#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#define BUFFSIZE 1024
int main(int argc, char**argv)
{
int Ret;
WSADATA wsaData;
SOCKET ListeningSocket;
SOCKET NewConnection;
SOCKADDR_IN ServerAddr;
SOCKADDR_IN ClientAddr;
int ClientAddrLen = sizeof(ClientAddr);
unsigned short Port = 5150;
char sendData[BUFFSIZE];
char recvData[BUFFSIZE];
if((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
{
printf("WSASTARTUP_ERROR: %d\n", Ret);
return 0;
}
//創建一個套接字來監聽客戶機連接
if((ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
printf("SOCKET_ERROR: %d\n", INVALID_SOCKET);
return 0;
}
/*
* 填充SOCKADDR_IN結構,這個結構將告知bind我們想要在5150端口監聽所有接口上的連接
*/
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(Port); //將端口變量從主機字節順序轉換位網絡字節順序
ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//使用bind將這個地址信息和套接字綁定起來
if(bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
{
printf("BIND_ERROR: %d\n", SOCKET_ERROR);
return 0;
}
//監聽客戶機連接。這里使用5個backlog
if(listen(ListeningSocket, 5) == SOCKET_ERROR)
{
printf("LISTEN_ERROR: %d\n", SOCKET_ERROR);
return 0;
}
//連接到達時,接受連接
printf("正在接受連接...");
if((NewConnection = accept(ListeningSocket, (SOCKADDR *)&ClientAddr, &ClientAddrLen)) == INVALID_SOCKET)
{
printf("ACCPET_ERROR: %d\n", INVALID_SOCKET);
closesocket(ListeningSocket);
return 0;
}
printf("檢測到一個連接: %s 端口:%d\n", inet_ntoa(ClientAddr.sin_addr), ntohs(ClientAddr.sin_port));
//聊天
while(true)
{
//接收數據
Ret = recv(NewConnection, recvData, BUFFSIZE, 0);
if(Ret > 0)
printf("C.C.: %s\n", recvData);
else if(Ret < 0)
printf("RECV_ERROR: %d\n", SOCKET_ERROR);
else
{
printf("對方退出程序,聊天結束!");
break;
}
//發送數據
printf("\n魯魯:");
scanf("%s", sendData);
if(strcmp(sendData, "quit") == 0) //退出
break;
if(send(NewConnection, sendData, BUFFSIZE, 0) == SOCKET_ERROR)
{
printf("消息發送失敗!\n");
break;
}
}
//從容關閉
shutdown(NewConnection, SD_BOTH);
//完成新接受的連接后,用closesocket API關閉這些套接字
closesocket(NewConnection);
closesocket(ListeningSocket);
//應用程序完成對接的處理后,調用WSACleanup
if(WSACleanup() == SOCKET_ERROR)
{
printf("WSACLEANUP_ERROR: %d\n", WSAGetLastError());
return 0;
}
system("pause");
return 0;
}
Client端程序代碼:
/*
* 客戶端 Client.c
*
*/
#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#define BUFFSIZE 1024
int main(int argc, char**argv)
{
int Ret;
WSADATA wsaData;
SOCKET s;
SOCKADDR_IN ServerAddr;
unsigned short Port = 5150;
char sendData[BUFFSIZE];
char recvData[BUFFSIZE];
if((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
{
printf("WSASTARTUP_ERROR: %d\n", Ret);
return 0;
}
if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
printf("SOCKET_ERROR: %d\n", INVALID_SOCKET);
return 0;
}
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(Port);
ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.101");// 這里S_un.S_addr在不同的IDE中可能不一樣,然后IPv4地址使用該程序所運行在的PC上的IPv4地址
if((connect(s, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr))) == SOCKET_ERROR)
{
printf("CONNECT_ERROR: %d\n", SOCKET_ERROR);
closesocket(s);
return 0;
}
//Chat
while(true)
{
printf("\nC.C:");
scanf("%s", sendData);
if(strcmp(sendData, "quit") == 0) //quit
break;
if(send(s, sendData, BUFFSIZE, 0) == SOCKET_ERROR)
{
printf("消息發送失敗!\n");
break;
}
Ret = recv(s, recvData, BUFFSIZE, 0);
if(Ret > 0)
printf("魯魯: %s\n", recvData);
else if(Ret < 0)
printf("RECV_ERROR: %d\n", SOCKET_ERROR);
else
{
printf("對方退出程序,聊天結束!");
break;
}
}
shutdown(s, SD_BOTH);
closesocket(s);
if(WSACleanup() == SOCKET_ERROR)
{
printf("WSACLEANUP_ERROR: %d\n", WSAGetLastError());
return 0;
}
system("pause");
return 0;
}
運行結果:

對於所有可能出現的錯誤我都加上了會出錯的函數名前綴,以便於定位出現錯誤的位置。
其他的書上解釋得很詳細,就不暴露自己水平了233...
p.s.2018-05-13 15:10:46
AF_INET的原型是:
#define AF_INET 2
然后關於客戶端代碼中的inet_addr()可看一下我的這篇,其中有一段相關解釋。
