转:http://www.linuxforu.com/2011/09/creating-your-own-server-the-socket-api-part-2/

By Pankaj Tanwar on September 1, 2011 in CodingDevelopers · 0 Comments

Earlier, we created a simple server and client program using the socket API. This time, we’ll first start with a program, and then explain what’s going on. So start up your systems, and get ready to go deeper into socket programming.

As mentioned, let’s dive straight into the code.

The IPv6 version of the server

Here’s the IPv6 version of the server we created in the previous article. There are no big changes, except the appearance of “6″ in the code. Let’s name it serverin6.c:

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
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
    int sfd, cfd;
    char ch;
    socklen_t len;
    struct sockaddr_in6 saddr, caddr;
    sfd= socket(AF_INET6, SOCK_STREAM, 0);
    saddr.sin6_family=AF_INET6;
    saddr.sin6_addr=in6addr_any;
    saddr.sin6_port=htons(1205);
    bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr));
    listen(sfd, 5);
    while(1) {
        printf("Waiting...n");
        len=sizeof(cfd);
        cfd=accept(sfd, (struct sockaddr *)&caddr, &len);
        if(read(cfd, &ch, 1)<0) perror("read");
        ch++;
        if(write(cfd, &ch, 1)<0) perror("write");
        close(cfd);
    }
}

Now let’s review the differences. The first is at Line 6 (sockaddr_in6) and is easily understood; for IPv6 addresses, we need to store the address, port and address family in this address structure, as we have done in lines 13, 14 and 15. At Line 14, in6addr_any is a wildcard for all IPv6 addresses, like INADDR_ANY in IPv4. There’s also a change in accept(), which can be easily understood. And here’s our server working with IPv6. You can set up your IPv6 using the following command as root:

ip -f inet6 addr add face:1f::ea54:a dev eth0

Here, -f specifies the family (inet6), and addr is for the address — we added face:1f::ea54:a(while writing IPv6 addresses, leading zeros can be dropped — the above address is actuallyface:001f::ea54:000a). You can give any address; and to randomise it, you can use your MAC address. The dev parameter specifies the device we are setting the address for — in this case,eth0. You can check the results using the ifconfig command.

Compile and run the server as follows:

cc serverin6.c -o serverin6
./serverin6

Before writing the client, you can see that it even works with our IPv4 client, as the IPv4 address is also pointing to the same machine. For every server, we can use telnet to test if it’s working as expected — for example:

telnet localhost 1205

Type in the character you want to send to the server and press Enter, and the server will reply with the next ASCII character, and close the connection. Remember, for IPv4, localhost is 127.0.0.1, and for IPv6, it is ::1.

Client (IPv6 version)

Now let’s write the IPv6 version client, and name it clientin6.c:

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
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char **argv)
{
    int cfd;
    struct sockaddr_in6 addr;
    char ch;
    if(argc!=3) {
        printf("Usage: %s in6addr charactern", argv[0]);
        return -1;
    }
    if( ! inet_pton(AF_INET6, argv[1], &(addr.sin6_addr))) { /* returns 0 on error */
        printf("Invalid Addressn");
        return -1;
    }
    ch=argv[2][0];      /* Set the character to second argument */
    cfd=socket(AF_INET6, SOCK_STREAM, 0);
    addr.sin6_family=AF_INET6;
    addr.sin6_port=htons(1205);
    if(connect(cfd, (struct sockaddr *)&addr,
    sizeof(addr))<0) {
        perror("connect error");
                return -1;
    }
    if(write(cfd, &ch, 1)<0) perror("write");
    if(read(cfd, &ch, 1)<0) perror("read");
    printf("Server sent: %cn", ch);
    close(cfd);
    return 0;
}

Here, again, the changes are small and self-explanatory — just an added “6″. The client takes the IP address and characters as arguments, so to run it, just type the following:

cc clientin6.c -o clientin6
./clientin6  ::1  d

When d is sent, the server replies with e. The output will be the same as for the last examples, shown in the figures of the last article. To process the address, we have used the inet_pton()function, which we’ll look at in more detail in the next section.

Diving deeper

First, let’s look at the function we used in our client to convert the address from string to numeric. In binary, the IP address 192.168.1.23 will be 11000000 10101000 00000001 00010111 (a 32-bit string of 0′s and 1′s). It will be a 128-bit string for IPv6 (64-bit address). The human-readable addresses 192.168.1.23 or face:1f::ea54:a are the ‘presentation’ form (the p of the function), and n is for the numeric (binary) form. The function is prototyped in <arpa/inet.h> as:

int inet_pton (int family, const char *strptr, void *addrptr);

This function can convert both IPv4 and IPv6 addresses from string form to binary as needed by the machine. The first argument is, of course, the family: AF_INET or AF_INET6. The *strptrargument is the address in string form, and *addrptr will be the destination where the numeric address will be stored; this must be a structure (sin_addr for IPv4, and sin6_addr for IPv6). The function returns 1 on success and 0 on error. The next function is:

const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

This function does exactly the reverse of the previous case; it converts the numeric (addrptr) and stores in the presentation (strptr). The last argument is len, the size/length of the destination variable, to avoid overflow. For ease, netinet/in.h defines the size constants, as follows:

#define INET_ADDRSTRLEN     16
#define INET6_ADDRSTRLEN    46

There are other functions available for the same purpose — inet_aton() and inet_ntoa() — but only for IPv4; therefore, we aren’t using them.

Now let’s look at the functions to convert bytes between the network protocol stack and host to get machine-independent code. Machine architecture is either big-endian or little-endian, i.e., how the machine stores data. The high-order byte at a higher address and low-order byte at a lower address is little-endian. Conversely, high-order byte at a lower address, and low-order byte at a higher address will be big-endian.

For example, if we have a 2-byte integer storing 0x1234 at address 0×0200, it will occupy two bytes: 0x0200 and 0x0201. If, at 0x0200 we have 0x12, and at 0x0201 we have 0x32, the machine is little-endian. If the values are stored the opposite way, the machine is big-endian. The book Unix Network Programming, by W Richard Stevens, gives a program to find out what machine you have. Let’s look at the functions now, defined in netinet/in.h:

uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue);

These two functions return 16-bit and 32-bit values respectively; htons() stands for the host to network short, htonl() for long. They convert from host byte-ordering to network-stack byte-ordering. The functions ntohs() and ntohl() do the reverse (from network byte-ordering to host byte-ordering).

These functions are particularly useful when we ask the system to provide us the information of the host running a client or server. Now let’s try to extract some information from the client. Just add these lines to the server:

char address[INET6_ADDRSTRLEN]; /* at declaration section */
printf("Connected to client %s at port %dn", inet_ntop(AF_INET6, &caddr.sin6_addr,
buff, sizeof(buff)), ntohs(caddr.sin6_port)); /* after the call to accept() */

You can see the output after these code changes in Figure 1. The code itself is clear—we have used the functions we just understood, above.

Figure 1: Server running

A little exercise

Now put this code in the proper section of the server program, to make it do something more interesting than what we did earlier:

if(read(cfd, &ch, 1)<0) perror("read");
while( ch != EOF) {
        if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z'))
        ch^=0x20;    /* EXORing 6th bit will result in change in case */
        if(write(cfd, &ch, 1)<0) perror("write");
        if(read(cfd, &ch, 1)<0) perror("read");
}

Yes, you’ve got it right: add it after the call to accept(). Now, if you’re as sleepy as I am right now, then just telnet to the server — or go on and write a client; great, that’s the spirit.

Well, before closing, let’s look at the output of a telnet session. Run telnet ::1 1205 in your shell, and start typing. See Figure 2 for a sample output.

Figure 2: Telnetting

On hitting Ctrl+D, the server will close the connection, and wait for a new one. And for those lazy fellas who didn’t want to write their own client’s :P, you just needed to put this into your client, instead of only write() and read() calls, for the result shown in Figure 3:

while(1) {
      ch=getchar();
      if(write(cfd, &ch, 1)<0) perror("write");
      if(read(cfd, &ch, 1)<0) perror("read");
      printf("%c", ch);
}

Figure 3: Client running

Compile and run your client.

To close, use Ctrl+D to send EOF to the server, and the server will close the connection to the client, instead of shutting the server down using Ctrl-C. Or you can also close the connection from the client; we’ll handle this problem later, using signal handlers. Now, I think I should try to get some rest. Good night, and FOSS ROCKS!

Creating Your Own Server: The Socket API, Part 2的更多相关文章

  1. Creating Your Own Server: The Socket API, Part 1

    转:http://www.linuxforu.com/2011/08/creating-your-own-server-the-socket-api-part-1/ By Pankaj Tanwar  ...

  2. The Socket API, Part 5: SCTP

    转:http://www.linuxforu.com/2011/12/socket-api-part-5-sctp/ By Pankaj Tanwar on December 29, 2011 in  ...

  3. Mac mySql ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)的解决办法

    我的环境:Mac 10.11.6 ,mysql  5.7.14  . mac mySql 报错ERROR 2002 (HY000): Can't connect to local MySQL serv ...

  4. .NET3.5中的高性能 Socket API

    转载:http://www.cnblogs.com/TianFang/archive/2007/11/09/954730.html 在 .NET Framework 2.0 版本中,System.Ne ...

  5. UNIX网络编程——SOCKET API和TCP STATE的对应关系_三次握手_四次挥手及TCP延迟确认

    在socket系统调用中,如何完成三次握手和四次挥手: SOCK_DGRAM即UDP中的connect操作知识在内核中注册对方机器的IP和PORT信息,并没有建立连接的过程,即没有发包,close也不 ...

  6. ERROR 2002 (HY000): Can&#39;t connect to local MySQL server through socket

    在安装好了MySQL之后,使用了新的配置文件后.MySQL服务器能够成功启动,但在登陆的时候出现了ERROR 2002 (HY000): Can't connect to local MySQL se ...

  7. Mac - Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

    在终端输入mysql,结果出现 macdeMacBook-Pro:~ mac$ alias mysql=/usr/local/mysql/bin/mysql macdeMacBook-Pro:~ ma ...

  8. JNI 和 socket api

    1.JavaVM 和 JNIEnvJNIEnv是一个与线程相关的变量,不同线程的JNIEnv彼此独立.JavaVM是虚拟机在JNI层的代表,在一个虚拟机进程中只有一个JavaVM,因此该进程的所有线程 ...

  9. socket编程 ------ BSD socket API

    伯克利套接字(Berkeley sockets),也称为BSD Socket.伯克利套接字的应用编程接口(API)是采用C语言的进程间通信的库,经常用在计算机网络间的通信. BSD Socket的应用 ...

随机推荐

  1. 一个介绍webrtc的国外网址

    http://www.html5rocks.com/en/tutorials/webrtc/basics/

  2. [翻译]创建ASP.NET WebApi RESTful 服务(9)

    一旦成功的发布API后,使用者将依赖于你所提供的服务.但是变更总是无法避免的,因此谨慎的制定ASP.NET Web API的版本策略就变得非常重要.一般来说,新的功能需要无缝的接入,有时新老版本需要并 ...

  3. Codeforces Round #368 (Div. 2) C. Pythagorean Triples(数学)

    Pythagorean Triples 题目链接: http://codeforces.com/contest/707/problem/C Description Katya studies in a ...

  4. MS 数据库存储过程加密解密

    存储过程加密解密在网上有很多,刚刚好最近需要用到,所以就查询了一下资料.记录一下 加密方法:执行如下存储过程 DECLARE @sp_name nvarchar(400) DECLARE @sp_co ...

  5. usb库文件usb_desc.c分析

    参考<圈圈教你玩USB> usb协议中使用的是小端结构,所以实际数据在传输时是低字节在先的. 设备描述符的实现: 已知每个设备都必须有且仅有一个设备描述符,它的结构在USB协议中有详细的定 ...

  6. CString转换成int CString类相应函数

    CString 型转化成 int 型 把 CString 类型的数据转化成整数类型最简单的方法就是使用标准的字符串到整数转换例程. 虽然通常你怀疑使用_atoi()函数是一个好的选择,它也很少会是一个 ...

  7. hdoj 5328 Problems killer

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5328 #include<stdio.h> #include<algorithm> ...

  8. 结构类模式(四):装饰(Decorator)

    定义 动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案. 它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 特点 装饰对象和真实对象有相同的接口.这样客户端对象就能以 ...

  9. Notes on Probabilistic Latent Semantic Analysis (PLSA)

    转自:http://www.hongliangjie.com/2010/01/04/notes-on-probabilistic-latent-semantic-analysis-plsa/ I hi ...

  10. 前端MVC学习总结(三)——AngularJS服务、路由、内置API、jQueryLite

    一.服务 AngularJS功能最基本的组件之一是服务(Service).服务为你的应用提供基于任务的功能.服务可以被视为重复使用的执行一个或多个相关任务的代码块. AngularJS服务是单例对象, ...