1. 程式人生 > >網路程式設計(23)—— socket客戶端連線上服務端是在listen之後而非在accept之時

網路程式設計(23)—— socket客戶端連線上服務端是在listen之後而非在accept之時

 我們知道在TCPIP中客戶端同服務端建立連線,需要三次握手:

   第一次:客戶端向服務端請求發起連線,傳送SYN

   第二次:服務端收到客戶端的請求後,向客戶端回送ACKSYN

   第三次:客戶端接收到服務端可以連線的資訊,再向服務端傳送ACK,表示收到服務端資訊完成連線。

   在剛剛接觸網路程式設計時,很長一段時間都以為只有服務端呼叫accept後,客戶端才會connect成功,但是實際上只要服務端開啟listen,客戶端就會連線成功,並可以傳送資料,下面就通過試驗驗證這一點:

    以下的服務端的程式碼:

#include<stdio.h>
#inlude<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<time.h>
#define BUF_SIZE 100 

void print_time();
void error_handling(char* message);

int main(int argc,char* argv[])
{
	int serv_sock,clnt_sock;
	struct sockaddr_in serv_addr,clnt_addr;
	int clnt_addr_sz;
	int str_len,i,j;
	char buf[BUF_SIZE];

	if(argc!=2)
	{
		printf("Usage %s <port>\n",argv[0]);
		exit(1);
	}
	//建立socket
	serv_sock=socket(AF_INET,SOCK_STREAM,0);
	if(serv_sock==-1)
		error_handling("socket error");

	//填充地址資訊
	memset(&serv_addr,0,sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	serv_addr.sin_port=htons(atoi(argv[1]));

	//socket和ip地址的繫結
	if(bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
		error_handling("bind error");

	//開啟監聽
	if(listen(serv_sock,5)==-1)
		error_handling(" listen error");
    	sleep(10);
	fputs("end sleep:",stdout);
	print_time();
	for(i=0;i<5;i++)
	{
		clnt_addr_sz=sizeof(clnt_addr);
		clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_sz);
		if(clnt_sock==-1)
			error_handling("accept error");
		else
			printf("clnt:%s connected\n",inet_ntoa(clnt_addr.sin_addr));
		//接受資料
		while(1)
		{
			str_len=read(clnt_sock,buf,BUF_SIZE);
				write(clnt_sock,buf,str_len);
			memset(buf,0,sizeof(buf));
			if(str_len<=0)
				break;
		}
		close(clnt_sock);
	}
	close(serv_sock);
	return 0;
}
void print_time()
{
    time_t now=time(0);
	struct tm* ptm=localtime(&now);
	char buf[256]={0};
	sprintf(buf,"time now:[%02d-%02d-%02d %02d:%02d:%02d]",
			ptm->tm_year+1900,
			ptm->tm_mon+1,
			ptm->tm_mday,
			ptm->tm_hour,
			ptm->tm_min,
			ptm->tm_sec);
	puts(buf);
}



void error_handling(char* message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}

  請留意在呼叫 listen()開啟監聽後的sleep(10),我們讓伺服器睡了10s中,以此來驗證客戶端程式依然可以正常的connect,同時sleep結束後呼叫了一下print_time(),列印了一下當前的時間。接下來是客戶端的程式碼:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<time.h>
#define BUF_SIZE 100

void print_time();
void error_handling(const char* message);

int main(int argc,char* argv[])
{
	int sock;
	struct sockaddr_in serv_addr;
	int str_len;
	char buf[BUF_SIZE];
	int recv_len=0;
	//建立socket
	sock=socket(AF_INET,SOCK_STREAM,0);
	if(sock==-1)
		error_handling("socket error");
	//準備地址
	memset(&serv_addr,0,sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
	serv_addr.sin_port=htons(atoi(argv[2]));

	//連結
	if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
		error_handling("connect error");
	else
	        puts("connect succeed");
	print_time();
	while(1)
	{
		memset(buf,0,BUF_SIZE);
		fputs("請輸入資料:",stdout);
		fgets(buf,BUF_SIZE,stdin);
		if(!strcmp(buf,"q\n")||!strcmp(buf,"Q/n"))
			break;
		print_time();
		str_len=write(sock,buf,strlen(buf));
		puts("writed");
		print_time();
		sizeof(buf,0,sizeof(buf));
		while(recv_len<str_len)
			recv_len+=read(sock,buf,BUF_SIZE-1);
		buf[str_len]=0;
		printf("伺服器傳回資訊:%s\n",buf);
	}
	close(sock);
	return 0;
}


void print_time()
{
    time_t now=time(0);
	struct tm* ptm=localtime(&now);
	char buf[256]={0};
	sprintf(buf,"time now:[%02d-%02d-%02d %02d:%02d:%02d]",
			ptm->tm_year+1900,
			ptm->tm_mon+1,
			ptm->tm_mday,
			ptm->tm_hour,
			ptm->tm_min,
			ptm->tm_sec);
	puts(buf);
}


void error_handling(const char* message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}	

        我們在進行connect服務端後,如果connect成功,就會打印出來一個“connected succeed”的字串,同時利用print_time打印出當前的時間,只要打印出來的時間早於伺服器中打印出來的時間就證明了我們的猜想,那麼請看列印的結果:


客戶端中:

[[email protected] echoSever]$ ./clnt 127.0.0.1 9190
connect succeed
time now:[2016-10-08 11:31:53]
請輸入資料:

伺服器中:
[[email protected] echoSever]$ ./serv 9190
end sleep:time now:[2016-10-08 11:32:02]
clnt:127.0.0.1 connected

        從上面的結果中,很明顯晚於伺服器中列印的時間明顯晚於客戶端中的時間,也就是說其實只要伺服器開啟listen,客戶端就可以connect並寫入資料,伺服器端會將客戶端連線資訊和傳送資訊儲存在輸入緩衝區中,等sleep結束就會進行讀取。


        也可以用netstat檢視當前的埠監聽的情況:

[[email protected] echoSever]$ netstat -an >> sock.txt



把netstat檢視的監聽情況放到sock.txt中是因為資訊過多,終端無法容納,開啟該檔案可以看到9190埠的socket已經是established





Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本專案:
git clone [email protected]:HymanLiuTS/NetDevelopment.git
獲取本文原始碼:
git checkout NL23



相關推薦

網路程式設計23—— socket客戶連線服務是在listen之後accept

 我們知道在TCPIP中客戶端同服務端建立連線,需要三次握手:    第一次:客戶端向服務端請求發起連線,傳送SYN    第二次:服務端收到客戶端的請求後,向客戶端回送ACK和SYN    第三次:客戶端接收到服務端可以連線的資訊,再向服務端傳送ACK,表示收到服務端資訊

Windows網路程式設計:建立UDP連線和收發訊息

UDP訊息的傳送和接收需要UDP連線,所以,上面的TCP連線已經不適用了,具體的區別主要有: 建立Socket時引數不同建立服務端時不需要listen和accept操作建立客戶端時不需要connect操作伺服器需要bind操作,客戶端不需要。 傳送和接收UDP訊息要用到sendt

Windows網路程式設計:建立TCP連線和收發訊息

先看服務端: // ConsoleApplication3.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #define _WINSOCK_DEPRECATED_NO_WARNINGS //這個宣告要在stdafx.h的後面,但要

網路程式設計——伺服器和客戶資訊的獲取

  目錄 1、字串IP地址和二進位制IP地址結構的轉換 2.套接字檔案描述符的判定 3、IP地址與域名之間的相互轉換 4、協議名稱處理函式 1、字串IP地址和二進位制IP地址結構的轉換 #include <sys/socket.h> #inclu

Socket網路程式設計1 ———— 基於TCP協議的客戶-伺服器socket例項

Socket網路程式設計(1) ———— 基於TCP協議的客戶-伺服器socket例項 1、TCP網路程式設計架構 2、程式碼段 伺服器: #include <stdio.h> #include <stdlib.h&

python------Socket網路程式設計粘包問題

一.socket網路程式設計  粘包:服務端兩次傳送指令在一起,它會把兩次傳送內容合在一起傳送,稱為粘包,從而出現錯誤。 解決方法:(比較low的方法) 有些需要實時更新的,用sleep有延遲,不能這樣解決問題。 解決方法之高階方法: 客戶端: 二.傳送檔案 ftp s

Python Socket網路程式設計初識SocketSocket初步使用

目錄 前言 網路程式設計 實質 IP地址和埠 資料傳輸協議 協議 Socket

Python Socket網路程式設計區域網內和區域網與廣域網的持續通訊

目錄 前言 IP地址 簡介 公有IP 私有IP 區域網之間網路通訊 前提 功能描述

Socket網路程式設計

此文使用的協議是 TCP       首先要寫入以下程式碼,不然很多函式都用不了 #include <WinSock2.h> #pragma comment(lib, "ws2_32.lib")   ●伺服器端    

Windows網路程式設計Socket簡介

Socket簡介 Socket被稱為套接字,描述了IP和埠等資訊,是一個通訊鏈的控制代碼。 微軟專門開發了一套支援多種網路協議的網路程式設計介面,叫做Winsock,Winsock是Windos SDK的一部分,全稱Windows Sockets API。它對多種協議做了封裝,S

c++ 網路程式設計TCP/IP LINUX/windows下 多執行緒超詳細教程 以及 多執行緒實現服務

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <process.h> #include <winsock2.h> #include <win

c++ 網路程式設計TCP/IP LINUX/windows下 多執行緒超詳細教程 以及 多執行緒實現服務

原文作者:aircraft 原文連結:https://www.cnblogs.com/DOMLX/p/9661012.html  先講Linux下(windows下在後面可以直接跳到後面看): 一.執行緒基本概念 前面我們講過多程序伺服器,但我們知道它開銷很大

Linux網路程式設計:一個簡單的socket程式

伺服器: /* *tcp_server.c */ #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include

Java 網路程式設計 使用TCP/IP的套接字Socket進行通訊

使用TCP/IP的套接字(Socket)進行通訊 套接字Socket的引入   為了能夠方便地開發網路應用軟體,由美國伯克利大學在Unix上推出了一種應用程式訪問通訊協議的作業系統用呼叫socket(套接字)。   socket的出現,使程式設計師可以很方便地訪問TCP/

網路程式設計53—— Windows下使用WSAAsyncSelect實現視窗處理socket訊息

一、引言        上一文中我們介紹了使用WSAEventSelect實現非同步通知IO的方法,本文我們主要討論下使用WSAAsyncSelect處理socket的方法。本文的主要目標,是建立一個帶介面的回聲服務端,接收並返回客戶端傳過來的字串,並在介面上顯示該字串。為

Socket網路程式設計UDP協議實現聊天工具

package UDP_chat; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.List; import java.awt.Panel; import java.awt.TextField; import jav

C#網路程式設計------多執行緒socket例項

伺服器端程式碼: using System;   using System.Collections.Generic;   using System.Linq;   using System.Text;   using System.Net;   using Sys

網路程式設計——WebSocket 和socket、HTTP的區別和聯絡

一、WebSocket 是什麼? WebSocket是HTML5規範提出的一種協議;目前除了完犢子的IE瀏覽器,其他瀏覽器都基本支援。他是一種協議,萬變不離其宗,也是基於TCP協議的;和HTTP協議是並存的兩種協議。 WebSocket是HTML5中的協議。HTML5 Web

python學習-網路程式設計

udp的接收和傳送資料程式碼: udp的傳送資料程式碼如下: import socket def main():     #建立套接字     udp_socket = socket.socket(socket.AF_I

網路程式設計:埠那些事兒

TCP和UDP協議都存在一個叫做埠的東西,但埠卻不是IP協議的一部分。 埠被設計出來主要是為了給協議棧和應用對應: 協議棧用埠號將資料分配給不同的應用層程式 應用層程式用埠號去區分不同的連線,參見之前提到過的“四元組” TCP和UDP協議都使用了埠號(Port num