“對圖中的那些函數,我這裏稍加解釋壹下。”
int?WSAStartup(WORD?wVersionRequested,?LPWSADATA?lpWSAData);?功能是初始化Windows?Socket?Dll,在Windows下必須使用它。
參數:
“wVersionRequested”表示版本,可以是1.1、2.2等;
“lpWSAData”指向WSADATA數據結構的指針。
int?socket(int?family,?int?type,?int?protocol);?
功能是建立Socket,返回以後會用到的Socket值。如果錯誤,返回-1。
參數:
“int?family”參數指定所要使用的通信協議,取以下幾個值:AF_UNIX(Unix內部協議)、AF_INET(Internet協議)、AF_NS?Xerox(NS協議)、AF_IMPLINK(IMP連接層),在Windows下只能把“AF”設為“AF_INET”;
“int?type”參數指定套接字的類型,取以下幾個值:SOCK_STREAM(流套接字)、SOCK_DGRAM?(數據報套接字)、SOCK_RAW(未加工套接字)、SOCK_SEQPACKET(順序包套接字);
“int?protocol”參數通常設置為0。
int?bind(int?sockfd,?struct?sockaddr?*my_addr,?int?addrlen);?
功能是把套接字和機器上壹定的端口關聯起來。
參數:
“sockfd”是調用socket()返回的套接字值;
“my_addr”是指向數據結構struct?sockaddr的指針,它保存妳的地址,即端口和IP地址信息;
“addrlen”設置為sizeof(struct?sockaddr)。?
int?listen(int?sockfd,?int?backlog);?功能是服務端監聽壹個端口,直到accept()。在發生錯誤時返回-1。
參數:
“sockfd”是調用socket()返回的套接字值;
“backlog”是允許的連接數目。大多數系統的允許數目是20,也可以設置為5到10。
int?connect(int?sockfd,?struct?sockaddr?*serv_addr,?int?addrlen);?功能是客戶端連接服務端監聽的端口。
參數:
“sockfd”是調用socket()返回的套接字值;
“serv_addr”保存著目的地端口和IP?地址的數據結構struct?sockaddr;
“addrlen”設置為sizeof(struct?sockaddr)。?
int?accept(int?sockfd,?void?*addr,?int?*addrlen);?功能是服務端接受客戶端的連接請求,並返回壹個新的套接字,以後服務端的數據傳輸就使用這個新的套接字。如果有錯誤,返回-1。
參數:
“sockfd”是和listen()中壹樣的套接字值;
“addr”是個指向局部的數據結構sockaddr_in的指針;
“addrlen”設置為sizeof(struct?sockaddr_in)。
int?send(int?sockfd,?const?void?*msg,?int?len,?int?flags);
int?recv(int?sockfd,?void?*buf,?int?len,?unsigned?int?flags);
功能是用於流式套接字或數據報套接字的通訊,我們數據的真正傳輸就由它們完成。
參數:
“sockfd”是發/收數據的套接字值;
“msg”指向妳想發送的數據的指針;
“buf”是指向接收數據存放的地址;
“len”是數據的長度;
“flags”設置為?0。
int?sendto(int?sockfd,?const?void?*msg,?int?len,?unsigned?int?flags,const?struct?sockaddr?*to,?int?tolen);
int?recvfrom(int?sockfd,?void?*buf,?int?len,?unsigned?int?flags,? struct?sockaddr?*from,?int?*fromlen);
功能和send、recv類似,不過是用於無連接數據報套接字的傳輸。
int?closesocket(int?sockfd)?功能是關閉套接字。
參數“sockfd”為要關閉的套接字值。
程序:
“這裏的目的是讓大家對Socket編程有個整體了解。不用怕,程序我會詳細解釋的,首先是服務端的程序。其流程是:
socket()→bind()→listen→accept()→recv()/send()→closesocket()?具體代碼如下:”
★
#include?<stdio.h>
#include?<winsock.h>
#pragma?comment(lib,"Ws2_32")
#define?MYPORT?830?/*定義用戶連接端口*/?
#define?BACKLOG?10?/*多少等待連接控制*/?
int?main()?
{
int?sockfd,?new_fd;?/*定義套接字*/ struct?sockaddr_in?my_addr;?/*本地地址信息?*/? struct?sockaddr_in?their_addr;/*連接者地址信息*/? int?sin_size; WSADATA?ws; WSAStartup(MAKEWORD(2,2),&ws);//初始化Windows?Socket?Dll //建立socket if?((sockfd?=?socket(AF_INET,?SOCK_STREAM,?0))?==?-1) {?//如果建立socket失敗,退出程序
printf("socket?error\n");?
exit(1);?
}? //bind本機的MYPORT端口 my_addr.sin_family?=?AF_INET;?/*?協議類型是INET?*/? my_addr.sin_port?=?htons(MYPORT);/*?綁定MYPORT端口*/? my_addr.sin_addr.s_addr?=?INADDR_ANY;/*?本機IP*/? if?(bind(sockfd,?(struct?sockaddr?*)&my_addr,?sizeof(struct?sockaddr))==?-1) {?//bind失敗,退出程序
printf("bind?error\n");?
closesocket(sockfd);
exit(1);?
} //listen,監聽端口 if?(listen(sockfd,?BACKLOG)?==?-1) {?//listen失敗,退出程序
printf("listen?error\n");?
closesocket(sockfd);
exit(1);?
}? printf("listen..."); //等待客戶端連接 sin_size?=?sizeof(struct?sockaddr_in);? if?((new_fd?=?accept(sockfd,?(struct?sockaddr?*)&their_addr,?&sin_size))?==?-1) {?printf("accept?error\n");?
closesocket(sockfd);
exit(1);
}? printf("\naccept!\n"); //有連接,發送ww0830字符串過去 if?(send(new_fd,?"ww0830\n",?14,?0)?==?-1)? {printf("send?error");
closesocket(sockfd);
closesocket(new_fd);?
exit(1);?
}? printf("send?ok!\n"); //成功,關閉套接字 closesocket(sockfd); closesocket(new_fd); return?0;}
對服務端程序的流程概括:
先是初始化Windows?Socket?Dll:?WSAStartup(MAKEWORD(2,2),&ws);?
然後建立Socket:?sockfd?=?socket(AF_INET,?SOCK_STREAM,?0)?
再bind本機的MYPORT端口:
my_addr.sin_family?=?AF_INET;?/*?協議類型是INET*/?
my_addr.sin_port?=?htons(MYPORT);/*?綁定MYPORT端口?*/?
my_addr.sin_addr.s_addr?=?INADDR_ANY;/*?本機IP*/?
bind(sockfd,?(struct?sockaddr?*)&my_addr,?sizeof(struct?sockaddr))?
接下來監聽端口:?listen(sockfd,?BACKLOG)?
如果有客戶端的連接請求,接收它:?new_fd?=?accept(sockfd,?(struct?sockaddr?*)&their_addr,?&sin_size)?
最後發送ww0830字符串過去:?send(new_fd,?"ww0830\n",?14,?0)?
收尾工作,關閉socket:?closesocket(sockfd);closesocket(new_fd);?”
編譯、執行,就會壹直監聽830端口
客戶端程序了。其流程是:
socket()→connect()→send()/recv()→closesocket()?比服務端更簡單吧!其實現代碼如下:”
★
#include?<stdio.h>
#include?<stdio.h>
#include?<winsock.h>
#pragma?comment(lib,"Ws2_32")
#define?PORT?830/*?客戶機連接遠程主機的端口?*/?
#define?MAXDATASIZE?100?/*?每次可以接收的最大字節?*/?
int?main(int?argc,?char?*argv[])?
{?
int?sockfd,?numbytes;?
char?buf[MAXDATASIZE];?
struct?sockaddr_in?their_addr;/*?對方的地址端口信息?*/?
if?(argc?!=?2)?
{?
//需要有服務端ip參數
fprintf(stderr,"usage:?client?hostname\n");?
exit(1);?
}?
WSADATA?ws;
WSAStartup(MAKEWORD(2,2),&ws);?//初始化Windows?Socket?Dll
if?((sockfd?=?socket(AF_INET,?SOCK_STREAM,?0))?==?-1){?
//如果建立socket失敗,退出程序
printf("socket?error\n");?
exit(1);?
}?
//連接對方
their_addr.sin_family?=?AF_INET;?/*?協議類型是INET?*/?
their_addr.sin_port?=?htons(PORT);/*?連接對方PORT端口?*/?
their_addr.sin_addr.s_addr?=?inet_addr(argv[1]);/*?連接對方的IP?*/?
if?(connect(sockfd,?(struct?sockaddr?*)&their_addr,sizeof(struct?sockaddr))?==?-1)
{?
//如果連接失敗,退出程序
printf("connet?error\n");?
closesocket(sockfd);?
exit(1);?
}?
//接收數據,並打印出來if?((numbytes=recv(sockfd,?buf,?MAXDATASIZE,?0))?==?-1)?
{?
//接收數據失敗,退出程序
printf("recv?error\n");?
closesocket(sockfd);?
exit(1);?
}?
buf[numbytes]?=?'\0';?
printf("Received:?%s",buf);?
closesocket(sockfd);?
return?0;?
}?
對客戶端程序的流程概括:
首先是初始化Windows?Socket?Dll:?WSAStartup(MAKEWORD(2,2),&ws);?
然後建立Socket:?sockfd?=?socket(AF_INET,?SOCK_STREAM,?0)?
接著連接服務器方:
their_addr.sin_family?=?AF_INET;?/*?協議類型是INET*/?
their_addr.sin_port?=?htons(PORT);/*?連接對方PORT端口?*/?
their_addr.sin_addr.s_addr?=?inet_addr(argv[1]);?/*?連接對方的IP?*/?
connect(sockfd,?(struct?sockaddr?*)&their_addr,sizeof(struct?sockaddr))?
連接成功就接收數據:?recv(sockfd,?buf,?MAXDATASIZE,?0)?
最後把收到的數據打印出來並關閉套接字:
printf("Received:?%s",buf);?closesocket(sockfd);?編譯結束後,運行服務端,然後。客戶端?服務端IP?回車
妳會看到服務端發來得數據。
那麽基本的點對點通信就沒問題了。只要兩臺機器同時包含服務端和客戶端,就可以互相通信了。
當然,妳也可以將服務端和客戶端分開做,專門壹個服務器負責用戶登錄和轉發消息。
流程如下:
A客戶端發登錄消息-----》服務器
服務器驗證發送用戶消息----》客戶端
A客戶端想發消息給B客戶端----》先發給服務端
服務器得到消息查詢B客戶端IP並轉發消息。(或者B客戶端循環發消息詢問服務器有無消息)
通信結束。