Johnnie’sWinsockTutorial
ორ�?გ�?ნალურ�? გვერდ�? http://johnnie.jerrata.com/winsocktutorial/
თუ თქვენ შეგნებულად მ�?ვ�?და ჩემ�? Winsock tutorial, თქვენ, სავარაუდოდ, ნაპოვნ�? �?დეა საკუთარ�? პროგრამებ�? კომუნ�?კაც�?�?ს მეშვეობ�?თ �?ნტერნეტშ�?, როგორც მომხ�?ბლავ�? პერსპექტ�?ვა, როგორც მე მაქვს. ან, ალბათ, ვ�?ნმე �?პოვა პერსპექტ�?ვა თანაბრად სა�?ნტერესო და თქვენ უკვე დაევალა შემოტანა ამ ხედვ�?ს რეალობად. ორ�?ვე შემთხვევაშ�?, Winsock ქსელ�?ს სერვ�?ს�? და ამ tutorial დაგეხმარებათ მ�?საღწევად თქვენ�? მ�?ზნებ�?ს კომერც�?ულ�? საწარმო, უბრალოდ შე�?სწავლოს სფეროშ�? ქსელ�? პროგრამ�?რებ�?ს პ�?რად�? სარგებლობ�?სათვ�?ს, ან რამე შორ�?ს.
აქ არ�?ს �?ს, რაც ჩვენ გააშუქებს:
- შექმნა მოსმენ�?ს ბუდე: გეძლევათ პატარა ჯარ�? ქსელ�?ს ფუნქც�?ებ�?, შეგვ�?ძლ�?ა ავაშენოთ პროგრამა, რომელ�?ც მოთმ�?ნებ�?თ ელოდება შემომავალ�? კავშ�?რებ�?? (დ�?ახ, შეგვ�?ძლ�?ა.)
- მ�?ღებ�?ს საკუთარ�? კავშ�?რებ�?: �?მ�?ს გათვალ�?სწ�?ნებ�?თ, რამდენ�?მე დამატებ�?თ�? ფუნქც�?ებ�?, შეგვ�?ძლ�?ა შევქმნათ პროგრამა, რომელ�?ც წარმატებ�?თ კავშ�?რებ�? მოსმენ�?ს სერვერზე? (დ�?ახ, შეგვ�?ძლ�?ა.)
- გაგზავნ�?ს და მ�?ღებ�?ს: ერთხელ მ�?ვაღწ�?ეთ აქტ�?ურ�? კავშ�?რ�?, როგორ გამოვ�?ყენოთ ეს გაცვლა მონაცემებ�?, მათ შორ�?ს ორ�? გადაცემა? (თქვენ მ�?ხვდა ეს—გაგზავნას() და recv().)
- არასამთავრობო ბლოკ�?რება და ას�?ნქრონულ�? სოკეტებ�?ს: როგორ შე�?ძლება ჩვენ გაზრდ�?ს ეფექტურობ�?ს ჩვენ�? კოდ�? ახორც�?ელებს სხვადასხვა ქსელ�?ს სქემა? (ჩვენ გ�?კავ�?ა ჩვენ�? ფანჯარა პროცედურა ცოტა.)
- უფრო გაკვეთ�?ლებ�? და ბმულებ�?: რა რესურს�? არსებობს, ზემოთ და მ�?ღმა ამ tutorial? მე მონ�?შნეთ 3 რომ უნდა შევ�?ნარჩუნოთ თქვენ დაკავებულ�?, ხოლო (მას შემდეგ, რაც თქვენ უკვე მონელებულ�? ჩემ�? tutorial, რა თქმა უნდა :-).
- კომენტარებ�? და კრ�?ტ�?კა: აქ არ�?ს თქვენ�? შესაძლებლობა შეფასება სამეურვეო, შეკ�?თხვებ�? ან დატოვოთ კომენტარ�?.
მ�?უხედავად �?მ�?სა, რომ თქვენ შე�?ძლება �?ყოს და�?ნტერესებულ�?, რომ მ�?აღწ�?ოს, რომ awe-�?ნსპ�?რ�?რებ�?თ პუნქტ�?, რომელ�?ც თქვენ�? განცხადება წარმატებ�?თ, რაც მ�?ს�? პ�?რველ�? კავშ�?რ�?, უნდა �?ცოდეს, ცნებებ�? უკან კოდ�?. ცდ�?ლობენ, რათა თავ�?დან ავ�?ც�?ლოთ უბრალოდ მან�?პულ�?რებ�?ს მოცემულ�? კოდ�? ვარ�?ანტს თქვენ�? უშუალო საჭ�?როებებს და ამ�?ს ნაცვლად �?დენტ�?ფ�?ც�?რება მოთხოვნებს თქვენ�? განაცხად�? და მხოლოდ ამ�?ს შემდეგ განახორც�?ელოს, რაც, როგორც ჩანს, საუკეთესო გამოსავალ�?. რომ საკმარ�?ს�?ა ჩემ�? Zen, პროგრამულ�? უზრუნველყოფა-შემუშავება რჩევა ახლა; მოდ�?თ, ნუ ზოგ�?ერთ�? ქსელურ�? პროგრამ�?რებ�?ს…
პროგრამებ�? მომსახურება გარეთ მანქანებ�? უწოდებენ სერვერებ�?. სერვერზე პროგრამებ�? მოუსმ�?ნეთ კლ�?ენტებს �?ნ�?ც�?ალ�?ზაც�?�?სას ერთ�? ან მეტ�? მოსმენ�?ს სოკეტებ�?ს. როდესაც კლ�?ენტ�? უკავშ�?რდება ერთ�? ასეთ�? მოსმენ�?ს სოკეტებ�?ს, სერვერზე �?ღებს შეტყობ�?ნება, 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() ჯერ კ�?დევ არ ყოფ�?ლა.
ასევე არ�?ს განსაზღვრავს �?ყ�?დება NETWORK_ERROR და NETWORK_OK. ეს შე�?ძლება �?ყოს სასარგებლო, როდესაც შემოწმებ�?ს დაბრუნება არც საკუთარ�? ქსელ�?ს ფუნქც�?ებ�?. თუ თქვენ�? ფუნქც�?ებ�? დაბრუნდა ერთ�? ამ ღ�?რებულებებ�?ს ნომრებზე ფუნქც�?ა შე�?ძლება შეასრულოს მარტ�?ვ�? თანასწორობ�?ს ტესტ�? გამოავლ�?ნოს ნებ�?სმ�?ერ�? შეცდომებ�?: თუ (myNetworkingFunction() == NETWORK_ERROR) {…}. ნომრებზე ფუნქც�?ა შე�?ძლება შემდეგ მ�?�?ღოს კონკრეტულ�? კოდ�? WSAGetLastError() და handle შეცდომა შესაბამ�?სად. საბოლოო ჯამშ�?, ახორც�?ელებს კარგ�? შეცდომა გატარება სქემა ახლა დაგ�?ზოგავთ თქვენ ბევრ�? დღ�?ს ან კვ�?რ�?ს განვ�?თარებ�?ს დროს, როგორც თქვენ მყ�?ს�?ერად ვ�?ც�? რატომ, თქვენ�? პროგრამა ვერ შეძლო.
გარდა �?მ�?სა, რომ დაბრუნებ�?ს ახალ�? კლ�?ენტ�? კავშ�?რ�?, მ�?�?ღოს() საშუალებას სერვერზე ამონაწერ�? �?ნფორმაც�?ა კლ�?ენტ�?ს შესახებ, ვ�?დრე მეშვეობ�?თ მეთოდებ�? მო�?თხოვს დამატებ�?თ�? ფუნქც�?ა ზარებ�? ან დრო (რომელ�?ც შე�?ძლება გახდეს საკ�?თხ�? ამ თამაშ�?ს სერვერებ�?, სადაც ს�?ჩქარე მ�?�?ღოს loop განსაკუთრებ�?თ კრ�?ტ�?კულ�?). �?სარგებლოს ამ ფუნქც�?ონ�?რება, გა�?ვლ�?ს მ�?სამართ�? sockaddr_in struct როლებშ�? რათა sockaddr მაჩვენებელ�?, ანუ (LPSOCKADDR) და aSockaddrInStructure. გარდა ამ�?სა, განვაცხადო, მთელ�? ცვლად�?, მ�?თ�?თებულ�? ღ�?რებულება int, რომ sizeof, რომ sockaddr struct, და აკეთებს მ�?სამართ�? რ�?ცხვ�?, როგორც მესამე პარამეტრ�?. თუ მ�?სამართ�?, �?ნფორმაც�?ა უნდა �?ყოს დაბრუნდა მას შემდეგ, რაც ფუნქც�?ა ზარ�?, ს�?გრძ�?ს პარამეტრ�? უნდა �?ყოს.
ეს არ არ�?ს ბევრ�? სერვერ�?, რადგან მას ელოდება მხოლოდ ერთ�? მომხმარებლ�?ს დაკავშ�?რება და შემდეგ ეგრევე �?რთვება, მაგრამ ეს არ�?ს ყველაზე ძ�?რ�?თად�? დ�?ზა�?ნ�?. უბრალოდ გარკვევა რამ, ზარ�? 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); }
ყველაზე რთულ�? ხაზ�? ჩამონათვალ�? არ�?ს შემდეგ�?:
�?მ�?ტომ, რომ �?გ�? ასრულებს რამდენ�?მე ოპერაც�?ებ�?—ერთ�? მათგან�? შედარებ�?თ დამალულ�?—ერთდროულად. ავ�?ღოთ �?ს გარდა ეტაპობრ�?ვად:
“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 და სხვებ�? არ გაკეთდა, ბევრად უკეთ, ვ�?დრე ჩემთვ�?ს საცდელ�? სპეც�?ფ�?კა ამ თემაზე: