• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            socket編程—技術實現

            Posted on 2008-01-17 22:22 MiweiDev 閱讀(765) 評論(0)  編輯 收藏 引用 所屬分類: 網絡編程
            什么是socket?socket就是...,我在這里就不抄書了,有興趣的同仁去查查書吧。
            不過還要說一句,socket就是不同進程之間的一種通信方式。就象打電話是朋友之間的一種通信方式是一樣。個人理解:所謂“通信”,就是相互之間發送數據。有人理解socket是不同計算機之間的一種通信方
            式,這是不確切的。兩個進程,不管是運行在同一臺計算機上,還是運行在不同計算機上,都可通過
            socket技術進行通信。

            socket套接字的使用需要有網卡的支持,所以socket一般都被用來在不同機器之間通信,而如果在同一臺計算機上的兩個進程進行通信,通常采用效率更高的共享內存技術來實現。

            兩個進程之間進行通訊,就需要兩個進程同時都在運行了(廢話),在具體實現中,兩個進程我們通常要區別對待,一個進程專門等待另一個進程給自己發消息,收到消息后進行處理,在把處理結果發送回去。我們把專門處理消息、提供服務的進程稱為服務器端,把發送消息、請求處理的進程稱為客戶端??傮w過程就是客戶端發送一個消息給服務器端,服務器端進程收到消息進行處理,把處理結果發送給客戶端。恩,就是這樣。

            還有一個問題,如果我現在有一個進程要跟另一臺計算機上的某個進程進行socket通信,那在我這個進程中如何指定另一個進程呢?這里還需要說一下另一個概念——端口,如果把操作系統比作一座房子的話,那端口就是房子的窗口,是系統外界同系統內部進行通信的通道。在socket實現中,我們不進行另一個進程的指定,而是指定發送消息或接收消息的端口號。比如說現在進程A要給進程B發消息,我們會把消息發送到進程B所運行的計算機的端口N上,而進程B此時正在監視端口N,這樣進程B就能收到進程A發送來的數據,同樣進程B也把消息發送到該端口上,進程A也能從該端口收到進程B發送來的數據,當然,這需要客戶端和服務器端關于端口號進行一個約定,即共同操作同一個端口。如果客戶端把消息發送到端口N1上,而服務器端監視的是端口N2,那通信一定不能成功。端口號最大為65535,不能比這個再大了,但在我們自己的程序中盡量不要用小于1024的端口號,小于1024的端口好很多都被系統使用了,比如23被telnet所使用。

            socket的實現是很簡單的,只要按照一定的步驟,就可馬上建立一個這樣的通信通道。

            下面較詳細的介紹幾個核心的函數:

            SOCKET socket(int af, int type, int protocol);
            無論是客戶端還是服務器端,下面這個函數是一定要用到的,也是最先用到的。
            這個函數是要告訴系統,給我準備好一個socket通道,我要和其它進程通信了。函數的返回值很重要,我們要記下來,它表示系統為我們準備好的這個socket通道,在以后的每個socket相關函數中都會用到,如果這個值等于SOCKET_ERROR,表示函數執行失敗了。函數的參數我們分別給:PF_INET、SOCK_STREAM和IPPROTO_TCP。

            int bind(SOCKET s, const sockaddr *addr, int namelen);
            這個函數只有服務器端程序使用,作用是與某個socket通道綁定??梢杂梅祷刂蹬袛嘣摵瘮祱绦薪Y果怎么樣,如果等于SOCKET_ERROR,那就是失敗了。第一個參數s,就是socket()函數的返回值;在結構addr中,我們要給定一個端口號;namelen等于結構sockaddr的大小。

            int listen(SOCKET s, int backlog);
            這個函數只有服務器端程序使用,作用是監聽該端口。返回值與bind函數意義一樣。

            int accept(SOCKET s, sockaddr *addr, int *addrlen);
            這個函數只有服務器端程序使用,作用是響應客戶端的連接。返回值與bind函數意義一樣。

            int connect(SOCKET s, const sockaddr *name, int namelen);
            這個函數只有客戶端程序使用,作用是把客戶端和某個計算機的某個端口建立連接。返回值與bind函數意義一樣。第一個參數s,就是socket()函數的返回值;在結構name中,我們要給定一個端口號和目的機器名;namelen等于結構sockaddr的大小。

            int send(SOCKET s, char *buf, int len, int flags);
            int recv(SOCKET s, char *buf, int len, int flags);
            這兩個函數就是發送數據和接收數據,客戶端和服務器端程序都能用,哪個發送哪個接收不用說了吧?呵呵。
            從函數的返回值可以檢查函數執行是否成功。參數中buf是指向發送或接收的數據的指針,len是數據長度。flags我們給個0就可以(其實是我不知道具體含義)。

            最后就是關閉socket了,這個很容易忘掉,但這個函數很重要,一定要用。
            int closesocket(SOCKET s);


            好了,關鍵函數就這么幾個,下圖是這幾個函數的執行順序:

            client端 service端

              |                    |
              v                 v
            socket()       socket()
              |                          |
              |                          v
              |                        bind()
              |                          |
              |                          v
              |                        listen()
              |                          |
              |                          v
              |                        accept() 掛起,直到有客戶端來連接
              |                          |
              v                  三段握手過程   |
            connect()       <-------------> |
              |                       |
              v   發送消息   v
              +---> send() ---------------> recv() <-------+
              |   |     . |
              |   |     . 處理消息 |
              |   v   響應消息   . |
              +---- recv() <--------------- send() --------+
              |     |
              v     |
            close() ---------------> recv()
                |
                v
              closesocket()

            上圖我覺得能很好的說明客戶端和服務器端的運行軌跡。

            使用以上幾個函數在 linux 系統上就可成功建立一個socket通信連路,但如果在windows系統上,還要用到另一個函數:
            int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
            在windows系統上,首先要執行這個函數,所以要把這個函數放在socket()函數的前面。

            我對上面的函數進行了一些封裝,為節省篇幅,我去掉所有注釋和非重要的函數,在這里可以看到各個函數的具體用法:

            在 VC60 環境下要運行下面的函數,要包含頭文件 errno.h 和 winsock2.h,還有,在連接的時候要連接上ws2_32.dll文件。

            這是頭文件內容:
            class Socket {
            public:

            bool setup();

            void close();

            bool connect(string host, int port);

            bool listen();

            int accept();

            int recv(char *buf, int len);

            int recv(int new_fd, char *buf, int len);

            int send(const char *msg, int len);

            int send(int new_fd, const char *msg, int len);

            private:
              int _fd;
            };

            這是實現文件內容:
            bool Socket::setup() {

            WSADATA wsd;
            _fd = WSAStartup(MAKEWORD(2,2), &wsd);
            if(_fd) {
            return false;
            }

            _fd = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
            if (_fd == -1) {
            return false;
            }
            return true;
            }

            bool Socket::listen() {
            struct sockaddr_in my_addr;

            my_addr.sin_family = AF_INET;
            my_addr.sin_port = htons(52309);
            my_addr.sin_addr.s_addr = INADDR_ANY;

            if(::bind(_fd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == SOCKET_ERROR) {
            return false;
            }

            if(::listen(_fd, BACKLOG) == SOCKET_ERROR) {
            return false;
            }

            return true;
            }

            int Socket::accept()
            {
            int new_fd;
            struct sockaddr_in their_addr;
            int sin_size = sizeof(their_addr);

            printf("accepting... \n");

            new_fd = ::accept(_fd,
              (struct sockaddr *)&their_addr,
              &sin_size);
            return new_fd == SOCKET_ERROR ? -1:new_fd;
            }

            bool Socket::connect(string host, int port) {
            struct hostent *_h = gethostbyname(host.c_str());
            if (_h == 0) {
            return false;
            }

            struct in_addr *_addr = (struct in_addr *)_h->h_addr;
            struct sockaddr_in sin;
            sin.sin_family = AF_INET;
            sin.sin_addr = *_addr;
            sin.sin_port = htons(port);

            if (::connect(_fd, (sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR) {
            return false;
            }

            return true;
            }

            int Socket::recv(int new_fd, char *buf, int len)
            {
            int nb = ::recv(new_fd, buf, len, 0);
            if (nb == -1) {
            printf("Error! recv.\n");
            }
            return nb;
            }

            int Socket::recv(char *buf, int len) {
            return recv(_fd, buf, len);
            }

            int Socket::send(const char *msg, int len) {
            return send(_fd, msg, len);
            }

            int Socket::send(int new_fd, const char *msg, int len)
            {
            int nb = ::send(new_fd, msg, len, 0);
            if (nb == -1) {
            printf("Error! send.\n");
            }

            return nb;
            }

            void Socket::close() {

            int trytimes = 0;
            while(::closesocket(_fd) && trytimes < CLOSE_TRY_TIMES)
            trytimes++;

            if(trytimes == 10) {
            printf("Cannot close socket!\n");
            }
            }

            好,socket類是封裝好了,下面就是組織了,服務器端和客戶端是不一樣的,下面分別給出代碼,到這里已經就很簡單了。

            客戶端:
            int main(int argc, char **argv)
            {
            printf("socket of client is run ...\n");
            Socket s;
            if (!s.connect("dezhi", 52309))
            return 0;

            char *msg = "ok, send a message.";
            for (int i=0; i<10; i++) {
            s.send(msg, 20);
            printf("message = %s\n", msg);
            }
            s.send("q", 1);
            s.close();

            return 0;
            }

            服務器:
            int main(int argc, char **argv) {
            printf("socket of service is run ...\n");

            Socket s;
            s.listen();
            int new_fd = s.accept();

            char buf[8];
            buf[7] = '\0';
            while (1) {
            if (s.recv(new_fd, buf, 5) != -1) {
              printf("%s\n", buf);
              if (buf[0] == 'q')
              break;
            }
            }
            s.close();
            }

            下面為運行結果:
            客戶端:
            socket of client is run ...
            Socket: WSAStartup success execute.
            Socket: socket success execute.
            Socket: Establish the connection to "127.0.0.1:52309"
            message = ok, send a message.
            message = ok, send a message.
            message = ok, send a message.
            message = ok, send a message.
            message = ok, send a message.
            message = ok, send a message.
            message = ok, send a message.
            message = ok, send a message.
            message = ok, send a message.
            message = ok, send a message.
            Socket: Close connection to "127.0.0.1:52309"
            Press any key to continue

            服務器端
            socket of service is run ...
            Socket: WSAStartup success execute.
            Socket: socket success execute.
            bind ok!
            listen ok!
            accepting...
            ok, send a message.
            ok, send a message.
            ok, send a message.
            ok, send a message.
            ok, send a message.
            ok, send a message.
            ok, send a message.
            ok, send a message.
            ok, send a message.
            ok, send a message.
            qk, send a message.
            Press any key to continue

            就到這里吧。socket的相關內容可遠不止這些,我在這里只是給大家來個拋磚引玉,想深究?路還很漫長。關于詳細的實現代碼,去我的《源碼》上找吧,不放在這里,是為了讓篇幅小些。

            亚洲国产精品久久久天堂| 久久国产视频99电影| 91精品免费久久久久久久久| 久久精品国产精品亚洲毛片| 精品国产乱码久久久久久郑州公司| 国产精品久久久久久| 久久精品国产亚洲av麻豆小说| 国产精品久久久久9999| 久久91这里精品国产2020| 久久这里只有精品首页| 久久免费视频6| 亚洲欧美伊人久久综合一区二区| 青青草国产成人久久91网| 久久亚洲AV无码精品色午夜| 久久九九亚洲精品| 欧洲成人午夜精品无码区久久| 狠狠色伊人久久精品综合网| 久久人人爽爽爽人久久久| 亚洲精品无码久久不卡| 狠狠精品干练久久久无码中文字幕 | 欧美伊人久久大香线蕉综合69 | 久久久久久久综合狠狠综合| 一级女性全黄久久生活片免费 | 精品久久久久久综合日本| 一级女性全黄久久生活片免费| 久久精品免费观看| 国产精品一久久香蕉国产线看观看| 精品国产VA久久久久久久冰| 久久精品国产男包| 2021久久精品免费观看| 色婷婷噜噜久久国产精品12p| AA级片免费看视频久久| 狠狠色丁香久久婷婷综| 久久久久亚洲精品男人的天堂| 成人国内精品久久久久影院VR| 久久精品国产亚洲沈樵| 欧美久久综合性欧美| 久久精品成人国产午夜| 亚洲伊人久久精品影院| 久久99久久99精品免视看动漫| 久久久久亚洲AV无码专区首JN |