• <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>

            牽著老婆滿街逛

            嚴(yán)以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            socket編程—技術(shù)實(shí)現(xiàn)

            這幾天都在玩socket了,有一點(diǎn)心得,貼出來(lái)與大家共賞,若有不妥或錯(cuò)誤的地方,還請(qǐng)各位看官指點(diǎn)一二。

            什么是socket?socket就是...,我在這里就不抄書了,有興趣的同仁去查查書吧。
            不過(guò)還要說(shuō)一句,socket就是不同進(jìn)程之間的一種通信方式。就象打電話是朋友之間的一種通信方式是一樣。個(gè)人理解:所謂“通信”,就是相互之間發(fā)送數(shù)據(jù)。有人理解socket是不同計(jì)算機(jī)之間的一種通信方
            式,這是不確切的。兩個(gè)進(jìn)程,不管是運(yùn)行在同一臺(tái)計(jì)算機(jī)上,還是運(yùn)行在不同計(jì)算機(jī)上,都可通過(guò)
            socket技術(shù)進(jìn)行通信。

            socket套接字的使用需要有網(wǎng)卡的支持,所以socket一般都被用來(lái)在不同機(jī)器之間通信,而如果在同一臺(tái)計(jì)算機(jī)上的兩個(gè)進(jìn)程進(jìn)行通信,通常采用效率更高的共享內(nèi)存技術(shù)來(lái)實(shí)現(xiàn)。

            兩個(gè)進(jìn)程之間進(jìn)行通訊,就需要兩個(gè)進(jìn)程同時(shí)都在運(yùn)行了(廢話),在具體實(shí)現(xiàn)中,兩個(gè)進(jìn)程我們通常要區(qū)別對(duì)待,一個(gè)進(jìn)程專門等待另一個(gè)進(jìn)程給自己發(fā)消息,收到消息后進(jìn)行處理,在把處理結(jié)果發(fā)送回去。我們把專門處理消息、提供服務(wù)的進(jìn)程稱為服務(wù)器端,把發(fā)送消息、請(qǐng)求處理的進(jìn)程稱為客戶端。總體過(guò)程就是客戶端發(fā)送一個(gè)消息給服務(wù)器端,服務(wù)器端進(jìn)程收到消息進(jìn)行處理,把處理結(jié)果發(fā)送給客戶端。恩,就是這樣。

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

            socket的實(shí)現(xiàn)是很簡(jiǎn)單的,只要按照一定的步驟,就可馬上建立一個(gè)這樣的通信通道。

            下面較詳細(xì)的介紹幾個(gè)核心的函數(shù):

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

            int bind(SOCKET s, const sockaddr *addr, int namelen);
            這個(gè)函數(shù)只有服務(wù)器端程序使用,作用是與某個(gè)socket通道綁定。可以用返回值判斷該函數(shù)執(zhí)行結(jié)果怎么樣,如果等于SOCKET_ERROR,那就是失敗了。第一個(gè)參數(shù)s,就是socket()函數(shù)的返回值;在結(jié)構(gòu)addr中,我們要給定一個(gè)端口號(hào);namelen等于結(jié)構(gòu)sockaddr的大小。

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

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

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

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

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


            好了,關(guān)鍵函數(shù)就這么幾個(gè),下圖是這幾個(gè)函數(shù)的執(zhí)行順序:

            client端 service端

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

            上圖我覺得能很好的說(shuō)明客戶端和服務(wù)器端的運(yùn)行軌跡。

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

            我對(duì)上面的函數(shù)進(jìn)行了一些封裝,為節(jié)省篇幅,我去掉所有注釋和非重要的函數(shù),在這里可以看到各個(gè)函數(shù)的具體用法:

            在 VC60 環(huán)境下要運(yùn)行下面的函數(shù),要包含頭文件 errno.h 和 winsock2.h,還有,在連接的時(shí)候要連接上ws2_32.dll文件。

            這是頭文件內(nèi)容:
            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;
            };

            這是實(shí)現(xiàn)文件內(nèi)容:
            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類是封裝好了,下面就是組織了,服務(wù)器端和客戶端是不一樣的,下面分別給出代碼,到這里已經(jīng)就很簡(jiǎn)單了。

            客戶端:
            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;
            }

            服務(wù)器:
            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();
            }

            下面為運(yùn)行結(jié)果:
            客戶端:
            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

            服務(wù)器端
            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的相關(guān)內(nèi)容可遠(yuǎn)不止這些,我在這里只是給大家來(lái)個(gè)拋磚引玉,想深究?路還很漫長(zhǎng)。關(guān)于詳細(xì)的實(shí)現(xiàn)代碼,去我的《源碼》上找吧,不放在這里,是為了讓篇幅小些。

            posted on 2006-04-20 17:36 楊粼波 閱讀(897) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 網(wǎng)絡(luò)編程

            久久一区二区免费播放| 伊人久久综合精品无码AV专区| 精品久久亚洲中文无码| 久久国产成人午夜aⅴ影院| 2021精品国产综合久久| 久久66热人妻偷产精品9| 亚洲精品无码久久千人斩| | 国产精品无码久久综合| 久久婷婷人人澡人人爽人人爱 | 精品久久久久久无码人妻热| 久久久久综合网久久| MM131亚洲国产美女久久| 精品国产VA久久久久久久冰| 久久人人爽人人爽人人片av高请| 久久久精品国产免大香伊 | www亚洲欲色成人久久精品| 日本三级久久网| 91久久精品国产免费直播| 国内精品久久久久久久亚洲| 久久99精品国产麻豆蜜芽| 久久久久国产| 97视频久久久| 精品无码久久久久久尤物| 一本一道久久精品综合| 久久久WWW免费人成精品| 久久综合亚洲鲁鲁五月天| 久久久久人妻一区二区三区vr| 精品无码久久久久国产| 99久久精品免费看国产| 亚洲午夜久久久| 99久久成人国产精品免费 | 中文字幕无码久久久| 久久经典免费视频| 久久w5ww成w人免费| 国产精品久久久久一区二区三区| 久久综合色之久久综合| 欧美熟妇另类久久久久久不卡| 色综合久久最新中文字幕| 中文字幕精品久久| 精品一区二区久久|