Johnnie’sWinsockTutorial

ორიგინალური გვერდი http://johnnie.jerrata.com/winsocktutorial/

 

თუ თქვენ შეგნებულად მივიდა ჩემი Winsock tutorial, თქვენ, სავარაუდოდ, ნაპოვნი იდეა საკუთარი პროგრამები კომუნიკაციის მეშვეობით ინტერნეტში, როგორც მომხიბლავი პერსპექტივა, როგორც მე მაქვს. ან, ალბათ, ვინმე იპოვა პერსპექტივა თანაბრად საინტერესო და თქვენ უკვე დაევალა შემოტანა ამ ხედვის რეალობად. ორივე შემთხვევაში, Winsock ქსელის სერვისი და ამ tutorial დაგეხმარებათ მისაღწევად თქვენი მიზნების კომერციული საწარმო, უბრალოდ შეისწავლოს სფეროში ქსელი პროგრამირების პირადი სარგებლობისათვის, ან რამე შორის.

აქ არის ის, რაც ჩვენ გააშუქებს:

  • შექმნა მოსმენის ბუდე: გეძლევათ პატარა ჯარი ქსელის ფუნქციები, შეგვიძლია ავაშენოთ პროგრამა, რომელიც მოთმინებით ელოდება შემომავალი კავშირები? (დიახ, შეგვიძლია.)
  • მიღების საკუთარი კავშირები: იმის გათვალისწინებით, რამდენიმე დამატებითი ფუნქციები, შეგვიძლია შევქმნათ პროგრამა, რომელიც წარმატებით კავშირები მოსმენის სერვერზე? (დიახ, შეგვიძლია.)
  • უფრო გაკვეთილები და ბმულები: რა რესურსი არსებობს, ზემოთ და მიღმა ამ tutorial? მე მონიშნეთ 3 რომ უნდა შევინარჩუნოთ თქვენ დაკავებული, ხოლო (მას შემდეგ, რაც თქვენ უკვე მონელებული ჩემი tutorial, რა თქმა უნდა :-).

მიუხედავად იმისა, რომ თქვენ შეიძლება იყოს დაინტერესებული, რომ მიაღწიოს, რომ awe-ინსპირირებით პუნქტი, რომელიც თქვენი განცხადება წარმატებით, რაც მისი პირველი კავშირი, უნდა იცოდეს, ცნებები უკან კოდი. ცდილობენ, რათა თავიდან ავიცილოთ უბრალოდ მანიპულირების მოცემული კოდი ვარიანტს თქვენი უშუალო საჭიროებებს და ამის ნაცვლად იდენტიფიცირება მოთხოვნებს თქვენი განაცხადი და მხოლოდ ამის შემდეგ განახორციელოს, რაც, როგორც ჩანს, საუკეთესო გამოსავალი. რომ საკმარისია ჩემი Zen, პროგრამული უზრუნველყოფა-შემუშავება რჩევა ახლა; მოდით, ნუ ზოგიერთი ქსელური პროგრამირების…

Feel free to download მთელი სამეურვეო კოდი ჩამონათვალი. გახსოვდეთ, რომ რაიმე კოდი წარმოდგენილი ეს გაკვეთილი უნდა იყოს დაკავშირებული Winsock ბიბლიოთეკა, როგორც წესი, wsock32.lib ან რამე ასეთივე დასახელდა. გარდა ამისა, როდესაც გამოყენებით კოდი ზუსტად ისე, როგორც წარმოადგინა სამეურვეო საკუთარი IDE (Dev-C++, Microsoft VC++, C++ Builder, და ა. შ.), აირჩიეთ აშენება Windows პროექტი WinMain() , რათა თავიდან ავიცილოთ შეცდომები.

შექმნა მოსმენის Socket

პროგრამები მომსახურება გარეთ მანქანები უწოდებენ სერვერები. სერვერზე პროგრამები მოუსმინეთ კლიენტებს ინიციალიზაციისას ერთი ან მეტი მოსმენის სოკეტების. როდესაც კლიენტი უკავშირდება ერთი ასეთი მოსმენის სოკეტების, სერვერზე იღებს შეტყობინება, Winsock, ადასტურებს კავშირი, და იწყება დისპეტჩერიზაციის და ჩაჭრა შეტყობინებები და ახალი კლიენტი. ალბათ ყველაზე მარტივი მეთოდი, რომელიც სერვერები სახელური მრავალჯერადი კლიენტებს არის spawn ახალი თემა თითოეულ კლიენტთან დაკავშირებით. ამ სერვერზე მოდელი ყველაზე ხშირად იყენებს დაბლოკვა სოკეტების, რომელიც პაუზის დროებით დაველოდოთ შემომავალი მონაცემების, ახალი კავშირი, და სხვა ქსელის მოვლენები. პირველ რიგში, მოდით იდენტიფიცირება გარკვეული სტრუქტურების ჩვენ უნდა ინიციალიზაცია დაბლოკვა ბუდე:

  • WSADATA: ეს სტრუქტურა არის მეორადი, რომ შეკითხვის ოპერაციული სისტემის ის ვერსია, Winsock ჩვენი კოდი მოითხოვს. განცხადება მოუწოდებს WSAStartup() ვრთავ სწორი Winsock DLL.
  • SOCKET: ობიექტი (ფაქტობრივად, ეს განისაზღვრება, როგორც u_int, unsigned integer, winsock.h—კარგი, ვიცი, smalltalk პარტიების) მიერ გამოყენებული პროგრამები შესანახად socket სახელური.
  • SOCKADDR_IN: პროგრამა იყენებს ამ სტრუქტურას უნდა მიუთითოთ, თუ როგორ socket უნდა იმოქმედონ. SOCKADDR_IN შეიცავს სფეროებში IP მისამართის და პორტის ნომერი:
struct sockaddr_in

{

 მოკლე sin_family; // ოქმი ტიპი

 u_short sin_port; // პორტის ნომერი socket

 struct in_addr sin_addr; // IP მისამართი

 char sin_zero[8]; // გამოუყენებელი

};

პირველი საველე ოქმი ტიპი, რომელიც, როგორც წესი, AF_INET (TCP/IP). როგორც მოსმენის socket არ არის დაინტერესებული, ქსელის მისამართი მანქანა, რომელშიც ის ცხოვრობს, Winsock ავტომატურად ანიჭებს IP მისამართის და პორტის ნომერი მოსმენის სოკეტების საფუძველზე შექმნა.

ჩვენ უნდა ავაშენოთ ჩვენი პირველი მოსმენით სერვერზე ზემოთ სტრუქტურები და პატარა არმია ქსელის ფუნქციები:

#include <windows.h>

#include <winsock.h>

#include <stdio.h>



#განსაზღვრავს NETWORK_ERROR -1

#განსაზღვრავს NETWORK_OK 0



ბათილად ReportError(int, const char *);





int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmd, int nShow)

{

 სიტყვა sockVersion;

 WSADATA wsaData;

 int nret;



 sockVersion = MAKEWORD(1, 1); // ჩვენ გვინდა, Winsock ვერსია 1.1





 // ჩვენ ვიწყებთ ინიციალიზაციისას Winsock

 WSAStartup(sockVersion, და wsaData);





 // შემდეგი, შექმნა მოსმენის socket

 SOCKET listeningSocket;



 listeningSocket = socket(AF_INET, // წავიდეთ მეტი TCP/IP

 SOCK_STREAM,  // ეს არის stream-ორიენტირებული socket

 IPPROTO_TCP); // გამოიყენოთ TCP ვიდრე UDP



 თუ (listeningSocket == INVALID_SOCKET)

{

 nret = WSAGetLastError(); // Get უფრო დეტალური შეცდომა

 ReportError(nret, "socket()"); // ანგარიში შეცდომა ჩვენი საბაჟო ფუნქცია



 WSACleanup(); // გამორთვა Winsock

 დაბრუნება NETWORK_ERROR; // დაბრუნება შეცდომა ღირებულება

}





 // გამოიყენოთ SOCKADDR_IN struct შეავსოთ მისამართზე ინფორმაცია

 SOCKADDR_IN serverInfo;



 serverInfo.sin_family = AF_INET;

 serverInfo.sin_addr.s_addr = INADDR_ANY; // მას შემდეგ, რაც ეს სოკეტი არის მოსმენის კავშირები,

  // ნებისმიერი ადგილობრივი მისამართი გააკეთებს

 serverInfo.sin_port = htons(8888); // გადაიყვანოთ რიცხვი 8888 ქსელში-byte მიზნით

 // და ჩადეთ პორტი სფეროში





 // სავალდებულოა socket ჩვენი ადგილობრივი სერვერის მისამართი

 nret = სავალდებულოა(listeningSocket, (LPSOCKADDR) და serverInfo, sizeof(struct sockaddr));



 თუ (nret == SOCKET_ERROR)

{

 nret = WSAGetLastError();

 ReportError(nret, "bind()");



WSACleanup();

 დაბრუნება NETWORK_ERROR;

}





 / / , რათა socket მოუსმინეთ

 nret = მოუსმინეთ(listeningSocket, 10); // Up to 10 კავშირები შეიძლება ველოდოთ ნებისმიერ

 // ერთი დრო უნდა მიიღოს()'ედ



 თუ (nret ==SOCKET_ERROR)

{

 nret = WSAGetLastError();

 ReportError(nret, "listen()");



WSACleanup();

 დაბრუნება NETWORK_ERROR;

}





 // დაველოდოთ კლიენტი

 SOCKET theClient;



 theClient = მიიღოს(listeningSocket,

 NULL, // არააუცილებელი, მისამართი SOCKADDR_IN struct

 NULL); // არააუცილებელი, მისამართი ცვლადი, რომელიც შეიცავს

 // sizeof ( struct SOCKADDR_IN )



 თუ (theClient == INVALID_SOCKET)

{

 nret = WSAGetLastError();

 ReportError(nret, "მიიღოს()");



WSACleanup();

 დაბრუნება NETWORK_ERROR;

}





 // გააგზავნოთ და მიიღოთ კლიენტს და საბოლოოდ,

closesocket(theClient);

closesocket(listeningSocket);





 // გამორთვა Winsock

WSACleanup();

 დაბრუნება NETWORK_OK;

}





ბათილად ReportError(int errorCode, const char *whichFunc)

{

 char errorMsg[92]; // აცხადებენ, ბუფერული გამართავს

 // გენერირებული შეცდომა



 ZeroMemory(errorMsg, 92); // ავტომატურად NULL-შეწყვიტოს string



 // შემდეგი ხაზი ასლები ფრაზა, whichFunc string, და რიცხვი errorCode შევიდა ბუფერული

 sprintf(errorMsg, "ზარი %s დაბრუნდა შეცდომა %d!", (char *)whichFunc, errorCode);



 MessageBox(NULL, errorMsg, "socketIndication", MB_OK);

}

ერთი რამ, თქვენ მაშინვე შეამჩნევთ შესახებ კოდი არის თანხის ძალისხმევა შევიდა შემოწმების შეცდომა. როდესაც შეცდომა ხდება, კოდი იძენს კონკრეტული შეცდომის კოდი ერთად WSAGetLastError() და მაღაზიები შედეგი nret. შეცდომა კოდი არის მაშინ გაგზავნილი ერთად სიმებიანი მიუთითებს, სახელი ვერ ფუნქცია საბაჟო ფუნქცია სახელად ReportError(). იქ, შეცდომა არის აშენებული და აჩვენა მომხმარებელს ზარი MessageBox(), რომელიც არის ნაწილი სტანდარტული WinAPI. მაგალითად, ჰქონდა მოუსმინეთ() ვერ შეცდომა კოდი 10093 (განისაზღვრება, როგორც WSANOTINITIALISED), მზა შეცდომა string იქნება “ზარის მოსასმენად() დაბრუნდა შეცდომა 10093!”. თქვენ, გონივრული შემქმნელი, მაშინ ეძებოთ კოდი და აღმოაჩენთ, რომ შეცდომა მოხდა იმიტომ, რომ წარმატებული ზარი WSAStartup() ჯერ კიდევ არ ყოფილა.

Aleksandar პავლოვის გაფართოვდა ეს ReportError() მოიცავს აღწერილობა დაახლოებით ათეული გავრცელებული socket შეცდომები. გამოყენებით მისი განახლებული ვერსია, თქვენ აღარ უნდა საძიებელი რა კოდი იმას ნიშნავს, და თქვენი პროგრამით ხდება ბევრად უფრო მოსახერხებელი ძალიან ცოტა ძალისხმევა თქვენი მხრიდან.

ასევე არის განსაზღვრავს იყიდება NETWORK_ERROR და NETWORK_OK. ეს შეიძლება იყოს სასარგებლო, როდესაც შემოწმების დაბრუნება არც საკუთარი ქსელის ფუნქციები. თუ თქვენი ფუნქციები დაბრუნდა ერთი ამ ღირებულებების ნომრებზე ფუნქცია შეიძლება შეასრულოს მარტივი თანასწორობის ტესტი გამოავლინოს ნებისმიერი შეცდომები: თუ (myNetworkingFunction() == NETWORK_ERROR) {…}. ნომრებზე ფუნქცია შეიძლება შემდეგ მიიღოს კონკრეტული კოდი WSAGetLastError() და handle შეცდომა შესაბამისად. საბოლოო ჯამში, ახორციელებს კარგი შეცდომა გატარება სქემა ახლა დაგიზოგავთ თქვენ ბევრი დღის ან კვირის განვითარების დროს, როგორც თქვენ მყისიერად ვიცი რატომ, თქვენი პროგრამა ვერ შეძლო.

გარდა იმისა, რომ დაბრუნების ახალი კლიენტი კავშირი, მიიღოს() საშუალებას სერვერზე ამონაწერი ინფორმაცია კლიენტის შესახებ, ვიდრე მეშვეობით მეთოდები მოითხოვს დამატებითი ფუნქცია ზარები ან დრო (რომელიც შეიძლება გახდეს საკითხი ამ თამაშის სერვერები, სადაც სიჩქარე მიიღოს loop განსაკუთრებით კრიტიკული). ისარგებლოს ამ ფუნქციონირება, გაივლის მისამართი sockaddr_in struct როლებში რათა sockaddr მაჩვენებელი, ანუ (LPSOCKADDR) და aSockaddrInStructure. გარდა ამისა, განვაცხადო, მთელი ცვლადი, მითითებული ღირებულება int, რომ sizeof, რომ sockaddr struct, და აკეთებს მისამართი რიცხვი, როგორც მესამე პარამეტრი. თუ მისამართი, ინფორმაცია უნდა იყოს დაბრუნდა მას შემდეგ, რაც ფუნქცია ზარი, სიგრძის პარამეტრი უნდა იყოს.

jdarnold გვაფრთხილებს, რომ არ მჯერა MSDN დაკავშირებით დოკუმენტაციის ეს მესამე პარამეტრი: “MSDN docs გულისხმობს, თქვენ არ უნდა გაიაროს addrlen, რომ ეს არის პირველი სურვილისამებრ გამომავალი პარამეტრი, მაგრამ ისინი არასწორია. შემომავალი ის ამბობს, რამდენი ბაიტი არიან sockaddr buffer, და გამავალი [Winsock] ავსებს რამდენი [Winsock] გამოყენებული. თუ თქვენ გაივლის ნულოვანი როგორც len, [Winsock] არ შეეხოთ buffer.”

ეს არ არის ბევრი სერვერი, რადგან მას ელოდება მხოლოდ ერთი მომხმარებლის დაკავშირება და შემდეგ ეგრევე ირთვება, მაგრამ ეს არის ყველაზე ძირითადი დიზაინი. უბრალოდ გარკვევა რამ, ზარი WSAStartup() მოყვება სიტყვა მიუთითებს, თუ რა ვერსია გსურთ ჩატვირთვა (ამ შემთხვევაში ეს არის 1.1) და მისამართი WSADATA სტრუქტურა. შემდეგი, ჩვენ დაფარავს როგორ აკავშირებს სხვა კომპიუტერები.

მიღების საკუთარი კავშირები

შექმნა socket დაკავშირება ვინმე იყენებს ყველაზე იგივე ფუნქციები, გარდა HOSTENT struct:

  • HOSTENT: სტრუქტურა, მეორადი, რომ გითხრათ, სოკეტი, რომელიც კომპიუტერული და პორტის დაკავშირება. ამ სტრუქტურებში ხშირად, როგორც ჩანს, LPHOSTENT ცვლადები, რომლებიც მხოლოდ მითითებას HOSTENT სტრუქტურებში. როგორც თქვენ კოდი Windows, თქვენ ზოგადად ნახავთ, რომ ნებისმიერი მონაცემთა ტიპი წინ უძღოდა LP აღნიშნავს, რომ ტიპი არის რეალურად მომცეთ “ბაზა” ტიპი (მაგალითად, LPCSTR არის მომცეთ C string, ასევე ცნობილია, როგორც char *).

ასე რომ, მოდით, მიიღოს უფლება კოდი:

#include <windows.h>

#include <winsock.h>

#include <stdio.h>



#განსაზღვრავს NETWORK_ERROR -1

#განსაზღვრავს NETWORK_OK 0



ბათილად ReportError(int, const char *);





int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmd, int nShow)

{

 სიტყვა sockVersion;

 WSADATA wsaData;

 int nret;



 sockVersion = MAKEWORD(1, 1);





 // ვრთავ Winsock, როგორც ადრე

 WSAStartup(sockVersion, და wsaData);





 // Store ინფორმაცია სერვერზე

 LPHOSTENT hostEntry;



 hostEntry = gethostbyname("www.yahoo.com"); // სასურველი სერვერის მიერ მისი სახელი;

 // კიდევ ერთი ვარიანტი:gethostbyaddr()



 თუ (!hostEntry)

{

 nret = WSAGetLastError();

 ReportError(nret, "gethostbyname()"); // ანგარიში შეცდომა, როგორც ადრე



WSACleanup();

 დაბრუნება NETWORK_ERROR;

}





 // შევქმნათ socket

 SOCKET theSocket;



 theSocket = socket(AF_INET, // წავიდეთ მეტი TCP/IP

 SOCK_STREAM, // ეს არის stream-ორიენტირებული socket

 IPPROTO_TCP); // გამოიყენოთ TCP ვიდრე UDP



 თუ (theSocket == INVALID_SOCKET)

{

 nret = WSAGetLastError();

 ReportError(nret, "socket()");



WSACleanup();

 დაბრუნება NETWORK_ERROR;

}





 // შეავსოთ SOCKADDR_IN struct ერთად მისამართიინფორმაცია

 SOCKADDR_IN serverInfo;



 serverInfo.sin_family = AF_INET;



 // ამ ეტაპზე, ჩვენ წარმატებით წაკითხვის თარიღი: სასიცოცხლო ინფორმაცია სერვერზე,

 // მათ შორის ჰოსთის, მაგალითი, და IP მისამართები. დაველოდოთ; როგორ შეიძლება ერთი

 // კომპიუტერული მქონდეს მისამართები, და ზუსტად რა არის შემდეგი ხაზი აკეთებს?

 // ნახოთ ახსნა ქვემოთ.



 serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);



 serverInfo.sin_port = htons(80); // ცვლილება ქსელი-byte მიზნით და

 // insert into პორტი სფეროში





 // სერვერთან დაკავშირება

 nret = დაკავშირება(theSocket,

 (LPSOCKADDR) და serverInfo,

 sizeof(struct sockaddr));



 თუ (nret == SOCKET_ERROR)

{

 nret = WSAGetLastError();

 ReportError(nret, "connect()");



WSACleanup();

 დაბრუნება NETWORK_ERROR;

}





 // წარმატებით დაკავშირებული!





 // Send/receive, მაშინ წმენდა:

closesocket(theSocket);

WSACleanup();

}





ბათილად ReportError(int errorCode, const char *whichFunc)

{

 char errorMsg[92]; // აცხადებენ, ბუფერული გამართავს

 // გენერირებული შეცდომა



 ZeroMemory(errorMsg, 92); // ავტომატურად NULL-შეწყვიტოს string



 // შემდეგი ხაზი ასლები ფრაზა, whichFunc string, და რიცხვი errorCode შევიდა ბუფერული

 sprintf(errorMsg, "ზარი %s დაბრუნდა შეცდომა %d!", (char *)whichFunc, errorCode);



 MessageBox(NULL, errorMsg, "socketIndication", MB_OK);

}

ყველაზე რთული ხაზი ჩამონათვალი არის შემდეგი:

serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);

იმიტომ, რომ იგი ასრულებს რამდენიმე ოპერაციები—ერთი მათგანი შედარებით დამალული—ერთდროულად. ავიღოთ ის გარდა ეტაპობრივად:

“H_addr_list წევრი HOSTENT struct, ძირითადად, განისაზღვრება, როგორც char **h_addr_list, რომელიც მასივი სიმები, ან char *’s. gethostbyname() განსაზღვრული და გადაწერა ყველა ცნობილი მისამართები სერვერზე შევიდა ამ სიაში. თუმცა, ეს კონცეფცია სხვადასხვა მისამართები ფუნდამენტურად აზრი? სინამდვილეში, ეს ასეა. თქვენს კომპიუტერში, ფაქტობრივად, უკვე მასივი ზოგადი ქსელის მისამართები. თქვენი ინტერნეტ მისამართი შეიძლება იყოს 205.182.67.96, თქვენი LAN მისამართი შეიძლება იყოს 10.0.0.2, და ყველა კომპიუტერი, რომელიც ფანჯრები არის დამონტაჟებული, ბუნებრივია, აქვს “loopback” მისამართი 127.0.0.1, გამოიყენება კომპიუტერული იხილეთ თავად ლოკალურ ქსელში. იგივე კონცეფცია ეხება სფეროში ინტერნეტ მისამართები ან IP-ს, რის გამოც სიაში არის საჭირო, ვიდრე საცავი ერთი მისამართი. გაითვალისწინეთ, რომ სასურველი მისამართი, რომ არის, ყველაზე ხელმისაწვდომი მისამართი, ყოველთვის გადაწერა შევიდა პირველი ელემენტი სიაში, რასაც მოჰყვა მეორე ამჯობინა ან სხვა მისამართები.

რა *hostEntry->h_addr_list აკეთებს? თქვენ ალბათ მიხვდები, რომ პატივისცემის ნიშნად ოპერატორი (*) გამოიყენება წვდომის ერთ მისამართი სიაში. თუმცა, ვერ უზრუნველყოფს კონკრეტული მაჩვენებელი, dereference ოპერაცია ავტომატურად ავლენს პირველი, სასურველი მისამართი. რომ კონკრეტული მონაკვეთის უდრის *hostEntry->h_addr_list[0], რომელიც არის გარანტირებული, რომ არსებობს მას შემდეგ, სერვერზე უნდა ჰქონდეს მინიმუმ ერთი მისამართი.

შემდეგი, char * დაბრუნდა dereferencing ოპერაცია არის მიცემული შევიდა in_addr * ან LPIN_ADDR. და ბოლოს, კიდევ ერთი პარადიგმები, ტერმინოლოგიური ოპერაცია ხორციელდება დაბრუნებას in_addr struct მოხსენიებული მაჩვენებელი, რომელიც შეიძლება მხოლოდ hold ერთი მისამართი. შედეგად in_addr struct არის მაშინ ენიჭება serverInfo.sin_addr. მომდევნო connect() იღებს ერთ მისამართი, როგორც პარამეტრი, როდესაც ფორმირების კავშირი სერვერზე.

თუ სერვერის IP მისამართი არის ცნობილი, მოქმედი HOSTENT შეიძლება იყოს მიღებული გამოყენების gethostbyaddr() (როგორც ეწინააღმდეგებოდა gethostbyname() გამოიყენება წინა ჩამონათვალი):

LPHOSTENT hostEntry;

in_addr iaHost;



iaHost.s_addr = inet_addr("204.52.135.52");



hostEntry = gethostbyaddr((const char *)&iaHost, sizeof(struct in_addr), AF_INET);



თუ (!hostEntry)

{

 // სახელური შესაბამისად

}

ამ შემთხვევაში, inet_addr() არის გამოყენებული ასლი სიმებიანი აღმნიშვნელი IP მისამართი პირდაპირ in_addr struct. ამის შემდეგ, მისამართი struct არის მიცემული შევიდა const char * როგორც ამას gethostbyaddr(). ორივე მეთოდი არის მოხსენიებული, როგორც მოგვარებაში სერვერის მისამართი, რადგან Winsock ბრუნდება სრული მისამართი ჩანაწერები ნაწილობრივი ინფორმაცია.

რამდენიმე დამატებითი შენიშვნები: port 80 იყო გამოყენებული, უბრალოდ იმიტომ, რომ ინტერნეტ ვებ გვერდი გზავნილები მოხდეს მეტი, რომ პორტი. თუ იყო გაგზავნას სიმებიანი სერვერზე მოთხოვნის კონკრეტული ფაილი და ცდილობენ მიიღონ რაღაც უკან, ნეტავ, აქვს ძალიან მარტივი web browser. რა თქმა უნდა, რომ ტექსტი უნდა შეიცავდეს სრულ HTTP ბრძანება. ეს არის დიდი, რომ ჩვენ შეიძლება მოუსმინოს და დაკავშირება სხვა კომპიუტერები, მაგრამ საკომუნიკაციო ასევე მოიცავს გაგზავნის და მიღების.

გაგზავნის და მიღების

გაგზავნის სიფრთხილით, მოხერხებულად საკმარისი, send() ფუნქცია:

int გაგზავნას(

 SOCKET s,

 const char * შორს buf,

 int len,

 int დროშები

);

ძირითადად თქვენ, რომ ასლი, რაც უნდოდა, into a buffer და გამოყენება send() ფუნქცია დაკავშირებულია ბუდე, რათა მონაცემების წასვლა სხვა დასასრული:

char buffer[256]; // გამოცხადების stack buffer

char *ბუფერული = new char[256]; // ან ბევრი



ZeroMemory(ბუფერული, 256);

strcpy(ბუფერული, "ვიტყვი, ეს არის მნიშვნელოვანი მონაცემები.");



nret = გაგზავნას(theSocket,

buffer,

 strlen(ბუფერული), // გაითვალისწინოთ, რომ ეს განსაზღვრავს სიგრძეზე სიმებიანი; არ

 // ზომა მთელი ბუფერული

 0); // ყველაზე ხშირად არის ნულოვანი, მაგრამ ვხედავ MSDN სხვა ვარიანტი



delete [] ბუფერი; // თუ და მხოლოდ თუ ბევრი დეკლარაცია იყო გამოყენებული,



თუ (nret == SOCKET_ERROR)

{

 // მიიღოს კონკრეტული კოდი

 // სახელური შესაბამისად

 დაბრუნება NETWORK_ERROR;

} else {

 // nret შეიცავს რაოდენობის ბაიტს გაგზავნილი

}

მიღება არის იგივე პროცესი, უკან:

char buffer[256]; // დასტის

char *ბუფერული = new char[256]; // ან ბევრი



nret = recv(theSocket,

buffer,

 256,//, სრული ზომა ბუფერული

0);



delete [] ბუფერი; // მანიპულირება buffer, მაშინ წაშალე, თუ და მხოლოდ თუ

 // ბუფერული იყო გამოყოფილი ბევრი



თუ (nret == SOCKET_ERROR)

{

 // მიიღოს კონკრეტული კოდი

 // სახელური შესაბამისად

 დაბრუნება NETWORK_ERROR;

} else {

 // nret შეიცავს რაოდენობის ბაიტს მიიღო

}

რა საინტერესოა, უნდა აღინიშნოს, რომ არსებობს ღილაკს პანელი Microsoft Outlook შეაფასა “გაგზავნას/Recv.” “მიიღოს” შემოკლებით “Recv” , უბრალოდ, უნდა უზრუნველყოს ღილაკს გამოიყურება უფლება, ან ეს programmer-ის ჩვევა საწყისი აკრეფა recv() იმდენჯერ? ფორმა საკუთარი შეთქმულების თეორიები (კიდევ ერთხელ, კარგი smalltalk პარტიები).

ეს არის სადაც მე შეუვარდნენ პატარა პრობლემა, როდესაც წერა ჩემი საკუთარი Winsock პროგრამები. უბრალოდ გამოყენებით recv() არის დიდი, როდესაც თქვენ იცით ზუსტად რამდენი მონაცემები თქვენ გექნებათ მიღება (ასეთი, როგორც თამაში, სადაც პირველი ბაიტი შეიძლება ბრძანება და მომდევნო byte იყოს პარამეტრი და ა. შ.), მაგრამ როცა არ იცი, რა ვქნათ? თუ მონაცემები თქვენ მიღების შეწყვეტისას მიერ newline ხასიათი (საერთო პრობლემა Java კლიენტებს საუბარი C სერვერები), თქვენ შეგიძლიათ დაწეროთ readLine() ფუნქციის ხელში ყველაფერი, რომ ხასიათი. აი რა მე:

char * readLine()

{

 vectortheVector;

 char buffer;

 int bytesReceived;



 while (true)

{

 bytesReceived = recv(theSocket, და ბუფერული, 1, 0);

 თუ (bytesReceived <= 0)

 return NULL;



 თუ (ბუფერული == '\n')

{

 char *pChar = new char[theVector.ზომა() + 1];

 memset(pChar, 0, theVector.ზომა() + 1);



 for (int f = 0; f < theVector.ზომა(); ვ++)

 pChar[f] = theVector[f];



 დაბრუნება pChar;

 } else {

theVector.push_back(ბუფერული);

}

}

}

ვექტორი არის გამოყენებული ნაცვლად მასივი, რადგან მისი შენახვა ფართი შეიძლება გაიზარდოს ავტომატურად ვარიანტს ხანგრძლივობა ხაზი. თუ recv() დააბრუნებს შეცდომის (მითითებულია bytesReceived მიმდინარეობს ნაკლებია, ვიდრე ნულოვანი), NULL დაბრუნდა. რადგან ეს არის შესაძლებლობა, მოუწოდებს ფუნქციები უნდა უზრუნველყოს, რომ string დაბრუნდა readLine() მოქმედებს ადრე გამოყენება. შიგნით loop, ერთი char არის მიღებული ბუდე და, თუ არ არის newline ხასიათი, დასძინა, რომ ვექტორი. თუ ეს არის newline ხასიათი, შინაარსი ვექტორი არის გადაწერილი შევიდა C სიმებიანი და დაბრუნდა. სიმებიანი არის დეკლარირებული უნდა იყოს ერთი char უფრო დიდი, ვიდრე ვექტორი და memset()’ted ნულოვანი ისე, რომ დაბრუნდა line ავტომატურად NULL-წყდება. დამთავრებული სიმები NULL ხელს უშლის უჩვეულო შეცდომები და, ზოგადად, კარგი პროგრამირების პრაქტიკა.

და არც წარმოადგენს ეს ჭკვიანურად გაუმჯობესებული ვერსია მხარდაჭერა backspaces და უნარი შეცვალოს newline ხასიათი მარტივად:

// კოდი თავდაპირველად დაწერილი და არც. შეცვლილია ოდნავ

// მხარდაჭერა MessageBox() API, რათა ლოგიკა უფრო იკითხება,

// გასწორება ინტერვალი, და დაამატეთ კომენტარი. გამოგზავნილია ნებართვით.



#განსაზღვრავს backKey '\b' // გამორთოთ backspaces, #define backKey NULL

#განსაზღვრავს newLine '\n'

#განსაზღვრავს endStr '\0'



char *readLine(SOCKET s)

{

 vectortheVector;

 char buffer;

 char *pChar;

 int bytesReceived;



 while (true)

{

 bytesReceived = recv(s, და ბუფერული, 1, 0);



 თუ (bytesReceived <= 0)

{

 MessageBox(NULL, "recv() დაბრუნდა არაფერი.", "socketIndication", MB_OK);

 return NULL;

}



 switch (ბუფერული)

{

 შემთხვევაში backKey: // სახელური უკუსვლის

 თუ (theVector.ზომა() > 0)

theVector.pop_back();

შესვენება;

 შემთხვევაში endStr: // If end of string char მიაღწია,

 შემთხვევაში newLine: // ან თუ end of line char მიაღწია,

 pChar = new char[theVector.ზომა() + 1];

 memset(pChar, 0, theVector.ზომა() + 1);



  for (int f = 0; f < theVector.ზომა(); ვ++)

 pChar[f] = theVector[f];

 დაბრუნება pChar;

შესვენება;

 default: // ნებისმიერი რეგულარული char

theVector.push_back(ბუფერული);

შესვენება;

}

}

}

არასამთავრობო ბლოკირება და ასინქრონული სოკეტების

სანამ ამ ეტაპზე ჩვენ უკვე ვსაუბრობთ დაბლოკვა სოკეტების, სადაც მოუწოდებენ ფუნქცია, როგორიცაა მიიღოს() ელოდება განუსაზღვრელი ვადით მომხმარებლის დაკავშირება. არასამთავრობო დაბლოკვა socket ბრუნდება მაშინვე, როდესაც ის განუცხადა, რომ რამე, არც წარმატებული შედეგი, შეცდომა, ან არაფერი (რაც მიუთითებს, რომ არ იქნება რაღაც, რომ მიიღოთ შემდეგ). მინუსი ეს ტიპი არის ის, რომ თქვენ უნდა ხელით შეკითხვის სოკეტი თუ შედეგი დადგა ყოველ ფუნქცია თქვენ დარეკეთ. თქვენ ვერ გაივლის კომპლექტი სოკეტების, რომ select() ფუნქცია, თუ რომელი პირობა არის მზად კითხვა, წერა, ან დაბრუნდა შეცდომები.

ფუნქციების გამოყენებით ასინქრონული სოკეტების ასევე დაბრუნებას დაუყოვნებლივ, მაგრამ თქვენ შეგიძლიათ მიუთითოთ წერილის გაგზავნას, თქვენი ფანჯრის პროცედურა, როდესაც გარკვეული მოვლენა მოხდა. მაგალითად, თქვენ შეგიძლიათ socket გამოგვიგზავნეთ SOCKET_GOTMSG გაგზავნა მაშინ, როდესაც ის იღებს რაიმე. როგორც წესი, ეს ჭკვიანი შემოწმება შეცდომები (რთული, მაგრამ აუცილებელი), როდესაც თქვენ მიიღებთ socket გაგზავნა, რათა თავიდან აიცილოს, რამაც ზედმეტი პრობლემები მოგვიანებით. პირველ რიგში, მოდით განვსაზღვროთ ზოგიერთი ფუნქცია ჩვენ ვიყენებთ, რათა შეიქმნას ასინქრონული ბუდე:

  • int WSAAsyncSelect ( SOCKET s, HWND hwnd, unsigned int wMsg, ლევენთ )
    ეს ფუნქცია გამოიყენება, რათა დადგინდეს socket როგორც ასინქრონული და ასოცირებული გაგზავნა იგი. s არის socket თქვენ მუშაობა. hwnd არის სახელური ფანჯარა, რომელიც მიიღებს გაგზავნა როდესაც ბუდე ქმნის ღონისძიება. wMsg არის გაგზავნა გსურთ გაუგზავნოთ თქვენი ფანჯარა პროცედურა (მაგალითად, არის SOCKET_GOTMSG გაგზავნა ზემოთ). “ლევენთ პარამეტრი იღებს ერთ ან მეტ დროშები რომ გითხრათ, სოკეტი, რომელიც მოვლენები, გამოაგზავნოთ თქვენი შეტყობინება. ზოგიერთი იმ დროშები:

    • FD_READ: Socket არის მზად, მიიღოს მონაცემები
    • FD_WRITE: ბუდე მზად არის გააგზავნოს მონაცემები
    • FD_ACCEPT: მეორადი სერვერები, ეს წერილი მიუთითებს, მომხმარებელი უკავშირდება
    • FD_CONNECT: გამოიყენება კლიენტის პროგრამა, ეს მესიჯი ეუბნება, სოკეტი აქვს უკავშირდება
    • FD_CLOSE: socket ახლახანს დაიხურა
  • WSAGETSELECTERROR ( LPARAM lparam )
    განსაზღვრავს, თუ socket დაბრუნდა შეცდომა. ტექნიკურად, ეს არ არის ფუნქცია, მაგრამ მაკრო (თქვენ ნამდვილად გენერირება smalltalk პარტიები ამ პატარა factoid).
  • WSAGETSELECTEVENT ( LPARAM lparam )
    კიდევ ერთი სასარგებლო მაკრო განსაზღვრული winsock2.h არის WSAGETSELECTEVENT(), რომელიც არის მეორადი, რომ ნახოთ ზუსტად რა socket გააკეთა.

ასე რომ, მოდით, შეიქმნა ასინქრონული ბუდე:

// ჩვენ ვიწყებთ შექმნით დროშა რომ Windows გამოიყენებს, რათა დაგვიკავშირდით, როდესაც რაღაც ხდება,

#განსაზღვრავს THERE_WAS_A_SOCKET_EVENT WM_USER + 100 // WM_USER არის ბაზა საბაჟო შეტყობინებები

// სადღაც ჩვენი ინიციალიზაციისას კოდი შემდეგ CreateWindow (), ჩვენ მოვუწოდებთ WSAAsyncSelect ()

WSAAsyncSelect ( theSocket, hwnd, THERE_WAS_A_SOCKET_EVENT, FD_READ | FD_WRITE | FD_CONNECT | ... );



// ეს ითარგმნება: Windows, გთხოვთ, დამიკავშირდეს THERE_WAS_A_SOCKET_EVENT დროშა, რომ მე

// წინასწარ განსაზღვრული როდესაც არსებობს მონაცემები, რომ წავიკითხე (FD_READ), ან როდესაც მე ვარ, უფასო მონაცემების

// (FD_WRITE), ან როდესაც მე წარმატებით დაკავშირებული ვინმე (FD_CONNECT), ან მაშინ, როდესაც ა.შ....

// ჩვენი ფანჯარა პროცედურა (ფუნქცია, რომელიც ამუშავებს ყველა შეტყობინებები, რომ Windows აგზავნის თქვენი app)

LRESULT WINAPI TheWindowProcedure ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )

{



 switch ( msg )

{

 შემთხვევაში THERE_WAS_A_SOCKET_EVENT:

 თუ ( WSAGETSELECTERROR ( lParam ) )

 { // თუ შეცდომა მოხდა,

 closesocket ( theSocket );

 WSACleanup (); // გამორთვა Winsock

 დაბრუნება NETWORK_ERROR;

}

 switch ( WSAGETSELECTEVENT ( lParam ) )

 { // თუ რა მოხდა ზუსტად?

 შემთხვევაში FD_READ:

 // მიღება მონაცემები

შესვენება;

 შემთხვევაში FD_WRITE:

 // დაწერეთმონაცემები

შესვენება;

 შემთხვევაში FD_CONNECT:

 // უბრალოდ დაკავშირებული სერვერზე

შესვენება;

 შემთხვევაში ... // იგივე კონფიგურაცია სხვა დროშები

შესვენება;

}

შესვენება;



 // სხვა შემთხვევაში განცხადებები ლოგიკა, რომელიც ამუშავებს სხვა Windows შეტყობინებები



}

}

გაითვალისწინეთ, რომ თქვენ ვერ განსაზღვრავს ერთი გაგზავნა თითოეული მოვლენა, როგორიცაა SOCKET_GOTMSG იყიდება FD_READ და შემდეგ SOCKET_CONNECTED იყიდება FD_CONNECT. ეს იმიტომ, რომ განმეორებითი ზარები WSAAsyncSelect () კონფიგურაცია თითოეული დროშა იქნება პასუხის ეფექტი ბოლო ზარი WSAAsyncSelect ().

უფრო გაკვეთილები და ბმულები

მე დავწერე ამ tutorial წლის დეკემბერში, 2000, და შვიდი ან იმდენად წლის შემდეგ არ მინახავს მუდმივი ნაკადი სია და გაუმჯობესება. მე იმედი მაქვს, რომ თქვენ სარგებლობდა კითხულობს, როგორც მე სარგებლობდა წერა: გმადლობთ, რომ სარგებლობთ Johnnie ის Winsock Tutorial. ზემოთ არის, მაგრამ მოკლე მიმოხილვა შესაძლებლობები, თქვენ შეიძლება მიაღწიოს მეშვეობით Winsock და სხვები არ გაკეთდა, ბევრად უკეთ, ვიდრე ჩემთვის საცდელი სპეციფიკა ამ თემაზე:

(ამოძრავეთ თქვენი მაუსის მეტი წიგნის ყდა მეტი ინფორმაცია.)
– See more at: http://johnnie.jerrata.com/winsocktutorial/#sthash.IMglbIzP.dpuf

Home | Clip Art World | Free Cliparts | |