winsock教程- windows下的socket编程(c语言实现)

winsock教程- windows下的socket编程(c语言实现)

使用winsock进行socket 编程

这是一个学习windows下socket编程(c语言)的快速指南。这是因为一下代码片段只能运行在windows下。windows API中的socket编程部分叫做winsock。

你电脑上做出的任何网络通信背后基本上都有socket,它是一个网络的基本组成部分。举个例子说当你在浏览器键入www.google.com的时候,socket连接到google.com并且取回那个页面然后才能显示给你。任何聊天软件也是一样的比如skype gtalk

在你开始之前

这个教程假定你有基本的c编程能力,并且请下载Visuall c++ 2010 express

初始化winsock

首先需要这样初始化


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

/*

    Initialise Winsock

*/

#include<stdio.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

int main(int argc , char *argv[])

{

    WSADATA wsa;

    

    printf("\n初始化中Initialising Winsock...");

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)

    {

        printf("Failed. Error Code : %d",WSAGetLastError());

        return 1;

    }

    

    printf("初始化成功Initialised.");

    return 0;

}

winsock函数包含在winsock2.H。你还需要link ws2_32.lib到你的程序以便使用这些函数。

WSAStartup函数用于启动或者初始化winsock库。它需要两个参数,第一个指定我们想要加载的版本,第二个是一个WSADATA结构体,它可以在winsock加载之后储存额外的数据。

如果有错误产生的话,WSAStartup会返回一个非零值。并且可以用WSAGetLastError 来获取更多信息。

好了,下一步就来创建一个socket。

创建一个socket

使用socket() 函数来创建一个socket,示例代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

/*

    Create a TCP socket

*/

#include<stdio.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

int main(int argc , char *argv[])

{

    WSADATA wsa;

    SOCKET s;

    printf("\n初始化Initialising Winsock...");

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)

    {

        printf("失败Failed. Error Code : %d",WSAGetLastError());

        return 1;

    }

    

    printf("Initialised.\n");

    

    

    if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)

    {

        printf("创建失败Could not create socket : %d" , WSAGetLastError());

    }

    printf("成功Socket created.\n");

    return 0;

}

socket() 用于函数创建socket并且返回一个socket描述符,该描述符可以在其他网络命令中使用,上述代码将会创建像下面这样的socket:

Address Family : AF_INET 表示IPv4
Type : SOCK_STREAM 面向TCP协议的
Protocol : 0 [ or IPPROTO_TCP , IPPROTO_UDP ]

这里here可以获取更多信息

下一步我们可以连接到google.com。


注意

除了SOCK_STREAM 类型以外还有一种类型叫做
SOCK_DGRAMSOCK_DGRAM用于表示UDP协议。 这种类型的socket是无连接的,在这个教程中我们只关注SOCK_stReAM或者TCPsocket。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

/*

    Create a TCP socket

*/

#include<stdio.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

int main(int argc , char *argv[])

{

    WSADATA wsa;

    SOCKET s;

    struct sockaddr_in server;

    char *message;

    printf("\nInitialising Winsock...");

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)

    {

        printf("Failed. Error Code : %d",WSAGetLastError());

        return 1;

    }

    

    printf("Initialised.\n");

    

    //Create a socket

    if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)

    {

        printf("Could not create socket : %d" , WSAGetLastError());

    }

    printf("Socket created.\n");

    

    

    server.sin_addr.s_addr = inet_addr("74.125.235.20");

    server.sin_family = AF_INET;

    server.sin_port = htons( 80 );

    //Connect to remote server

    if (connect(s , (struct sockaddr *)&server , sizeof(server)) < 0)

    {

        puts("connect error");

        return 1;

    }

    

    puts("Connected");

    

    //Send some data

    message = "GET / HTTP/1.1\r\n\r\n";

    if( send(s , message , strlen(message) , 0) < 0)

    {

        puts("Send failed");

        return 1;

    }

    puts("Data Send\n");

    return 0;

}

发送接收数据

函数send recv用来发送接收数据。下面的例子我们将发送请求给google.com并且接收返回的数据。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

/*

    Create a TCP socket

*/

#include<stdio.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

int main(int argc , char *argv[])

{

    WSADATA wsa;

    SOCKET s;

    struct sockaddr_in server;

    char *message , server_reply[2000];

    int recv_size;

    printf("\nInitialising Winsock...");

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)

    {

        printf("Failed. Error Code : %d",WSAGetLastError());

        return 1;

    }

    

    printf("Initialised.\n");

    

    //Create a socket

    if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)

    {

        printf("Could not create socket : %d" , WSAGetLastError());

    }

    printf("Socket created.\n");

    

    

    server.sin_addr.s_addr = inet_addr("74.125.235.20");

    server.sin_family = AF_INET;

    server.sin_port = htons( 80 );

    //Connect to remote server

    if (connect(s , (struct sockaddr *)&server , sizeof(server)) < 0)

    {

        puts("connect error");

        return 1;

    }

    

    puts("Connected");

    

    //Send some data发送请求

    message = "GET / HTTP/1.1\r\n\r\n";

    if( send(s , message , strlen(message) , 0) < 0)

    {

        puts("Send failed");

        return 1;

    }

    puts("Data Send\n");

    

    //Receive a reply from the server接收html文档

    if((recv_size = recv(s , server_reply , 2000 , 0)) == SOCKET_ERROR)

    {

        puts("recv failed");

    }

    

    puts("Reply received\n");

    //Add a NULL terminating character to make it a proper string before printing

    server_reply[recv_size] = ‘\0‘;

    puts(server_reply);

    return 0;

}

一下是上面代码的输出:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

Initialising Winsock...Initialised.

Socket created.

Connected已连接

Data Send数据已发送

Reply received已收到

HTTP/1.1 302 Found

Location: http://www.google.co.in/

Cache-Control: private

Content-Type: text/html; charset=UTF-8

Set-Cookie: PREF=ID=7da819edfd7af808:FF=0:TM=1324882923:LM=1324882923:S=PdlMu0TE

E3QKrmdB; expires=Wed, 25-Dec-2013 07:02:03 GMT; path=/; domain=.google.com

Date: Mon, 26 Dec 2011 07:02:03 GMT

Server: gws

Content-Length: 221

X-XSS-Protection: 1; mode=block

X-Frame-Options: SAMEORIGIN

<HTML>

<HEAD>

<meta http-equiv="content-type" content="text/html;charset=utf-8">

<TITLE>302 Moved</TITLE>

</HEAD>

<BODY>

<H1>302 Moved</H1>

The document has moved

<A HREF="http://www.google.co.in/">here</A>.

</BODY></HTML>

Press any key to continue按任意键继续

我们可以看到google.com返回的数据,它看起来好像是html,是的那就是html。google返回了我们想要的数据,太简单啦!

现在我们已经收到了我们的数据,现在我们该关掉socket了。

关闭socket

closesocket函数是用来关闭socket的,另外WSACleanup 函数也必须被调用来卸载winsock库ws2_32.dll。


1

2

closesocket(s);

WSACleanup();

.就是这些

由主机名/域名获得ip地址

当要连接到一个远程主机的时候,必须要知道它的iP地址。gethostbyname 就是用来获取ip的。它使用域名作为参数并且返回一个hostent结构体,该结构体包含ip信息。该结构体包含于netdb.h中,结构如下:

 注意

gethostbyname 函数已被废弃,取而代之的是getaddrinfo函数。强烈建议winsock2的开发者使用getaddrinfo而不是gethostbyname


1

2

3

4

5

6

7

8

9

/* Description of data base entry for a single host.  */

struct hostent

{

  char *h_name;         /* Official name of host.  官方主机名*/

  char **h_aliases;     /* Alias list.  别名列表*/

  int h_addrtype;       /* Host address type.  主机地址类型*/

  int h_length;         /* Length of address.  地址长度*/

  char **h_addr_list;       /* List of addresses from name server. 主机地址列表 */

};

h_addr_list 包含ip地址,如下代码演示如何获取地址。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

/*

    Get IP address from domain name

*/

#include<stdio.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

int main(int argc , char *argv[])

{

    WSADATA wsa;

    char *hostname = "www.google.com";

    char ip[100];

    struct hostent *hostEntry;

    struct in_addr **addr_list;

    int i;

    

    printf("\nInitialising Winsock...");

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)

    {

        printf("Failed. Error Code : %d",WSAGetLastError());

        return 1;

    }

    

    printf("Initialised.\n");

    

        

    if ( (hostEntry = gethostbyname( hostname ) ) == NULL)

    {

        //gethostbyname failed

        printf("gethostbyname failed : %d" , WSAGetLastError());

        return 1;

    }

    

    //Cast the h_addr_list to in_addr , since h_addr_list also has the ip address in long format only

    addr_list = (struct in_addr **)hostEntry->h_addr_list;

    

    for(i = 0; addr_list[i] != NULL; i++)

    {

        //Return the first one;

        strcpy(ip , inet_ntoa(*addr_list[i]) );

    }

    

    printf("%s resolved to : %s\n" , hostname , ip);

    return 0;

    return 0;

}

代码输出如下


1

www.google.com resolved to : 74.125.235.20

h_addr_list 可以把长地址转换成点分地址。

服务器相关概念

OK现在我们来看一下服务器相关的东西,服务器基本上就做了如下几件事:

1. 打开socket
2. 绑定到一个地址和端口
3. Listen 监听进来的连接
4. Accept 接受连接
5. Read/Send读取数据/发送数据

We have already learnt how to open a socket. So the next thing would be to bind it.

Bind a socket

Function bind can be used to bind a socket to a particular address and port. It needs a sockaddr_in structure similar to connect function.

Lets see a code example :


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

/*

    Bind socket to port 8888 on localhost

*/

#include<stdio.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

int main(int argc , char *argv[])

{

    WSADATA wsa;

    SOCKET s;

    struct sockaddr_in server;

    printf("\nInitialising Winsock...");

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)

    {

        printf("Failed. Error Code : %d",WSAGetLastError());

        return 1;

    }

    

    printf("Initialised.\n");

    

    //Create a socket

    if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)

    {

        printf("Could not create socket : %d" , WSAGetLastError());

    }

    printf("Socket created.\n");

    

    //Prepare the sockaddr_in structure

    server.sin_family = AF_INET;

    server.sin_addr.s_addr = INADDR_ANY;

    server.sin_port = htons( 8888 );

    

    //Bind

    if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)

    {

        printf("Bind failed with error code : %d" , WSAGetLastError());

    }

    

    puts("Bind done");

    

    closesocket(s);

    return 0;

}

Now that bind is done, its time to make the socket listen to connections. We bind a socket to a particular IP address and a certain port number. By doing this we ensure that all incoming data which is directed towards this port number is received by this application.

This makes it obvious that you cannot have 2 sockets bound to the same port.

Listen for connections

After binding a socket to a port the next thing we need to do is listen for connections. For this we need to put the socket in listening mode. Function listen is used to put the socket in listening mode. Just add the following line after bind.


1

2

//Listen

listen(s , 3);

Thats all. Now comes the main part of accepting new connections.

Accept connection

Function accept is used for this. Here is the code


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

/*

    Bind socket to port 8888 on localhost

*/

#include<stdio.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

int main(int argc , char *argv[])

{

    WSADATA wsa;

    SOCKET s , new_socket;

    struct sockaddr_in server , client;

    int c;

    printf("\nInitialising Winsock...");

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)

    {

        printf("Failed. Error Code : %d",WSAGetLastError());

        return 1;

    }

    

    printf("Initialised.\n");

    

    //Create a socket

    if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)

    {

        printf("Could not create socket : %d" , WSAGetLastError());

    }

    printf("Socket created.\n");

    

    //Prepare the sockaddr_in structure

    server.sin_family = AF_INET;

    server.sin_addr.s_addr = INADDR_ANY;

    server.sin_port = htons( 8888 );

    

    //Bind

    if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)

    {

        printf("Bind failed with error code : %d" , WSAGetLastError());

    }

    

    puts("Bind done");

    

    //Listen to incoming connections

    listen(s , 3);

    

    //Accept and incoming connection

    puts("Waiting for incoming connections...");

    

    c = sizeof(struct sockaddr_in);

    new_socket = accept(s , (struct sockaddr *)&client, &c);

    if (new_socket == INVALID_SOCKET)

    {

        printf("accept failed with error code : %d" , WSAGetLastError());

    }

    

    puts("Connection accepted");

    closesocket(s);

    WSACleanup();

    return 0;

}

Output

Run the program. It should show


1

2

3

4

Initialising Winsock...Initialised.

Socket created.

Bind done

Waiting for incoming connections...

So now this program is waiting for incoming connections on port 8888. Dont close this program , keep it running.
Now a client can connect to it on this port. We shall use the telnet client for testing this. Open a terminal and type


1

telnet localhost 8888

And the server output will show


1

2

3

4

5

6

Initialising Winsock...Initialised.

Socket created.

Bind done

Waiting for incoming connections...

Connection accepted

Press any key to continue

So we can see that the client connected to the server. Try the above process till you get it perfect.

Note

You can get the ip address of client and the port of connection by using the sockaddr_in structure passed to accept function. It is very simple :


1

2

char *client_ip = inet_ntoa(client.sin_addr);

int client_port = ntohs(client.sin_port);

We accepted an incoming connection but closed it immediately. This was not very productive. There are lots of things that can be done after an incoming connection is established. Afterall the connection was established for the purpose of communication. So lets reply to the client.

Here is an example :


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

/*

    Bind socket to port 8888 on localhost

*/

#include<io.h>

#include<stdio.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

int main(int argc , char *argv[])

{

    WSADATA wsa;

    SOCKET s , new_socket;

    struct sockaddr_in server , client;

    int c;

    char *message;

    printf("\nInitialising Winsock...");

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)

    {

        printf("Failed. Error Code : %d",WSAGetLastError());

        return 1;

    }

    

    printf("Initialised.\n");

    

    //Create a socket

    if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)

    {

        printf("Could not create socket : %d" , WSAGetLastError());

    }

    printf("Socket created.\n");

    

    //Prepare the sockaddr_in structure

    server.sin_family = AF_INET;

    server.sin_addr.s_addr = INADDR_ANY;

    server.sin_port = htons( 8888 );

    

    //Bind

    if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)

    {

        printf("Bind failed with error code : %d" , WSAGetLastError());

    }

    

    puts("Bind done");

    //Listen to incoming connections

    listen(s , 3);

    

    //Accept and incoming connection

    puts("Waiting for incoming connections...");

    

    c = sizeof(struct sockaddr_in);

    new_socket = accept(s , (struct sockaddr *)&client, &c);

    if (new_socket == INVALID_SOCKET)

    {

        printf("accept failed with error code : %d" , WSAGetLastError());

    }

    

    puts("Connection accepted");

    //Reply to client

    message = "Hello Client , I have received your connection. But I have to go now, bye\n";

    send(new_socket , message , strlen(message) , 0);

    

    getchar();

    closesocket(s);

    WSACleanup();

    

    return 0;

}

Run the above code in 1 terminal. And connect to this server using telnet from another terminal and you should see this :


1

Hello Client , I have received your connection. But I have to go now, bye

So the client(telnet) received a reply from server. We had to use a getchar because otherwise the output would scroll out of the client terminal without waiting

We can see that the connection is closed immediately after that simply because the server program ends after accepting and sending reply. A server like www.google.com is always up to accept incoming connections.

It means that a server is supposed to be running all the time. Afterall its a server meant to serve. So we need to keep our server RUNNING non-stop. The simplest way to do this is to put the accept in a loop so that it can receive incoming connections all the time.

Live Server

So a live server will be alive for all time. Lets code this up :


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

/*

    Live Server on port 8888

*/

#include<io.h>

#include<stdio.h>

#include<winsock2.h>

#pragma comment(lib,"ws2_32.lib") //Winsock Library

int main(int argc , char *argv[])

{

    WSADATA wsa;

    SOCKET s , new_socket;

    struct sockaddr_in server , client;

    int c;

    char *message;

    printf("\nInitialising Winsock...");

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)

    {

        printf("Failed. Error Code : %d",WSAGetLastError());

        return 1;

    }

    

    printf("Initialised.\n");

    

    //Create a socket

    if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)

    {

        printf("Could not create socket : %d" , WSAGetLastError());

    }

    printf("Socket created.\n");

    

    //Prepare the sockaddr_in structure

    server.sin_family = AF_INET;

    server.sin_addr.s_addr = INADDR_ANY;

    server.sin_port = htons( 8888 );

    

    //Bind

    if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)

    {

        printf("Bind failed with error code : %d" , WSAGetLastError());

        exit(EXIT_FAILURE);

    }

    

    puts("Bind done");

    //Listen to incoming connections

    listen(s , 3);

    

    //Accept and incoming connection

    puts("Waiting for incoming connections...");

    

    c = sizeof(struct sockaddr_in);

    

    while( (new_socket = accept(s , (struct sockaddr *)&client, &c)) != INVALID_SOCKET )

    {

        puts("Connection accepted");

        

        //Reply to the client

        message = "Hello Client , I have received your connection. But I have to go now, bye\n";

        send(new_socket , message , strlen(message) , 0);

    }

    

    if (new_socket == INVALID_SOCKET)

    {

        printf("accept failed with error code : %d" , WSAGetLastError());

        return 1;

    }

    closesocket(s);

    WSACleanup();

    

    return 0;

}

We havent done a lot there. Just the accept was put in a loop.

Now run the program in 1 terminal , and open 3 other terminals. From each of the 3 terminal do a telnet to the server port.

Run telnet like this


1

C:\>telnet


1

2

3

Welcome to Microsoft Telnet Client

Escape Character is ‘CTRL+]‘

Microsoft Telnet> open localhost 8888


1

Hello Client , I have received your connection. But I have to go now, bye

And the server terminal would show


1

2

3

4

5

6

Initialising Winsock...Initialised.

Socket created.

Bind done

Waiting for incoming connections...

Connection accepted

Connection accepted

So now the server is running nonstop and the telnet terminals are also connected nonstop. Now close the server program.
All telnet terminals would show "Connection to host lost."
Good so far. But still there is not effective communication between the server and the client.

The server program accepts connections in a loop and just send them a reply, after that it does nothing with them. Also it is not able to handle more than 1 connection at a time. So now its time to handle the connections , and handle multiple connections together.

Handling Connections

To handle every connection we need a separate handling code to run along with the main server accepting connections.
One way to achieve this is using threads. The main server program accepts a connection and creates a new thread to handle communication for the connection, and then the server goes back to accept more connections.

We shall now use threads to create handlers for each connection the server accepts. Lets do it pal.


1

Run the above server and open 3 terminals like before. Now the server will create a thread for each client connecting to it.

The telnet terminals would show :


1

This one looks good , but the communication handler is also quite dumb. After the greeting it terminates. It should stay alive and keep communicating with the client.

One way to do this is by making the connection handler wait for some message from a client as long as the client is connected. If the client disconnects , the connection handler ends.

So the connection handler can be rewritten like this :


1

The above connection handler takes some input from the client and replies back with the same. Simple! Here is how the telnet output might look


1

So now we have a server thats communicative. Thats useful now.

Conclusion

The winsock api is quite similar to Linux sockets in terms of function name and structures. Few differences exist like :

1. Winsock needs to be initialised with the WSAStartup function. No such thing in linux.

2. Header file names are different. Winsock needs winsock2.h , whereas Linux needs socket.h , apra/inet.h , unistd.h and many others.

3. Winsock function to close a socket is closesocket , whereas on Linux it is close.
On Winsock WSACleanup must also be called to unload the winsock dll.

4. On winsock the error number is fetched by the function WSAGetLastError(). On Linux the errno variable from errno.h file is filled with the error number.

And there are many more differences as we go deep.

By now you must have learned the basics of socket programming in C. You can try out some experiments like writing a chat client or something similar.

If you think that the tutorial needs some addons or improvements or any of the code snippets above dont work then feel free to make a comment below so that it gets fixed.

Last Updated On : 12th December 2012

socket programmingwinsockwinsock tutorial

Handle multiple connections - Asynchronous socket programming

For this server to be any useful, it must be able to accept multiple incoming connections and keep processing them till the clients want. So the next attempt shall be to write a server that can handle multiple connections and tackle all of them simultaneously.

There are many ways to handle multiple client connections. The first and most intuitive one is using threads. As soon as a client connects, assign a separate thread to process each client. However threads are too much work and difficult to code properly.

There are other techniques like polling. Polling involves monitoring multiple sockets to see if "something" happened on any of them. For example, the server could be monitoring the sockets of 5 connected clients, and as soon as any of them send a message, the server gets notified of the event and then processes it. In this way it can handle multiple sockets. The winsock api provides a function called "select" which can monitor multiple sockets for some activity.

Since we are able to handle all sockets together at once it is called asynchronous socket programming. It is also called event-driven socket programming or select()-based multiplexing.

The select function prototype is like this

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);

The first parameter is a dummy one. The readfds parameter is a pointer to an array of sockets which should be monitored to be readable. This means that if any socket in the readfds set receives some data, it becomes readable. Similarly the writefds sockets would be monitored to be writable and the exceptfds sockets shall be monitored for any error. The last parameter is the timeout parameter, which indicates the length of time which the select function shall wait for before returning.

Now after a select function returns, it re-fills the same readfds array with the readable sockets. Same with writefds and exceptfds. This means that we have to keep calling select function in a loop, and everytime have to prepare our list of readfds, writefds and exceptfds array of sockets to pass.

The socket arrays are variables of type fd_set. fd_set is basically a structure that looks like this

typedef struct fd_set {
  u_int  fd_count;
  SOCKET fd_array[FD_SETSIZE];
} fd_set;

To work with fd_set array the following macros have to be used.

FD_CLR(s, *set) - Removes a socket from an fd_set structure FD_ISSET(s, *set) - Checks if a socket is present in an fd_set structure FD_SET(s, *set) - Adds a socket to an fd_set structure FD_ZERO(*set) - Initializes the set to the null set. This will empty an fd_set structure

Now that is a lot of theory. Lets get to the final code that uses all that theory to get something working.


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

/*

    TCP Echo server example in winsock

    Live Server on port 8888

*/

#include<stdio.h>

#include<winsock2.h>

#pragma comment(lib, "ws2_32.lib") //Winsock Library

int main(int argc , char *argv[])

{

    WSADATA wsa;

    SOCKET master , new_socket , client_socket[30] , s;

    struct sockaddr_in server, address;

    int max_clients = 30 , activity, addrlen, i, valread;

    char *message = "ECHO Daemon v1.0 \r\n";

    

    //size of our receive buffer, this is string length.

    int MAXRECV = 1024;

    //set of socket descriptors

    fd_set readfds;

    //1 extra for null character, string termination

    char *buffer;

    buffer =  (char*) malloc((MAXRECV + 1) * sizeof(char));

    for(i = 0 ; i < 30;i++)

    {

        client_socket[i] = 0;

    }

    printf("\nInitialising Winsock...");

    if (WSAStartup(MAKEWORD(2,2),&wsa) != 0)

    {

        printf("Failed. Error Code : %d",WSAGetLastError());

        exit(EXIT_FAILURE);

    }

    

    printf("Initialised.\n");

    

    //Create a socket

    if((master = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET)

    {

        printf("Could not create socket : %d" , WSAGetLastError());

        exit(EXIT_FAILURE);

    }

    printf("Socket created.\n");

    

    //Prepare the sockaddr_in structure

    server.sin_family = AF_INET;

    server.sin_addr.s_addr = INADDR_ANY;

    server.sin_port = htons( 8888 );

    

    //Bind

    if( bind(master ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR)

    {

        printf("Bind failed with error code : %d" , WSAGetLastError());

        exit(EXIT_FAILURE);

    }

    

    puts("Bind done");

    //Listen to incoming connections

    listen(master , 3);

    

    //Accept and incoming connection

    puts("Waiting for incoming connections...");

    

    addrlen = sizeof(struct sockaddr_in);

    

    while(TRUE)

    {

        //clear the socket fd set

        FD_ZERO(&readfds);

 

        //add master socket to fd set

        FD_SET(master, &readfds);

        

        //add child sockets to fd set

        for (  i = 0 ; i < max_clients ; i++)

        {

            s = client_socket[i];

            if(s > 0)

            {

                FD_SET( s , &readfds);

            }

        }

        

        //wait for an activity on any of the sockets, timeout is NULL , so wait indefinitely

        activity = select( 0 , &readfds , NULL , NULL , NULL);

   

        if ( activity == SOCKET_ERROR )

        {

            printf("select call failed with error code : %d" , WSAGetLastError());

            exit(EXIT_FAILURE);

        }

         

        //If something happened on the master socket , then its an incoming connection

        if (FD_ISSET(master , &readfds))

        {

            if ((new_socket = accept(master , (struct sockaddr *)&address, (int *)&addrlen))<0)

            {

                perror("accept");

                exit(EXIT_FAILURE);

            }

         

            //inform user of socket number - used in send and receive commands

            printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));

       

            //send new connection greeting message

            if( send(new_socket, message, strlen(message), 0) != strlen(message) )

            {

                perror("send failed");

            }

             

            puts("Welcome message sent successfully");

             

            //add new socket to array of sockets

            for (i = 0; i < max_clients; i++)

            {

                if (client_socket[i] == 0)

                {

                    client_socket[i] = new_socket;

                    printf("Adding to list of sockets at index %d \n" , i);

                    break;

                }

            }

        }

         

        //else its some IO operation on some other socket :)

        for (i = 0; i < max_clients; i++)

        {

            s = client_socket[i];

            //if client presend in read sockets            

            if (FD_ISSET( s , &readfds))

            {

                //get details of the client

                getpeername(s , (struct sockaddr*)&address , (int*)&addrlen);

                //Check if it was for closing , and also read the incoming message

                //recv does not place a null terminator at the end of the string (whilst printf %s assumes there is one).

                valread = recv( s , buffer, MAXRECV, 0);

                

                if( valread == SOCKET_ERROR)

                {

                    int error_code = WSAGetLastError();

                    if(error_code == WSAECONNRESET)

                    {

                        //Somebody disconnected , get his details and print

                        printf("Host disconnected unexpectedly , ip %s , port %d \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));

                     

                        //Close the socket and mark as 0 in list for reuse

                        closesocket( s );

                        client_socket[i] = 0;

                    }

                    else

                    {

                        printf("recv failed with error code : %d" , error_code);

                    }

                }

                if ( valread == 0)

                {

                    //Somebody disconnected , get his details and print

                    printf("Host disconnected , ip %s , port %d \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));

                     

                    //Close the socket and mark as 0 in list for reuse

                    closesocket( s );

                    client_socket[i] = 0;

                }

                 

                //Echo back the message that came in

                else

                {

                    //add null character, if you want to use with printf/puts or other string handling functions

                    buffer[valread] = ‘\0‘;

                    printf("%s:%d - %s \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port), buffer);

                    send( s , buffer , valread , 0 );

                }

            }

        }

    }

    

    closesocket(s);

    WSACleanup();

    

    return 0;

}

Does that look like a big program. Compile and run it. It should show an output like this

Initialising Winsock...Initialised.
Socket created.
Bind done
Waiting for incoming connections...

Now the socket server is ready and waiting for incoming connection. At this point we need to connect to it using some client like telnet. But wait, we are not going to use telnet. Telnet has a problem that it always operates in character mode and that will screw up our interaction with this simple program. So get another utility called putty or ncat. Ncat is the netcat version that comes with nmap. Download it from their website. Or download puttytel , the putty telnet.

If you are using ncat then connect to the socket server like this

C:\>ncat localhost 8888

If you are using puttytel, the launch it and go to Connection > Telnet and select Passive mode. This will make putty line mode. Then come back to Session tab and enter the hostname and port and click open. it will connect to the server and start a black telnet like terminal.

Once the client program is connected with the server, try sending some message by typing first and then hit enter. The server will reply back with the same message.

C:\>ncat localhost 8888
ECHO Daemon v1.0
hello
hello
how are you
how are you
i am fine
i am fine

The server terminal would look like this

Initialising Winsock...Initialised.
Socket created.
Bind done
Waiting for incoming connections...
New connection , socket fd is 3972 , ip is : 127.0.0.1 , port : 1129
Welcome message sent successfully
Adding to list of sockets at index 0
127.0.0.1:1129 - hello

127.0.0.1:1129 - how are you

127.0.0.1:1129 - i am fine

And now, try to open multiple client terminals and connect at the same time to server. The server would be able to process requests from all the clients together.

Initialising Winsock...Initialised.
Socket created.
Bind done
Waiting for incoming connections...
New connection , socket fd is 3972 , ip is : 127.0.0.1 , port : 1129
Welcome message sent successfully
Adding to list of sockets at index 0
127.0.0.1:1129 - hello

127.0.0.1:1129 - how are you

127.0.0.1:1129 - i am fine

New connection , socket fd is 3956 , ip is : 127.0.0.1 , port : 1130
Welcome message sent successfully
Adding to list of sockets at index 1
127.0.0.1:1130 - i am the second client

New connection , socket fd is 3944 , ip is : 127.0.0.1 , port : 1131
Welcome message sent successfully
Adding to list of sockets at index 2
127.0.0.1:1131 - and i am the third

127.0.0.1:1131 - ha ha ha

Now thats a long run. I will go and take a cup of coffee, and meanwhile you check if the programs are running fine.

Last Updated On : 28th March 2013

socket servertcpwinsock

winsock教程- windows下的socket编程(c语言实现),布布扣,bubuko.com

时间: 2024-10-12 11:27:23

winsock教程- windows下的socket编程(c语言实现)的相关文章

Linux下TCP网络编程与基于Windows下C#socket编程间通信

一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使socket处于被动的监听模式,并为该  socket  建立一个输入数据队列,将到达的服务器, 请求保存在此队列中,直到程序处理他们. Accept():让服务器接收客户的连接请求. Connect():客户端使用connect函数来配置 socket并与远端服务器建立一个 TCP 连接. Clos

Linux下TCP网络编程与基于Windows下C#socket编程之间通信

一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使socket处于被动的监听模式,并为该  socket  建立一个输入 数据队列,将到达的服务器, 请求保存在此队列中,直到程序处理他们. Accept():让服务器接收客户的连接请求. Connect():客户端使用connect函数来配置 socket并与远端服务器建立一个 TCP 连接. Clo

windows下的socket网络编程(入门级)

windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考. windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作.还有windows下编译的时候需要连接ws32_lib库. 大致过程如下 1.初始

windows下的socket网络编程

windows下的socket网络编程 windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考. windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作.还有windows下编译的时候需要连接ws

一个复杂的Windows下的socket程序

上节演示了 Linux 下的 socket 程序,这节来看一下 Windows 下的 socket 程序.异样,server.cpp 为效劳器端代码,client 为客户端代码.效劳器端代码 server.cpp: #include <stdio.h> #include <winsock2.h> #pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll int main(){ //初始化 DLL WSADATA ws

在windows下的QT编程中的_TCHAR与QString之间的转换

由于在windows下的QT编程中,如果涉及到使用微软的API,那么不可避免使用_TCHAR这些类型,因此在网上查了一下,其中一个老外的论坛有人给出了这个转换,因此在这里做一下笔记 : )#ifdef UNICODE #define QStringToTCHAR(x)     (wchar_t*) x.utf16() #define PQStringToTCHAR(x)    (wchar_t*) x->utf16() #define TCHARToQString(x)     QString:

一个简单的Windows下的socket程序

服务器端代码server.cpp: 1 #include <stdio.h> 2 #include <WinSock2.h> 3 #pragma comment(lib,"ws2_32.lib") //加载ws2_32.dll 4 5 int main() 6 { 7 //初始化DLL 8 /************************************************************************/ 9 /*调用WSASta

Python下的socket编程

首先需要说明的一点是:这里并不会记录很深奥的socket编程,只是会分析一个最简单的socket编程聊天室下的几种特殊异常情况的处理,代码如下: 服务端: 1 import socket 2 3 HOST = "" 4 PORT = 8870 5 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 sk.bind((HOST, PORT)) 7 sk.listen(5) 8 while True: 9 print("

基于TCP协议下的socket编程

socket: TCP/IP协议中一个端口号和一个IP地址绑定在一起就生成一个socket就表示了网络中唯一的一个进程,它是全双工的工作方式. 基于TCP的socket编程 函数的使用: 1.socket()         #include <sys/types.h>          /* See NOTES */        #include <sys/socket.h>        int socket(int domain, int type, int protoco