Johnnie’s Winsock Tutorial

Original:http://johnnie.jerrata.com/winsocktutorial/ 

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

აი, რაც ჩვენ უნდა მოიცავს:

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

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

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

შექმნა მოსმენა სოკეტი

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

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

    {

    short sin_family; // Protocol type

    u_short sin_port; // Port number of სოკეტი

    სტრუქტურა in_addr sin_addr; // IP address

    char sin_zero[8]; // Unused

    };

    ირველი ველი ოქმის ტიპის, რომელიც, როგორც წესი, AF_INET (TCP / IP). როგორც მოსმენის სოკეტი არ არის დაინტერესებული ქსელის მისამართი მანქანა, რომელშიც ის ცხოვრობს, 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)
    
    {
    
    	WORD sockVersion;
    
    	WSADATA wsaData;
    
    	int nret;
    
    
    
    	sockVersion = MAKEWORD (1, 1); // გვსურს Winsock ვერსია 1.1
    
    
    
    
    
    	// ჩვენ ვიწყებთ მიერ ინიციალიზაციისას Winsock
    
    	WSAStartup (sockVersion, და wsaData);
    
    
    
    
    
    	// შემდეგი, შექმნათ მოსმენის სოკეტი
    
    	სოკეტი listeningსოკეტი;
    
    
    
    	listeningსოკეტი = სოკეტი (AF_INET, // გადასვლა ზე მეტი TCP / IP
    
    			         SOCK_STREAM, // ეს არის ნაკადი ორიენტირებული სოკეტი
    
    				 IPPROTO_TCP); // გამოყენება TCP ვიდრე UDP
    
    
    
    	თუ (listeningსოკეტი == INVALID_სოკეტი)
    
    	{
    
    		nret = WSAGetLastError (); // მიიღეთ უფრო დეტალური შეცდომა
    
    		ReportError (nret, "ბუდე ()"); // ანგარიშს შეცდომა ჩვენი საბაჟო ფუნქცია
    
    
    
    		WSACleanup (); // Shutdown Winsock
    
    		დაბრუნდეს NETWORK_ERROR; // დაბრუნება შეცდომის მნიშვნელობა
    
    	}
    
    
    
    
    
    	// გამოიყენეთ SOCKADDR_IN სტრუქტურა შევსება მისამართზე ინფორმაცია
    
    	SOCKADDR_IN serverInfo;
    
    
    
    	serverInfo.sin_family = AF_INET;
    
    	serverInfo.sin_addr.s_addr = INADDR_ANY; // მას შემდეგ, რაც ამ სოკეტი უსმენს კავშირები,
    
    							// ნებისმიერი ადგილობრივი მისამართი გააკეთებს
    
    	serverInfo.sin_port = htons (8888); // კონვერტაციის რიცხვი 8888 ქსელში-ბაიტი მიზნით
    
    							// და ჩადეთ პორტის ველი
    
    
    
    
    
    	// სავალდებულოა სოკეტი ჩვენი ადგილობრივი სერვერის მისამართი
    
    	nret = bind (listeningსოკეტი (LPSOCKADDR) და serverInfo, sizeof (სტრუქტურა sockaddr));
    
    
    
    	თუ (nret == სოკეტი_ERROR)
    
    	{
    
    		nret = WSAGetLastError ();
    
    		ReportError (nret "bind ()");
    
    
    
    		WSACleanup ();
    
    		დაბრუნდეს NETWORK_ERROR;
    
    	}
    
    
    
    
    
    	// ჩადება სოკეტი მოუსმინოს
    
    	nret = მოუსმინოს (listeningსოკეტი, 10); // 10 კავშირები შეიძლება ველოდოთ ნებისმიერ
    
    							// ერთ დროს უნდა მიიღოს () ed
    
    
    
    	თუ (nret == სოკეტი_ERROR)
    
    	{
    
    		nret = WSAGetLastError ();
    
    		ReportError (nret, "მოსმენა ()");
    
    
    
    		WSACleanup ();
    
    		დაბრუნდეს NETWORK_ERROR;
    
    	}
    
    
    
    
    
    	// დაელოდეთ კლიენტს
    
    	სოკეტი theClient;
    
    
    
    	theClient = მიიღოს (listeningსოკეტი,
    
    			   NULL, // არააუცილებელი, მისამართი SOCKADDR_IN სტრუქტურა
    
    			   NULL); // არააუცილებელი, მისამართი ცვლადი, რომელიც შეიცავს
    
    							// sizeof (სტრუქტურა SOCKADDR_IN)
    
    
    
    	თუ (theClient == INVALID_სოკეტი)
    
    	{
    
    		nret = WSAGetLastError ();
    
    		ReportError (nret, "მიიღოს ()");
    
    
    
    		WSACleanup ();
    
    		დაბრუნდეს NETWORK_ERROR;
    
    	}
    
    
    
    
    
    	// გაგზავნა და მიღება კლიენტს, და ბოლოს,
    
    	closeსოკეტი (theClient);
    
    	closeსოკეტი (listeningსოკეტი);
    
    
    
    
    
    	// Shutdown Winsock
    
    	WSACleanup ();
    
    	დაბრუნდეს NETWORK_OK;
    
    }
    
    
    
    
    
    ბათილად ReportError (int ERRORCODE, const char * whichFunc)
    
    {
    
       char errorMsg [92]; // უგზო ბუფერული გამართავს
    
    							// გენერირებული შეცდომა
    
       
    
       ZeroMemory (errorMsg, 92); // ავტომატურად NULL-შეწყვიტოს სიმებიანი
    
    
    
       // შემდეგ ხაზი ასლები ფრაზა, whichFunc სიმებიანი და მთელი ERRORCODE შევიდა ბუფერული
    
       sprintf (errorMsg, "ზარის% s დაბრუნდა შეცდომის% d!", (char *) whichFunc, ERRORCODE);
    
    
    
       გაგზავნა (NULL, errorMsg "სოკეტიIndication", MB_OK);
    
    }

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

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

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

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

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

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

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

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

    • HOSTENT : სტრუქტურა გამოიყენება ვუთხრა სოკეტი, რომელსაც კომპიუტერული და პორტი დაკავშირება. ეს სტრუქტურები ხშირად გამოჩნდება, როგორც LPHOSTENT ცვლადები, რომლებიც მხოლოდ მითითებას HOSTENT სტრუქტურებში. როგორც თქვენ კოდი Windows, თქვენ ზოგადად, რომ ნებისმიერი მონაცემები ტიპის უძღოდა LP ნიშნავს, რომ ტიპი არის რეალურად მომცეთ “ბაზა” ტიპის (მაგალითად, LPCSTR მომცეთ C სიმებიანი, ასევე ცნობილი როგორც 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)
    
    {
    
    	WORD sockVersion;
    
    	WSADATA wsaData;
    
    	int nret;
    
    
    
    	sockVersion = MAKEWORD (1, 1);
    
    
    
    
    
    	// Initialize Winsock როგორც ადრე
    
    	WSAStartup (sockVersion, და wsaData);
    
    
    
    
    
    	// Store ინფორმაცია სერვერზე
    
    	LPHOSTENT hostEntry;
    
    
    
    	hostEntry = gethostbyname ( "www.yahoo.com"); // დაზუსტება სერვერზე მისი სახელი;
    
    							// კიდევ ერთი ვარიანტი: gethostbyaddr ()
    
    
    
    	თუ (! hostEntry)
    
    	{
    
    		nret = WSAGetLastError ();
    
    		ReportError (nret "gethostbyname ()"); // ანგარიშს შეცდომა, როგორც ადრე
    
    
    
    		WSACleanup ();
    
    		დაბრუნდეს NETWORK_ERROR;
    
    	}
    
    
    
    
    
    	// შექმნა სოკეტი
    
    	სოკეტი theსოკეტი;
    
    
    
    	theსოკეტი = სოკეტი (AF_INET, // გადასვლა ზე მეტი TCP / IP
    
    			   SOCK_STREAM, // ეს არის ნაკადი ორიენტირებული სოკეტი
    
    			   IPPROTO_TCP); // გამოყენება TCP ვიდრე UDP
    
    
    
    	თუ (theსოკეტი == INVALID_სოკეტი)
    
    	{
    
    		nret = WSAGetLastError ();
    
    		ReportError (nret, "ბუდე ()");
    
    
    
    		WSACleanup ();
    
    		დაბრუნდეს NETWORK_ERROR;
    
    	}
    
    
    
    
    
    	// შეავსეთ SOCKADDR_IN სტრუქტურა ერთად მისამართზე ინფორმაცია
    
    	SOCKADDR_IN serverInfo;
    
    
    
    	serverInfo.sin_family = AF_INET;
    
    
    
    	// ამ ეტაპზე, ჩვენ წარმატებით მოძიებული სასიცოცხლო ინფორმაციას სერვერზე,
    
    	// მათ შორის, მისი hostname, მაგალითი, და IP მისამართები. დაველოდოთ; როგორ შეიძლება ერთი
    
    	// კომპიუტერში აქვს სხვადასხვა მისამართები და ზუსტად ის, რაც შემდეგ ხაზი აკეთებს?
    
    	// იხილეთ განმარტება ქვემოთ.
    
    
    
    	serverInfo.sin_addr = * ((LPIN_ADDR) * hostEntry-> h_addr_list);
    
    
    
    	serverInfo.sin_port = htons (80); // შეცვლა ქსელის ბაიტი მიზნით და
    
    							// ჩადეთ პორტის ველი
    
    
    
    
    
    	// სერვერთან დაკავშირება
    
    	nret = დაკავშირება (theსოკეტი,
    
    		       (LPSOCKADDR) და serverInfo,
    
    		       sizeof (სტრუქტურა sockaddr));
    
    
    
    	თუ (nret == სოკეტი_ERROR)
    
    	{
    
    		nret = WSAGetLastError ();
    
    		ReportError (nret, "დაკავშირება ()");
    
    
    
    		WSACleanup ();
    
    		დაბრუნდეს NETWORK_ERROR;
    
    	}
    
    
    
    
    
    	// წარმატებით უკავშირდება!
    
    
    
    
    
    	// გაგზავნა / მიღება, მაშინ გასუფთავება
    
    	closeსოკეტი (theსოკეტი);
    
    	WSACleanup ();
    
    }
    
    
    
    
    
    ბათილად ReportError (int ERRORCODE, const char * whichFunc)
    
    {
    
       char errorMsg [92]; // უგზო ბუფერული გამართავს
    
    							// გენერირებული შეცდომა
    
       
    
       ZeroMemory (errorMsg, 92); // ავტომატურად NULL-შეწყვიტოს სიმებიანი
    
    
    
       // შემდეგ ხაზი ასლები ფრაზა, whichFunc სიმებიანი და მთელი ERRORCODE შევიდა ბუფერული
    
       sprintf (errorMsg, "ზარის% s დაბრუნდა შეცდომის% d!", (char *) whichFunc, ERRORCODE);
    
    
    
       გაგზავნა (NULL, errorMsg "სოკეტიIndication", MB_OK);
    
    }

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

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

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

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

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

    შემდეგი, char * დაბრუნდა dereferencing ოპერაცია მიცემული შევიდა in_addr * ან LPIN_ADDR. ბოლოს, კიდევ ერთი პატივისცემის ოპერაცია ხორციელდება, რომ დაბრუნდეს in_addr სტრუქტურა მიერ მითითებული მაჩვენებელი, რომელიც შეიძლება მხოლოდ გამართავს ერთ მისამართზე. შედეგად in_addr სტრუქტურა შემდეგ ენიჭება 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(სტრუქტურა in_addr), AF_INET);

     

    if (!hostEntry)

    {

    // Handle accordingly

    }

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

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

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

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

    int send(

    სოკეტი s,

    const char * FAR buf,

    int len,

    int flags

    );

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

    char ბუფერული [256]; // გამოცხადება ბუფერული დასტის
    
    char * ბუფერული = new char [256]; // ან heap
    
    
    
    ZeroMemory (ბუფერული, 256);
    
    strcpy (ბუფერული, "ვითომ ეს არის მნიშვნელოვანი მონაცემები.");
    
    
    
    nret = გაუგზავნე (theსოკეტი,
    
    	    ბუფერული,
    
    	    strlen (ბუფერული), // გაითვალისწინეთ, რომ ეს განსაზღვრავს სიგრძეზე სიმებიანი; არა
    
    				// ზომა მთელი ბუფერული
    
    	    0); // ყველაზე ხშირად არის ნულოვანი, მაგრამ ვხედავ MSDN სხვა ვარიანტი
    
    
    
    წაშლა [] პაკეტი; // თუ და მხოლოდ იმ შემთხვევაში, თუ ბევრი დეკლარაცია გამოიყენება
    
    
    
    თუ (nret == სოკეტი_ERROR)
    
    {
    
    	// მიიღეთ კონკრეტული კოდი
    
    	// Handle შესაბამისად
    
    	დაბრუნდეს NETWORK_ERROR;
    
    } Else {
    
    	// nret შეიცავს რაოდენობის ბაიტიs გაგზავნილი
    
    }

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

char ბუფერული [256]; // დასტის

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



nret = recv (theსოკეტი,

	    ბუფერული,

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

	    0);



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

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



თუ (nret == სოკეტი_ERROR)

{

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

	// Handle შესაბამისად

	დაბრუნდეს NETWORK_ERROR;

} Else {

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

}

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

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

char * წაკითხვის ხაზი ()

{

   ვექტორი theVector;

   char ბუფერული;

   int ბაიტიsReceived;



   ხოლო (ნამდვილი)

   {

      ბაიტიsReceived = recv (theსოკეტი, და ბუფერული, 1, 0);

      თუ (ბაიტიsReceived <= 0)

         დაბრუნების NULL;



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

      {

         char * pChar = new char [theVector.size () + 1];

         memset (pChar, 0, theVector.size () + 1);



         for (int f = 0; f <theVector.size (); ვ ++)

            pChar [f] = theVector [f];



         დაბრუნდეს pChar;

      } Else {

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

      }

   }

}

ვექტორი გამოყენებული ნაცვლად მასივი, რადგან მისი შენახვის სივრცეში შეიძლება გაიზარდოს ავტომატურად ვარიანტს სიგრძის ხაზი. თუ recv () დააბრუნებს შეცდომის (მიერ მითითებულ ბაიტიsReceived რომ ნაკლები ნულოვანი), NULL დაბრუნდა. ვინაიდან ეს არის შესაძლებლობა, მოუწოდებდა ფუნქციები უნდა უზრუნველყოს, რომ სიმებიანი დაბრუნდა წაკითხვის ხაზი () მოქმედებს, სანამ გამოყენება. შიგნით მარყუჟის, ერთი char მიღებული ბუდე და, თუ არ newline ხასიათი, დასძინა, რომ ვექტორი. თუ ეს არის სტრიქონების გადატანით ხასიათი, შინაარსი ვექტორი გადაწერა შევიდა C სიმებიანი და დაბრუნდა. სიმებიანი ცხადდება ერთი char აღემატება ვექტორი და memset () ნულის ისე, რომ დაბრუნდა ხაზი იქნება ავტომატურად NULL წყდება. დამთავრებული სიმებიანიs ერთად NULL ხელს უშლის უჩვეულო შეცდომები და ზოგადად კარგი პროგრამირების პრაქტიკაში.

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

// Code დაწერილი არც. შეცვლილია ოდნავ

// მხარდასაჭერად გაგზავნა () API, რათა ლოგიკა მეტი იკითხება,

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



განსაზღვრავს backKey \ ბ "// გამორთვა backspaces, განსაზღვრავს backKey NULL

განსაზღვრავს NEWLINE '\ n "

განსაზღვრავს endStr \ 0 "



char * წაკითხვის ხაზი (სოკეტი s)

{

	ვექტორი theVector;

	char ბუფერული;

	char * pChar;

	int ბაიტიsReceived;



	ხოლო (ნამდვილი)

	{

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



		თუ (ბაიტიsReceived <= 0)

		{

			გაგზავნა (NULL, "recv () დაბრუნდა არაფერი.", "სოკეტიIndication", MB_OK);

			დაბრუნების NULL;

		}



		switch (ბუფერული)

		{

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

				თუ (theVector.size ()> 0)

					theVector.pop_back ();

				მოხსნა;

			შემთხვევაში endStr: // თუ ბოლომდე სიმებიანი char მიაღწია,

			შემთხვევაში NEWLINE: // ან, თუ დასასრულს ხაზი char მიაღწია,

				pChar = new char [theVector.size () + 1];

				memset (pChar, 0, theVector.size () + 1);



				for (int f = 0; f <theVector.size (); ვ ++)

					pChar [f] = theVector [f];

				დაბრუნდეს pChar;

				მოხსნა;

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

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

				მოხსნა;

		}

	}

}

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

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

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

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

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

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

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

განსაზღვრავს THERE_WAS_A_სოკეტი_EVENT WM_USER + 100 // WM_USER ბაზას საბაჟო შეტყობინებები
// სადღაც ჩვენი ინიციალიზაციისას კოდი შემდეგ CreateWindow (), ჩვენ მოვუწოდებთ WSAAsyncSelect ()

WSAAsyncSelect (theსოკეტი, HWND, THERE_WAS_A_სოკეტი_EVENT, FD_READ | FD_WRITE | FD_CONNECT | ...);



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

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

// (FD_WRITE), ან როდესაც მე წარმატებით დაკავშირებული სხვის (FD_CONNECT), ან როდესაც ... და სხვ.
// ჩვენი window პროცედურა (ფუნქცია რომელიც ემსახურება ყველა შეტყობინება, რომელიც Windows აგზავნის თქვენი app)

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

{



	switch (MSG)

	{

		შემთხვევაში THERE_WAS_A_სოკეტი_EVENT:

			თუ (WSAGETSELECTERROR (lParam))

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

				closeსოკეტი (theსოკეტი);

				WSACleanup (); // Shutdown Winsock

				დაბრუნდეს NETWORK_ERROR;

			}

			switch (WSAGETSELECTEVENT (lParam))

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

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

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

					მოხსნა;

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

					// წერს მონაცემებს

					მოხსნა;

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

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

					მოხსნა;

				საქმე ... // იგივე setup სხვა დროშები

					მოხსნა;

			}

			მოხსნა;



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



	}

}

გაითვალისწინეთ, რომ არ შეიძლება ერთი გზავნილი თითოეული მოვლენა, როგორიცაა სოკეტი_GOTMSG for FD_READ და შემდეგ სოკეტი_CONNECTED for FD_CONNECT. ეს იმიტომ, რომ განმეორებითი მოთხოვნის WSAAsyncSelect () კონფიგურაცია თითოეული დროშა მოხდება ეფექტი ბოლო ზარი WSAAsyncSelect ().

სხვა გაკვეთილები და ბმულები

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

Effective TCP/IP Programming: 44 Tips to Improve Your Network Programs   TCP/IP Sockets in C: Practical Guide for Programmers      Programming Windows, Fifth Edition       1 year subscription to Maxim magazine

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

როგორ გავაუმჯობესო?

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

 

Leave a Reply